X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Ffiles.c;h=3f22b604963ec8a996dd949b468b368c86e454ec;hb=ad15815ec04f42779d0717a6d291f93b8e847d39;hp=3286dd07131ca5cc4a2a7ccd07a2aaf7a0b5294c;hpb=6fa4785d987eb1a6cfd36d95151dbb318ab8793e;p=rocksndiamonds.git diff --git a/src/files.c b/src/files.c index 3286dd07..3f22b604 100644 --- a/src/files.c +++ b/src/files.c @@ -30,12 +30,15 @@ #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 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" @@ -47,6 +50,57 @@ /* level file functions */ /* ========================================================================= */ +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; @@ -74,7 +128,7 @@ static void setLevelInfoToDefaults(struct LevelInfo *level) 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; @@ -87,6 +141,13 @@ static void setLevelInfoToDefaults(struct LevelInfo *level) 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; iscore[i] = 10; @@ -100,11 +161,17 @@ static void setLevelInfoToDefaults(struct LevelInfo *level) 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= 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; } @@ -276,7 +372,7 @@ static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level) 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); @@ -376,6 +472,7 @@ static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level) num_contents = getFile8Bit(file); content_xsize = getFile8Bit(file); content_ysize = getFile8Bit(file); + ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED); for(i=0; ienvelope_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); @@ -452,7 +584,7 @@ static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level) 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); } @@ -496,8 +628,8 @@ static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level) 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); @@ -513,40 +645,156 @@ static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level) 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(struct LevelInfo *level, char *filename) { char cookie[MAX_LINE_LEN]; @@ -627,9 +875,11 @@ void LoadLevelFromFilename(struct LevelInfo *level, char *filename) { "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 } }; @@ -675,18 +925,198 @@ void LoadLevelFromFilename(struct LevelInfo *level, char *filename) fclose(file); } -static void LoadLevel_InitLevel(struct LevelInfo *level, char *filename) +#if 1 + +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 + 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"); + + /* player was faster than monsters in (pre-)1.0 levels */ + 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; + } + 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. */ + + 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; + } + } + + /* 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; yfieldy; y++) + { + for(x=0; xfieldx; 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; + } + } + + /* copy elements to runtime playfield array */ + for(x=0; xfield[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)) { #if 0 - printf("::: This level is private or contributed: '%s'\n", filename); + printf("\n::: This level is private or contributed: '%s'\n", filename); #endif /* For user contributed and private levels, use the version of @@ -714,7 +1144,7 @@ static void LoadLevel_InitLevel(struct LevelInfo *level, char *filename) else { #if 0 - printf("::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n", + printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n", leveldir_current->sort_priority, filename); #endif @@ -738,7 +1168,7 @@ static void LoadLevel_InitLevel(struct LevelInfo *level, char *filename) level->em_slippery_gems = TRUE; } - /* map elements which have changed in newer versions */ + /* map elements that have changed in newer versions */ for(y=0; yfieldy; y++) { for(x=0; xfieldx; x++) @@ -753,7 +1183,8 @@ static void LoadLevel_InitLevel(struct LevelInfo *level, char *filename) element == EL_CHAR(']') ? EL_CHAR_UUMLAUT : element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element); } - else if (level->game_version < VERSION_IDENT(3,0,0)) + + 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 : @@ -767,6 +1198,48 @@ static void LoadLevel_InitLevel(struct LevelInfo *level, char *filename) } } + /* 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; xgame_version); } +#endif + void LoadLevelTemplate(int level_nr) { char *filename = getLevelFilename(level_nr); LoadLevelFromFilename(&level_template, filename); + LoadLevel_InitVersion(&level, filename); + LoadLevel_InitElements(&level, filename); + ActivateLevelTemplate(); } @@ -801,7 +1279,13 @@ void LoadLevel(int level_nr) 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) @@ -837,7 +1321,7 @@ static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level) 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)); @@ -937,6 +1421,23 @@ static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element) 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) @@ -978,12 +1479,12 @@ static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level, { 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++; @@ -995,6 +1496,7 @@ static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level, } #endif +#if 0 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level, int num_changed_custom_elements) { @@ -1006,7 +1508,7 @@ static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level, { 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) { @@ -1023,8 +1525,8 @@ static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level, 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); @@ -1039,27 +1541,27 @@ static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level, 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); @@ -1074,12 +1576,92 @@ static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level, if (check != num_changed_custom_elements) /* should not happen */ Error(ERR_WARN, "inconsistent number of custom element properties"); } +#endif + +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) { int body_chunk_size; - int num_changed_custom_elements = 0; - int level_chunk_CUS3_size; int i, x, y; FILE *file; @@ -1116,12 +1698,6 @@ static void SaveLevelFromFilename(struct LevelInfo *level, char *filename) 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); - putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED); putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE); @@ -1150,10 +1726,33 @@ static void SaveLevelFromFilename(struct LevelInfo *level, char *filename) SaveLevel_CNT2(file, level, EL_BD_AMOEBA); } - if (num_changed_custom_elements > 0 && !level->use_custom_template) + /* 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); + } + } + + /* 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); @@ -1197,7 +1796,7 @@ void DumpLevel(struct LevelInfo *level) 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")); @@ -1273,6 +1872,8 @@ static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape) engine_version = getFileVersion(file); if (engine_version > 0) tape->engine_version = engine_version; + else + tape->engine_version = tape->game_version; } return chunk_size; @@ -1500,7 +2101,8 @@ void LoadTapeFromFilename(char *filename) 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 } @@ -1799,8 +2401,10 @@ void SaveScore(int level_nr) #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 @@ -1885,6 +2489,8 @@ static struct TokenInfo editor_setup_tokens[] = { 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[] = @@ -1964,9 +2570,9 @@ static void setSetupInfoToDefaults(struct SetupInfo *si) 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; @@ -1980,6 +2586,9 @@ static void setSetupInfoToDefaults(struct SetupInfo *si) 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; @@ -2218,7 +2827,8 @@ void LoadSpecialMenuDesignSettings() 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; @@ -2244,7 +2854,8 @@ void LoadSpecialMenuDesignSettings() 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);