#define LEVEL_HEADER_UNUSED 13 /* 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 */
+#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 16 /* unused CUS3 bytes / part */
+#define LEVEL_CPART_CUS3_UNUSED 15 /* unused CUS3 bytes / part */
#define TAPE_HEADER_SIZE 20 /* size of tape file header */
#define TAPE_HEADER_UNUSED 3 /* unused tape header bytes */
-#define LEVEL_CHUNK_CUS3_SIZE(x) (2 + x * LEVEL_CPART_CUS3_SIZE)
+#define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
+#define LEVEL_CHUNK_CUS4_SIZE(x) (48 + 48 + (x) * 48)
/* file identifier strings */
#define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
/* level file functions */
/* ========================================================================= */
-static void setLevelInfoToDefaults()
+void setElementChangePages(struct ElementInfo *ei, int change_pages)
+{
+ int change_page_size = sizeof(struct ElementChangeInfo);
+
+ ei->num_change_pages = MAX(1, change_pages);
+
+ ei->change_page =
+ checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
+
+ if (ei->current_change_page >= ei->num_change_pages)
+ ei->current_change_page = ei->num_change_pages - 1;
+
+ ei->change = &ei->change_page[ei->current_change_page];
+}
+
+void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
+{
+ int x, y;
+
+ change->can_change = FALSE;
+
+ change->events = CE_BITMASK_DEFAULT;
+ change->sides = CH_SIDE_ANY;
+
+ change->target_element = EL_EMPTY_SPACE;
+
+ change->delay_fixed = 0;
+ change->delay_random = 0;
+ change->delay_frames = 1;
+
+ 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;
+
+ for(x=0; x<3; x++)
+ for(y=0; y<3; y++)
+ change->content[x][y] = EL_EMPTY_SPACE;
+
+ change->direct_action = 0;
+ change->other_action = 0;
+
+ change->pre_change_function = NULL;
+ change->change_function = NULL;
+ change->post_change_function = NULL;
+}
+
+static void setLevelInfoToDefaults(struct LevelInfo *level)
{
int i, j, x, y;
- level.file_version = FILE_VERSION_ACTUAL;
- level.game_version = GAME_VERSION_ACTUAL;
+ level->file_version = FILE_VERSION_ACTUAL;
+ level->game_version = GAME_VERSION_ACTUAL;
- level.encoding_16bit_field = FALSE; /* default: only 8-bit elements */
- level.encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
- level.encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
+ level->encoding_16bit_field = FALSE; /* default: only 8-bit elements */
+ level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
+ level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
- lev_fieldx = level.fieldx = STD_LEV_FIELDX;
- lev_fieldy = level.fieldy = STD_LEV_FIELDY;
+ 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++)
- Feld[x][y] = Ur[x][y] = EL_SAND;
-
- 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.gravity = FALSE;
- level.em_slippery_gems = FALSE;
-
- level.use_custom_template = FALSE;
+ level->field[x][y] = EL_SAND;
+
+ 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->use_custom_template = FALSE;
for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
- level.name[i] = '\0';
+ level->name[i] = '\0';
for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
- level.author[i] = '\0';
+ level->author[i] = '\0';
+
+ strcpy(level->name, NAMELESS_LEVEL_NAME);
+ strcpy(level->author, ANONYMOUS_NAME);
- strcpy(level.name, NAMELESS_LEVEL_NAME);
- strcpy(level.author, ANONYMOUS_NAME);
+ 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++)
- level.score[i] = 10;
+ level->score[i] = 10;
- level.num_yamyam_contents = STD_ELEMENT_CONTENTS;
+ 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++)
- level.yamyam_content[i][x][y] =
+ level->yamyam_content[i][x][y] =
(i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
- Feld[0][0] = Ur[0][0] = EL_PLAYER_1;
- Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
- Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_EXIT_CLOSED;
+ 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++)
+ {
+ setElementChangePages(&element_info[i], 1);
+ setElementChangeInfoToDefaults(element_info[i].change);
+ }
for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
{
int element = EL_CUSTOM_START + i;
- for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
+ for(j=0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
element_info[element].description[j] = '\0';
- strcpy(element_info[element].description,
- element_info[element].editor_description);
+ if (element_info[element].custom_description != NULL)
+ strncpy(element_info[element].description,
+ element_info[element].custom_description, MAX_ELEMENT_NAME_LEN);
+ else
+ strcpy(element_info[element].description,
+ element_info[element].editor_description);
element_info[element].use_gfx_element = FALSE;
element_info[element].gfx_element = EL_EMPTY_SPACE;
- element_info[element].score = 0;
- element_info[element].gem_count = 0;
+ 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].move_direction_initial = MV_NO_MOVING;
element_info[element].move_stepsize = TILEX / 8;
- for(x=0; x<3; x++)
- for(y=0; y<3; y++)
- element_info[element].content[x][y] = EL_EMPTY_SPACE;
-
- element_info[element].change.events = CE_BITMASK_DEFAULT;
- element_info[element].change.target_element = EL_EMPTY_SPACE;
-
- element_info[element].change.delay_fixed = 0;
- element_info[element].change.delay_random = 0;
- element_info[element].change.delay_frames = -1; /* use default */
-
- element_info[element].change.trigger_element = EL_EMPTY_SPACE;
-
- element_info[element].change.explode = FALSE;
- element_info[element].change.use_content = FALSE;
- element_info[element].change.only_complete = FALSE;
- element_info[element].change.use_random_change = FALSE;
- element_info[element].change.random = 0;
- element_info[element].change.power = CP_NON_DESTRUCTIVE;
+ element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
for(x=0; x<3; x++)
for(y=0; y<3; y++)
- element_info[element].change.content[x][y] = EL_EMPTY_SPACE;
+ element_info[element].content[x][y] = EL_EMPTY_SPACE;
element_info[element].access_type = 0;
element_info[element].access_layer = 0;
element_info[element].smash_targets = 0;
element_info[element].deadliness = 0;
element_info[element].consistency = 0;
- element_info[element].change_player_action = 0;
- element_info[element].change_collide_action = 0;
- element_info[element].change_other_action = 0;
element_info[element].can_explode_by_fire = FALSE;
element_info[element].can_explode_smashed = FALSE;
element_info[element].can_explode_impact = FALSE;
+ element_info[element].current_change_page = 0;
+
/* start with no properties at all */
for (j=0; j < NUM_EP_BITFIELDS; j++)
Properties[element][j] = EP_BITMASK_DEFAULT;
+
+ element_info[element].modified_settings = FALSE;
}
BorderElement = EL_STEELWALL;
- level.no_level_file = FALSE;
+ level->no_level_file = FALSE;
if (leveldir_current == NULL) /* only when dumping level */
return;
/* try to determine better author name than 'anonymous' */
if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
{
- strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
- level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
+ strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
+ level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
}
else
{
switch (LEVELCLASS(leveldir_current))
{
case LEVELCLASS_TUTORIAL:
- strcpy(level.author, PROGRAM_AUTHOR_STRING);
+ strcpy(level->author, PROGRAM_AUTHOR_STRING);
break;
case LEVELCLASS_CONTRIBUTION:
- strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
- level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
+ strncpy(level->author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
+ level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
break;
case LEVELCLASS_USER:
- strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
- level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
+ strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
+ level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
break;
default:
}
}
+static void ActivateLevelTemplate()
+{
+ /* Currently there is no special action needed to activate the template
+ data, because 'element_info' and 'Properties' overwrite the original
+ level data, while all other variables do not change. */
+}
+
+boolean LevelFileExists(int level_nr)
+{
+ char *filename = getLevelFilename(level_nr);
+
+ return (access(filename, F_OK) == 0);
+}
+
static int checkLevelElement(int element)
{
+ /* map some (historic, now obsolete) elements */
+
+#if 1
+ switch (element)
+ {
+ case EL_PLAYER_OBSOLETE:
+ element = EL_PLAYER_1;
+ break;
+
+ case EL_KEY_OBSOLETE:
+ element = EL_KEY_1;
+
+ case EL_EM_KEY_1_FILE_OBSOLETE:
+ element = EL_EM_KEY_1;
+ break;
+
+ case EL_EM_KEY_2_FILE_OBSOLETE:
+ element = EL_EM_KEY_2;
+ break;
+
+ case EL_EM_KEY_3_FILE_OBSOLETE:
+ element = EL_EM_KEY_3;
+ break;
+
+ case EL_EM_KEY_4_FILE_OBSOLETE:
+ element = EL_EM_KEY_4;
+ break;
+
+ case EL_ENVELOPE_OBSOLETE:
+ element = EL_ENVELOPE_1;
+ break;
+
+ case EL_SP_EMPTY:
+ element = EL_EMPTY;
+ break;
+
+ default:
+ if (element >= NUM_FILE_ELEMENTS)
+ {
+ Error(ERR_WARN, "invalid level element %d", element);
+
+ element = EL_CHAR_QUESTION;
+ }
+ break;
+ }
+#else
if (element >= NUM_FILE_ELEMENTS)
{
Error(ERR_WARN, "invalid level element %d", element);
+
element = EL_CHAR_QUESTION;
}
else if (element == EL_PLAYER_OBSOLETE)
element = EL_PLAYER_1;
else if (element == EL_KEY_OBSOLETE)
element = EL_KEY_1;
+#endif
return element;
}
{
int i, x, y;
- lev_fieldx = level->fieldx = fgetc(file);
- lev_fieldy = level->fieldy = fgetc(file);
+ level->fieldx = getFile8Bit(file);
+ level->fieldy = getFile8Bit(file);
level->time = getFile16BitBE(file);
level->gems_needed = getFile16BitBE(file);
for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
- level->name[i] = fgetc(file);
+ level->name[i] = getFile8Bit(file);
level->name[MAX_LEVEL_NAME_LEN] = 0;
for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
- level->score[i] = fgetc(file);
+ 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++)
- level->yamyam_content[i][x][y] = checkLevelElement(fgetc(file));
+ level->yamyam_content[i][x][y] = checkLevelElement(getFile8Bit(file));
- level->amoeba_speed = fgetc(file);
- level->time_magic_wall = fgetc(file);
- level->time_wheel = fgetc(file);
- level->amoeba_content = checkLevelElement(fgetc(file));
- level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
- level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
- level->encoding_16bit_field = (fgetc(file) == 1 ? TRUE : FALSE);
- level->em_slippery_gems = (fgetc(file) == 1 ? TRUE : FALSE);
+ level->amoeba_speed = getFile8Bit(file);
+ level->time_magic_wall = getFile8Bit(file);
+ level->time_wheel = getFile8Bit(file);
+ level->amoeba_content = checkLevelElement(getFile8Bit(file));
+ level->double_speed = (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);
- level->use_custom_template = (fgetc(file) == 1 ? TRUE : FALSE);
+ level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
int i;
for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
- level->author[i] = fgetc(file);
+ level->author[i] = getFile8Bit(file);
level->author[MAX_LEVEL_NAME_LEN] = 0;
return chunk_size;
for(y=0; y<level->fieldy; y++)
for(x=0; x<level->fieldx; x++)
- Feld[x][y] = Ur[x][y] =
- checkLevelElement(level->encoding_16bit_field ?
- getFile16BitBE(file) : fgetc(file));
+ level->field[x][y] =
+ checkLevelElement(level->encoding_16bit_field ? getFile16BitBE(file) :
+ getFile8Bit(file));
return chunk_size;
}
return chunk_size_expected;
}
- fgetc(file);
- level->num_yamyam_contents = fgetc(file);
- fgetc(file);
- fgetc(file);
+ getFile8Bit(file);
+ level->num_yamyam_contents = getFile8Bit(file);
+ getFile8Bit(file);
+ getFile8Bit(file);
/* correct invalid number of content fields -- should never happen */
if (level->num_yamyam_contents < 1 ||
for(x=0; x<3; x++)
level->yamyam_content[i][x][y] =
checkLevelElement(level->encoding_16bit_field ?
- getFile16BitBE(file) : fgetc(file));
+ getFile16BitBE(file) : getFile8Bit(file));
return chunk_size;
}
int content_array[MAX_ELEMENT_CONTENTS][3][3];
element = checkLevelElement(getFile16BitBE(file));
- num_contents = fgetc(file);
- content_xsize = fgetc(file);
- content_ysize = fgetc(file);
+ num_contents = getFile8Bit(file);
+ content_xsize = getFile8Bit(file);
+ content_ysize = getFile8Bit(file);
+
ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
return chunk_size;
}
+static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
+{
+ int i;
+ int element;
+ int envelope_nr;
+ int envelope_len;
+ int chunk_size_expected;
+
+ element = checkLevelElement(getFile16BitBE(file));
+ if (!IS_ENVELOPE(element))
+ element = EL_ENVELOPE_1;
+
+ envelope_nr = element - EL_ENVELOPE_1;
+
+ envelope_len = getFile16BitBE(file);
+
+ level->envelope_xsize[envelope_nr] = getFile8Bit(file);
+ level->envelope_ysize[envelope_nr] = getFile8Bit(file);
+
+ ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
+
+ chunk_size_expected = LEVEL_CHUNK_CNT3_HEADER + envelope_len;
+
+ if (chunk_size_expected != chunk_size)
+ {
+ ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
+ return chunk_size_expected;
+ }
+
+ for(i=0; i < envelope_len; i++)
+ level->envelope_text[envelope_nr][i] = getFile8Bit(file);
+
+ return chunk_size;
+}
+
static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
{
int num_changed_custom_elements = getFile16BitBE(file);
int custom_target_element = getFile16BitBE(file);
if (IS_CUSTOM_ELEMENT(element))
- element_info[element].change.target_element = custom_target_element;
+ element_info[element].change->target_element = custom_target_element;
else
Error(ERR_WARN, "invalid custom element number %d", element);
}
element_info[element].gfx_element =
checkLevelElement(getFile16BitBE(file));
- element_info[element].score = getFile8Bit(file);
- element_info[element].gem_count = getFile8Bit(file);
+ element_info[element].collect_score = getFile8Bit(file);
+ element_info[element].collect_count = getFile8Bit(file);
element_info[element].push_delay_fixed = getFile16BitBE(file);
element_info[element].push_delay_random = getFile16BitBE(file);
element_info[element].content[x][y] =
checkLevelElement(getFile16BitBE(file));
- element_info[element].change.events = getFile32BitBE(file);
+ element_info[element].change->events = getFile32BitBE(file);
- element_info[element].change.target_element =
+ element_info[element].change->target_element =
checkLevelElement(getFile16BitBE(file));
- element_info[element].change.delay_fixed = getFile16BitBE(file);
- element_info[element].change.delay_random = getFile16BitBE(file);
- element_info[element].change.delay_frames = getFile16BitBE(file);
+ element_info[element].change->delay_fixed = getFile16BitBE(file);
+ element_info[element].change->delay_random = getFile16BitBE(file);
+ element_info[element].change->delay_frames = getFile16BitBE(file);
- element_info[element].change.trigger_element =
+ element_info[element].change->trigger_element =
checkLevelElement(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->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.random = getFile8Bit(file);
- element_info[element].change.power = getFile8Bit(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++)
- element_info[element].change.content[x][y] =
+ element_info[element].change->content[x][y] =
checkLevelElement(getFile16BitBE(file));
+ element_info[element].slippery_type = getFile8Bit(file);
+
/* some free bytes for future properties and padding */
ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
+
+ /* mark that this custom element has been modified */
+ element_info[element].modified_settings = TRUE;
+ }
+
+ return chunk_size;
+}
+
+static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
+{
+ struct ElementInfo *ei;
+ int chunk_size_expected;
+ int element;
+ int i, x, y;
+
+ element = getFile16BitBE(file);
+
+ if (!IS_CUSTOM_ELEMENT(element))
+ {
+ Error(ERR_WARN, "invalid custom element number %d", element);
+
+ element = EL_DEFAULT; /* dummy element used for artwork config */
+ }
+
+ ei = &element_info[element];
+
+ for(i=0; i < MAX_ELEMENT_NAME_LEN; i++)
+ ei->description[i] = getFile8Bit(file);
+ ei->description[MAX_ELEMENT_NAME_LEN] = 0;
+
+ Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
+ ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
+
+ ei->num_change_pages = getFile8Bit(file);
+
+ /* some free bytes for future base property values and padding */
+ ReadUnusedBytesFromFile(file, 5);
+
+ chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
+ if (chunk_size_expected != chunk_size)
+ {
+ ReadUnusedBytesFromFile(file, chunk_size - 48);
+ return chunk_size_expected;
+ }
+
+ /* read custom property values */
+
+ ei->use_gfx_element = getFile8Bit(file);
+ ei->gfx_element = checkLevelElement(getFile16BitBE(file));
+
+ ei->collect_score = getFile8Bit(file);
+ ei->collect_count = getFile8Bit(file);
+
+ ei->push_delay_fixed = getFile16BitBE(file);
+ ei->push_delay_random = getFile16BitBE(file);
+ ei->move_delay_fixed = getFile16BitBE(file);
+ ei->move_delay_random = getFile16BitBE(file);
+
+ ei->move_pattern = getFile16BitBE(file);
+ ei->move_direction_initial = getFile8Bit(file);
+ ei->move_stepsize = getFile8Bit(file);
+
+ ei->slippery_type = getFile8Bit(file);
+
+ 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 */
+ ReadUnusedBytesFromFile(file, 12);
+
+ /* read change property values */
+
+ setElementChangePages(ei, ei->num_change_pages);
+
+ for (i=0; i < ei->num_change_pages; i++)
+ {
+ struct ElementChangeInfo *change = &ei->change_page[i];
+
+ /* always start with reliable default values */
+ setElementChangeInfoToDefaults(change);
+
+ change->events = getFile32BitBE(file);
+
+ change->target_element = checkLevelElement(getFile16BitBE(file));
+
+ change->delay_fixed = getFile16BitBE(file);
+ change->delay_random = getFile16BitBE(file);
+ change->delay_frames = getFile16BitBE(file);
+
+ change->trigger_element = checkLevelElement(getFile16BitBE(file));
+
+ change->explode = getFile8Bit(file);
+ change->use_content = getFile8Bit(file);
+ change->only_complete = getFile8Bit(file);
+ change->use_random_change = getFile8Bit(file);
+
+ change->random = getFile8Bit(file);
+ change->power = getFile8Bit(file);
+
+ 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, 8);
}
+ /* mark this custom element as modified */
+ ei->modified_settings = TRUE;
+
return chunk_size;
}
-void LoadLevelFromFilename(char *filename)
+void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
{
char cookie[MAX_LINE_LEN];
char chunk_name[CHUNK_ID_LEN + 1];
FILE *file;
/* always start with reliable default values */
- setLevelInfoToDefaults();
+ setLevelInfoToDefaults(level);
if (!(file = fopen(filename, MODE_READ)))
{
- level.no_level_file = TRUE;
+ level->no_level_file = TRUE;
+
+ if (level != &level_template)
+ Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
- Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
return;
}
return;
}
- if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
+ if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
{
Error(ERR_WARN, "unsupported version of level file '%s'", filename);
fclose(file);
}
/* pre-2.0 level files have no game version, so use file version here */
- level.game_version = level.file_version;
+ level->game_version = level->file_version;
}
- if (level.file_version < FILE_VERSION_1_2)
+ if (level->file_version < FILE_VERSION_1_2)
{
/* level files from versions before 1.2.0 without chunk structure */
- LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, &level);
- LoadLevel_BODY(file, level.fieldx * level.fieldy, &level);
+ LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
+ LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
}
else
{
{ "BODY", -1, LoadLevel_BODY },
{ "CONT", -1, LoadLevel_CONT },
{ "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
+ { "CNT3", -1, LoadLevel_CNT3 },
{ "CUS1", -1, LoadLevel_CUS1 },
{ "CUS2", -1, LoadLevel_CUS2 },
{ "CUS3", -1, LoadLevel_CUS3 },
+ { "CUS4", -1, LoadLevel_CUS4 },
{ NULL, 0, NULL }
};
{
/* call function to load this level chunk */
int chunk_size_expected =
- (chunk_info[i].loader)(file, chunk_size, &level);
+ (chunk_info[i].loader)(file, chunk_size, level);
/* the size of some chunks cannot be checked before reading other
chunks first (like "HEAD" and "BODY") that contain some header
}
fclose(file);
+}
+static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
+{
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))
{
+#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
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)
+ 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 */
- level.double_speed = TRUE;
+ level->double_speed = TRUE;
}
/* 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 (level->game_version == VERSION_IDENT(2,0,1))
+ level->em_slippery_gems = TRUE;
}
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,
make the game emulation more accurate, while (hopefully) not
breaking existing levels created from other players. */
- level.game_version = GAME_VERSION_ACTUAL;
+ 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,
of gem style elements). Already existing converted levels (neither
private nor contributed levels) are changed to the new behaviour. */
- if (level.file_version < FILE_VERSION_2_0)
- level.em_slippery_gems = TRUE;
+ if (level->file_version < FILE_VERSION_2_0)
+ level->em_slippery_gems = TRUE;
+ }
+}
+
+static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
+{
+ 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) */
+ 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 and copying events to be mapped is important */
+ for (j=CE_BY_OTHER_ACTION; 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 and copying 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);
+ }
+ }
+ }
+ }
+
+ /* some custom element change events get mapped since version 3.0.3 */
+ for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+ {
+ int element = EL_CUSTOM_START + i;
+
+ if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER) ||
+ HAS_CHANGE_EVENT(element, CE_BY_COLLISION))
+ {
+ SET_CHANGE_EVENT(element, CE_BY_PLAYER, FALSE);
+ SET_CHANGE_EVENT(element, CE_BY_COLLISION, FALSE);
+
+ SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, 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;
+ }
+ }
+
+ /* set default push delay values (corrected since version 3.0.7) */
+ if (level->game_version < VERSION_IDENT(3,0,7))
+ {
+ game.default_push_delay_fixed = 2;
+ game.default_push_delay_random = 8;
+ }
+ else
+ {
+ game.default_push_delay_fixed = 8;
+ game.default_push_delay_random = 8;
}
- /* map some elements which have changed in newer versions */
- if (level.game_version <= VERSION_IDENT(2,2,0))
+ /* set uninitialized push delay values of custom elements in older levels */
+ for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
{
- int x, y;
+ int element = EL_CUSTOM_START + i;
+
+ 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;
+ }
- /* map game font elements */
- for(y=0; y<level.fieldy; y++)
+ /* 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++)
+ {
+ 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))
{
- int element = Ur[x][y];
-
- if (element == EL_CHAR('['))
- element = EL_CHAR_AUMLAUT;
- else if (element == EL_CHAR('\\'))
- element = EL_CHAR_OUMLAUT;
- else if (element == EL_CHAR(']'))
- element = EL_CHAR_UUMLAUT;
- else if (element == EL_CHAR('^'))
- element = EL_CHAR_COPYRIGHT;
-
- Feld[x][y] = Ur[x][y] = element;
+ /* 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;
}
}
+ /* 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();
}
+void LoadLevelTemplate(int level_nr)
+{
+ char *filename = getLevelFilename(level_nr);
+
+ LoadLevelFromFilename(&level_template, filename);
+
+ LoadLevel_InitVersion(&level, filename);
+ LoadLevel_InitElements(&level, filename);
+
+ ActivateLevelTemplate();
+}
+
void LoadLevel(int level_nr)
{
char *filename = getLevelFilename(level_nr);
- LoadLevelFromFilename(filename);
- InitElementPropertiesEngine(level.game_version);
+ LoadLevelFromFilename(&level, filename);
+
+ if (level.use_custom_template)
+ LoadLevelTemplate(-1);
+
+#if 1
+ LoadLevel_InitVersion(&level, filename);
+ LoadLevel_InitElements(&level, filename);
+ LoadLevel_InitPlayfield(&level, filename);
+#else
+ LoadLevel_InitLevel(&level, filename);
+#endif
}
static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
{
int i, x, y;
- fputc(level->fieldx, file);
- fputc(level->fieldy, file);
+ putFile8Bit(file, level->fieldx);
+ putFile8Bit(file, level->fieldy);
putFile16BitBE(file, level->time);
putFile16BitBE(file, level->gems_needed);
for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
- fputc(level->name[i], file);
+ putFile8Bit(file, level->name[i]);
for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
- fputc(level->score[i], file);
+ 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++)
- fputc((level->encoding_16bit_yamyam ? EL_EMPTY :
- level->yamyam_content[i][x][y]),
- file);
- fputc(level->amoeba_speed, file);
- fputc(level->time_magic_wall, file);
- fputc(level->time_wheel, file);
- fputc((level->encoding_16bit_amoeba ? EL_EMPTY : level->amoeba_content),
- file);
- fputc((level->double_speed ? 1 : 0), file);
- fputc((level->gravity ? 1 : 0), file);
- fputc((level->encoding_16bit_field ? 1 : 0), file);
- fputc((level->em_slippery_gems ? 1 : 0), file);
-
- fputc((level->use_custom_template ? 1 : 0), file);
+ putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
+ level->yamyam_content[i][x][y]));
+ putFile8Bit(file, level->amoeba_speed);
+ putFile8Bit(file, level->time_magic_wall);
+ putFile8Bit(file, level->time_wheel);
+ putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
+ level->amoeba_content));
+ putFile8Bit(file, (level->double_speed ? 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));
+
+ putFile8Bit(file, (level->use_custom_template ? 1 : 0));
WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
}
int i;
for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
- fputc(level->author[i], file);
+ putFile8Bit(file, level->author[i]);
}
static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
for(y=0; y<level->fieldy; y++)
for(x=0; x<level->fieldx; x++)
if (level->encoding_16bit_field)
- putFile16BitBE(file, Ur[x][y]);
+ putFile16BitBE(file, level->field[x][y]);
else
- fputc(Ur[x][y], file);
+ putFile8Bit(file, level->field[x][y]);
}
#if 0
{
int i, x, y;
- fputc(EL_YAMYAM, file);
- fputc(level->num_yamyam_contents, file);
- fputc(0, file);
- fputc(0, file);
+ putFile8Bit(file, EL_YAMYAM);
+ putFile8Bit(file, level->num_yamyam_contents);
+ putFile8Bit(file, 0);
+ putFile8Bit(file, 0);
for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
for(y=0; y<3; y++)
if (level->encoding_16bit_field)
putFile16BitBE(file, level->yamyam_content[i][x][y]);
else
- fputc(level->yamyam_content[i][x][y], file);
+ putFile8Bit(file, level->yamyam_content[i][x][y]);
}
#endif
}
putFile16BitBE(file, element);
- fputc(num_contents, file);
- fputc(content_xsize, file);
- fputc(content_ysize, file);
+ putFile8Bit(file, num_contents);
+ putFile8Bit(file, content_xsize);
+ putFile8Bit(file, content_ysize);
WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
putFile16BitBE(file, content_array[i][x][y]);
}
+static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
+{
+ int i;
+ int envelope_nr = element - EL_ENVELOPE_1;
+ int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
+
+ putFile16BitBE(file, element);
+ putFile16BitBE(file, envelope_len);
+ putFile8Bit(file, level->envelope_xsize[envelope_nr]);
+ putFile8Bit(file, level->envelope_ysize[envelope_nr]);
+
+ WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
+
+ for(i=0; i < envelope_len; i++)
+ putFile8Bit(file, level->envelope_text[envelope_nr][i]);
+}
+
#if 0
static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
int num_changed_custom_elements)
{
int element = EL_CUSTOM_START + i;
- if (element_info[element].change.target_element != EL_EMPTY_SPACE)
+ if (element_info[element].change->target_element != EL_EMPTY_SPACE)
{
if (check < num_changed_custom_elements)
{
putFile16BitBE(file, element);
- putFile16BitBE(file, element_info[element].change.target_element);
+ putFile16BitBE(file, element_info[element].change->target_element);
}
check++;
}
#endif
+#if 0
static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
int num_changed_custom_elements)
{
{
int element = EL_CUSTOM_START + i;
- if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
+ if (element_info[element].modified_settings)
{
if (check < num_changed_custom_elements)
{
putFile8Bit(file, element_info[element].use_gfx_element);
putFile16BitBE(file, element_info[element].gfx_element);
- putFile8Bit(file, element_info[element].score);
- putFile8Bit(file, element_info[element].gem_count);
+ putFile8Bit(file, element_info[element].collect_score);
+ putFile8Bit(file, element_info[element].collect_count);
putFile16BitBE(file, element_info[element].push_delay_fixed);
putFile16BitBE(file, element_info[element].push_delay_random);
for(x=0; x<3; x++)
putFile16BitBE(file, element_info[element].content[x][y]);
- putFile32BitBE(file, element_info[element].change.events);
+ putFile32BitBE(file, element_info[element].change->events);
- putFile16BitBE(file, element_info[element].change.target_element);
+ putFile16BitBE(file, element_info[element].change->target_element);
- putFile16BitBE(file, element_info[element].change.delay_fixed);
- putFile16BitBE(file, element_info[element].change.delay_random);
- putFile16BitBE(file, element_info[element].change.delay_frames);
+ putFile16BitBE(file, element_info[element].change->delay_fixed);
+ putFile16BitBE(file, element_info[element].change->delay_random);
+ putFile16BitBE(file, element_info[element].change->delay_frames);
- putFile16BitBE(file, element_info[element].change.trigger_element);
+ 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->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.random);
- putFile8Bit(file, element_info[element].change.power);
+ 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++)
- putFile16BitBE(file, element_info[element].change.content[x][y]);
+ putFile16BitBE(file, element_info[element].change->content[x][y]);
+
+ putFile8Bit(file, element_info[element].slippery_type);
/* some free bytes for future properties and padding */
WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
if (check != num_changed_custom_elements) /* should not happen */
Error(ERR_WARN, "inconsistent number of custom element properties");
}
+#endif
-void SaveLevel(int level_nr)
+static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
+{
+ struct ElementInfo *ei = &element_info[element];
+ int i, x, y;
+
+ putFile16BitBE(file, element);
+
+ for(i=0; i < MAX_ELEMENT_NAME_LEN; i++)
+ putFile8Bit(file, ei->description[i]);
+
+ putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
+ WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
+
+ putFile8Bit(file, ei->num_change_pages);
+
+ /* some free bytes for future base property values and padding */
+ WriteUnusedBytesToFile(file, 5);
+
+ /* write custom property values */
+
+ putFile8Bit(file, ei->use_gfx_element);
+ putFile16BitBE(file, ei->gfx_element);
+
+ putFile8Bit(file, ei->collect_score);
+ putFile8Bit(file, ei->collect_count);
+
+ putFile16BitBE(file, ei->push_delay_fixed);
+ putFile16BitBE(file, ei->push_delay_random);
+ putFile16BitBE(file, ei->move_delay_fixed);
+ putFile16BitBE(file, ei->move_delay_random);
+
+ putFile16BitBE(file, ei->move_pattern);
+ putFile8Bit(file, ei->move_direction_initial);
+ putFile8Bit(file, ei->move_stepsize);
+
+ putFile8Bit(file, ei->slippery_type);
+
+ 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 */
+ WriteUnusedBytesToFile(file, 12);
+
+ /* write change property values */
+
+ for (i=0; i < ei->num_change_pages; i++)
+ {
+ struct ElementChangeInfo *change = &ei->change_page[i];
+
+ putFile32BitBE(file, change->events);
+
+ putFile16BitBE(file, change->target_element);
+
+ putFile16BitBE(file, change->delay_fixed);
+ putFile16BitBE(file, change->delay_random);
+ putFile16BitBE(file, change->delay_frames);
+
+ 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->random);
+ putFile8Bit(file, change->power);
+
+ 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, 8);
+ }
+}
+
+static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
{
- char *filename = getLevelFilename(level_nr);
int body_chunk_size;
- int num_changed_custom_elements = 0;
- int level_chunk_CUS3_size;
int i, x, y;
FILE *file;
return;
}
- level.file_version = FILE_VERSION_ACTUAL;
- level.game_version = GAME_VERSION_ACTUAL;
+ level->file_version = FILE_VERSION_ACTUAL;
+ level->game_version = GAME_VERSION_ACTUAL;
/* 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++)
- if (Ur[x][y] > 255)
- level.encoding_16bit_field = TRUE;
+ level->encoding_16bit_field = FALSE;
+ 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++)
+ 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++)
- if (level.yamyam_content[i][x][y] > 255)
- level.encoding_16bit_yamyam = TRUE;
+ if (level->yamyam_content[i][x][y] > 255)
+ level->encoding_16bit_yamyam = TRUE;
/* check amoeba content for 16-bit elements */
- level.encoding_16bit_amoeba = FALSE;
- if (level.amoeba_content > 255)
- level.encoding_16bit_amoeba = TRUE;
+ level->encoding_16bit_amoeba = FALSE;
+ if (level->amoeba_content > 255)
+ level->encoding_16bit_amoeba = TRUE;
/* calculate size of "BODY" chunk */
body_chunk_size =
- level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
-
- /* check for non-standard custom elements and calculate "CUS3" chunk size */
- for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
- if (Properties[EL_CUSTOM_START +i][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
- num_changed_custom_elements++;
- level_chunk_CUS3_size = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
+ level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
- SaveLevel_VERS(file, &level);
+ SaveLevel_VERS(file, level);
putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
- SaveLevel_HEAD(file, &level);
+ SaveLevel_HEAD(file, level);
putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
- SaveLevel_AUTH(file, &level);
+ SaveLevel_AUTH(file, level);
putFileChunkBE(file, "BODY", body_chunk_size);
- SaveLevel_BODY(file, &level);
+ SaveLevel_BODY(file, level);
- if (level.encoding_16bit_yamyam ||
- level.num_yamyam_contents != STD_ELEMENT_CONTENTS)
+ if (level->encoding_16bit_yamyam ||
+ level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
{
putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
- SaveLevel_CNT2(file, &level, EL_YAMYAM);
+ SaveLevel_CNT2(file, level, EL_YAMYAM);
}
- if (level.encoding_16bit_amoeba)
+ if (level->encoding_16bit_amoeba)
{
putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
- SaveLevel_CNT2(file, &level, EL_BD_AMOEBA);
+ SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
+ }
+
+ /* check for envelope content */
+ for (i=0; i<4; i++)
+ {
+ if (strlen(level->envelope_text[i]) > 0)
+ {
+ int envelope_len = strlen(level->envelope_text[i]) + 1;
+
+ putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_HEADER + envelope_len);
+ SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
+ }
}
- if (num_changed_custom_elements > 0)
+ /* check for non-default custom elements (unless using template level) */
+ if (!level->use_custom_template)
{
- putFileChunkBE(file, "CUS3", level_chunk_CUS3_size);
- SaveLevel_CUS3(file, &level, num_changed_custom_elements);
+ for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+ {
+ int element = EL_CUSTOM_START + i;
+
+ if (element_info[element].modified_settings)
+ {
+ int num_change_pages = element_info[element].num_change_pages;
+
+ putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
+ SaveLevel_CUS4(file, level, element);
+ }
+ }
}
fclose(file);
SetFilePermissions(filename, PERMS_PRIVATE);
}
+void SaveLevel(int level_nr)
+{
+ char *filename = getLevelFilename(level_nr);
+
+ SaveLevelFromFilename(&level, filename);
+}
+
+void SaveLevelTemplate()
+{
+ char *filename = getLevelFilename(-1);
+
+ SaveLevelFromFilename(&level, filename);
+}
+
void DumpLevel(struct LevelInfo *level)
{
printf_line("-", 79);
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"));
/* read header fields that are new since version 1.2 */
if (tape->file_version >= FILE_VERSION_1_2)
{
- byte store_participating_players = fgetc(file);
+ byte store_participating_players = getFile8Bit(file);
int engine_version;
/* since version 1.2, tapes store which players participate in the tape */
engine_version = getFileVersion(file);
if (engine_version > 0)
tape->engine_version = engine_version;
+ else
+ tape->engine_version = tape->game_version;
}
return chunk_size;
checked_realloc(tape->level_identifier, level_identifier_size);
for(i=0; i < level_identifier_size; i++)
- tape->level_identifier[i] = fgetc(file);
+ tape->level_identifier[i] = getFile8Bit(file);
tape->level_nr = getFile16BitBE(file);
tape->pos[i].action[j] = MV_NO_MOVING;
if (tape->player_participates[j])
- tape->pos[i].action[j] = fgetc(file);
+ tape->pos[i].action[j] = getFile8Bit(file);
}
- tape->pos[i].delay = fgetc(file);
+ tape->pos[i].delay = getFile8Bit(file);
if (tape->file_version == FILE_VERSION_1_0)
{
tape.length_seconds = GetTapeLength();
#if 0
- printf("tape version: %d\n", tape.game_version);
+ printf("tape game version: %d\n", tape.game_version);
+ printf("tape engine version: %d\n", tape.engine_version);
#endif
}
putFile32BitBE(file, tape->date);
putFile32BitBE(file, tape->length);
- fputc(store_participating_players, file);
+ putFile8Bit(file, store_participating_players);
/* unused bytes not at the end here for 4-byte alignment of engine_version */
WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
putFile16BitBE(file, level_identifier_size);
for(i=0; i < level_identifier_size; i++)
- fputc(tape->level_identifier[i], file);
+ putFile8Bit(file, tape->level_identifier[i]);
putFile16BitBE(file, tape->level_nr);
}
{
for(j=0; j<MAX_PLAYERS; j++)
if (tape->player_participates[j])
- fputc(tape->pos[i].action[j], file);
+ putFile8Bit(file, tape->pos[i].action[j]);
- fputc(tape->pos[i].delay, file);
+ putFile8Bit(file, tape->pos[i].delay);
}
}
#define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
#define SETUP_TOKEN_EDITOR_EL_CHARS 7
#define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
+#define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
+#define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
-#define NUM_EDITOR_SETUP_TOKENS 9
+#define NUM_EDITOR_SETUP_TOKENS 11
/* shortcut setup */
#define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
{ TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
{ TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
{ 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" },
};
static struct TokenInfo shortcut_setup_tokens[] =
si->fullscreen = FALSE;
si->ask_on_escape = TRUE;
- si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
- si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
- si->music_set = getStringCopy(MUSIC_SUBDIR);
+ si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
+ si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
+ si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
si->override_level_graphics = FALSE;
si->override_level_sounds = FALSE;
si->override_level_music = FALSE;
si->editor.el_dx_boulderdash = TRUE;
si->editor.el_chars = TRUE;
si->editor.el_custom = TRUE;
+ si->editor.el_custom_more = FALSE;
+
+ si->editor.el_headlines = TRUE;
si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
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_integer_from_string(image_config[j].value);
+ get_auto_parameter_value(image_config_vars[i].token,
+ image_config[j].value);
if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
return;
char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
if (value != NULL)
- *image_config_vars[i].value = get_integer_from_string(value);
+ *image_config_vars[i].value =
+ get_auto_parameter_value(image_config_vars[i].token, value);
}
freeSetupFileHash(setup_file_hash);