X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Ffiles.c;h=f5275728156c3e921462a8cf5d030149262672b1;hb=9b360b977c0c1988c9a9bd63bb6d3f3991d1a650;hp=20eadecd7b52d3d85d445af79ab7d82994a9234a;hpb=6f6af05b1d4b9671bc115ab9ab2fa3b9d516a652;p=rocksndiamonds.git diff --git a/src/files.c b/src/files.c index 20eadecd..f5275728 100644 --- a/src/files.c +++ b/src/files.c @@ -49,6 +49,23 @@ #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x" #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2" +static struct +{ + int filetype; + char *id; +} +filetype_id_list[] = +{ + { LEVEL_FILE_TYPE_RND, "RND" }, + { LEVEL_FILE_TYPE_BD, "BD" }, + { LEVEL_FILE_TYPE_EM, "EM" }, + { LEVEL_FILE_TYPE_SP, "SP" }, + { LEVEL_FILE_TYPE_DX, "DX" }, + { LEVEL_FILE_TYPE_SB, "SB" }, + { LEVEL_FILE_TYPE_DC, "DC" }, + { -1, NULL }, +}; + /* ========================================================================= */ /* level file functions */ @@ -71,11 +88,12 @@ void setElementChangePages(struct ElementInfo *ei, int change_pages) void setElementChangeInfoToDefaults(struct ElementChangeInfo *change) { - int x, y; + int i, x, y; change->can_change = FALSE; - change->events = CE_BITMASK_DEFAULT; + for (i = 0; i < NUM_CHANGE_EVENTS; i++) + change->has_event[i] = FALSE; change->trigger_player = CH_PLAYER_ANY; change->trigger_side = CH_SIDE_ANY; @@ -96,6 +114,11 @@ void setElementChangeInfoToDefaults(struct ElementChangeInfo *change) change->random_percentage = 100; change->replace_when = CP_WHEN_EMPTY; + change->has_action = FALSE; + change->action_type = CA_NO_ACTION; + change->action_mode = CA_MODE_UNDEFINED; + change->action_arg = CA_ARG_UNDEFINED; + for (x = 0; x < 3; x++) for (y = 0; y < 3; y++) change->target_content[x][y] = EL_EMPTY_SPACE; @@ -111,9 +134,10 @@ void setElementChangeInfoToDefaults(struct ElementChangeInfo *change) static void setLevelInfoToDefaults(struct LevelInfo *level) { static boolean clipboard_elements_initialized = FALSE; - int i, j, x, y; + setLevelInfoToDefaults_EM(); + level->native_em_level = &native_em_level; level->game_engine_type = GAME_ENGINE_TYPE_RND; @@ -153,8 +177,11 @@ static void setLevelInfoToDefaults(struct LevelInfo *level) level->block_last_field = FALSE; /* EM does not block by default */ level->sp_block_last_field = TRUE; /* SP blocks the last field */ + +#if 0 /* !!! THIS IS NOT A LEVEL SETTING => LOGIC MOVED TO "game.c" !!! */ level->block_delay = 8; /* when blocking, block 8 frames */ level->sp_block_delay = 9; /* SP indeed blocks 9 frames, not 8 */ +#endif level->can_move_into_acid_bits = ~0; /* everything can move into acid */ level->dont_collide_with_bits = ~0; /* always deadly when colliding */ @@ -162,6 +189,25 @@ static void setLevelInfoToDefaults(struct LevelInfo *level) level->use_spring_bug = FALSE; level->use_step_counter = FALSE; + /* values for the new EMC elements */ + level->android_move_time = 10; + level->android_clone_time = 10; + level->ball_random = FALSE; + level->ball_state_initial = FALSE; + level->ball_time = 10; + level->lenses_score = 10; + level->magnify_score = 10; + level->slurp_score = 10; + level->lenses_time = 10; + level->magnify_time = 10; + level->wind_direction_initial = MV_NO_MOVING; + for (i = 0; i < NUM_MAGIC_BALL_CONTENTS; i++) + for (x = 0; x < 3; x++) + for (y = 0; y < 3; y++) + level->ball_content[i][x][y] = EL_EMPTY; + for (i = 0; i < 16; i++) + level->android_array[i] = FALSE; + level->use_custom_template = FALSE; for (i = 0; i < MAX_LEVEL_NAME_LEN; i++) @@ -252,6 +298,9 @@ static void setLevelInfoToDefaults(struct LevelInfo *level) element_info[element].explosion_delay = 16; element_info[element].ignition_delay = 8; + element_info[element].counter_initial = 0; + element_info[element].counter = 0; + for (x = 0; x < 3; x++) for (y = 0; y < 3; y++) element_info[element].content[x][y] = EL_EMPTY_SPACE; @@ -391,34 +440,14 @@ static int getFileTypeFromBasename(char *basename) return LEVEL_FILE_TYPE_UNKNOWN; } -static char *getSingleLevelBasename(int nr, int type) +static char *getSingleLevelBasename(int nr) { static char basename[MAX_FILENAME_LEN]; - char *level_filename = getStringCopy(leveldir_current->level_filename); - - if (level_filename == NULL) - level_filename = getStringCat2("%03d.", LEVELFILE_EXTENSION); - - switch (type) - { - case LEVEL_FILE_TYPE_RND: - if (nr < 0) - sprintf(basename, "template.%s", LEVELFILE_EXTENSION); - else - sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION); - break; - - case LEVEL_FILE_TYPE_EM: - sprintf(basename, "%d", nr); - break; - - case LEVEL_FILE_TYPE_UNKNOWN: - default: - sprintf(basename, level_filename, nr); - break; - } - free(level_filename); + if (nr < 0) + sprintf(basename, "template.%s", LEVELFILE_EXTENSION); + else + sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION); return basename; } @@ -461,9 +490,9 @@ static char *getPackedLevelBasename(int type) return basename; } -static char *getSingleLevelFilename(int nr, int type) +static char *getSingleLevelFilename(int nr) { - return getLevelFilenameFromBasename(getSingleLevelBasename(nr, type)); + return getLevelFilenameFromBasename(getSingleLevelBasename(nr)); } #if 0 @@ -475,9 +504,10 @@ static char *getPackedLevelFilename(int type) char *getDefaultLevelFilename(int nr) { - return getSingleLevelFilename(nr, LEVEL_FILE_TYPE_RND); + return getSingleLevelFilename(nr); } +#if 0 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi, int type) { @@ -486,6 +516,23 @@ static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi, lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type); lfi->filename = getLevelFilenameFromBasename(lfi->basename); } +#endif + +static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi, + int type, char *format, ...) +{ + static char basename[MAX_FILENAME_LEN]; + va_list ap; + + va_start(ap, format); + vsprintf(basename, format, ap); + va_end(ap); + + lfi->type = type; + lfi->packed = FALSE; + lfi->basename = basename; + lfi->filename = getLevelFilenameFromBasename(lfi->basename); +} static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi, int type) @@ -496,7 +543,37 @@ static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi, lfi->filename = getLevelFilenameFromBasename(lfi->basename); } -static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi) +static int getFiletypeFromID(char *filetype_id) +{ + char *filetype_id_lower; + int filetype = LEVEL_FILE_TYPE_UNKNOWN; + int i; + + if (filetype_id == NULL) + return LEVEL_FILE_TYPE_UNKNOWN; + + filetype_id_lower = getStringToLower(filetype_id); + + for (i = 0; filetype_id_list[i].id != NULL; i++) + { + char *id_lower = getStringToLower(filetype_id_list[i].id); + + if (strcmp(filetype_id_lower, id_lower) == 0) + filetype = filetype_id_list[i].filetype; + + free(id_lower); + + if (filetype != LEVEL_FILE_TYPE_UNKNOWN) + break; + } + + free(filetype_id_lower); + + return filetype; +} + +#if 0 +static void OLD_determineLevelFileInfo_Filename(struct LevelFileInfo *lfi) { /* special case: level number is negative => check for level template file */ if (lfi->nr < 0) @@ -508,8 +585,11 @@ static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi) if (leveldir_current->level_filename != NULL) { + int filetype = getFiletypeFromID(leveldir_current->level_filetype); + /* check for file name/pattern specified in "levelinfo.conf" */ - setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN); + setLevelFileInfo_SingleLevelFilename(lfi, filetype); + if (fileExists(lfi->filename)) return; } @@ -532,6 +612,79 @@ static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi) /* no known level file found -- try to use default values */ setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN); } +#endif + +static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi) +{ + int nr = lfi->nr; + + /* special case: level number is negative => check for level template file */ + if (nr < 0) + { + setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND, + "template.%s", LEVELFILE_EXTENSION); + + /* no fallback if template file not existing */ + return; + } + + /* special case: check for file name/pattern specified in "levelinfo.conf" */ + if (leveldir_current->level_filename != NULL) + { + int filetype = getFiletypeFromID(leveldir_current->level_filetype); + + setLevelFileInfo_FormatLevelFilename(lfi, filetype, + leveldir_current->level_filename, nr); + if (fileExists(lfi->filename)) + return; + } + + /* check for native Rocks'n'Diamonds level file */ + setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND, + "%03d.%s", nr, LEVELFILE_EXTENSION); + if (fileExists(lfi->filename)) + return; + + /* check for Emerald Mine level file (V1) */ + setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c", + 'a' + (nr / 10) % 26, '0' + nr % 10); + if (fileExists(lfi->filename)) + return; + setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c", + 'A' + (nr / 10) % 26, '0' + nr % 10); + if (fileExists(lfi->filename)) + return; + + /* check for Emerald Mine level file (V2 to V5) */ + setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr); + if (fileExists(lfi->filename)) + return; + + /* check for Emerald Mine level file (V6 / single mode) */ + setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr); + if (fileExists(lfi->filename)) + return; + setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr); + if (fileExists(lfi->filename)) + return; + + /* check for Emerald Mine level file (V6 / teamwork mode) */ + setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr); + if (fileExists(lfi->filename)) + return; + setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr); + if (fileExists(lfi->filename)) + return; + + /* check for various packed level file formats */ + setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN); + if (fileExists(lfi->filename)) + return; + + /* no known level file found -- use default values (and fail later) */ + setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND, + "%03d.%s", nr, LEVELFILE_EXTENSION); +} static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi) { @@ -946,6 +1099,7 @@ static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level) for (i = 0; i < num_changed_custom_elements; i++) { int element = getFile16BitBE(file); + unsigned long event_bits; if (!IS_CUSTOM_ELEMENT(element)) { @@ -984,7 +1138,10 @@ static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level) element_info[element].content[x][y] = getMappedElement(getFile16BitBE(file)); - element_info[element].change->events = getFile32BitBE(file); + event_bits = getFile32BitBE(file); + for (j = 0; j < NUM_CHANGE_EVENTS; j++) + if (event_bits & (1 << j)) + element_info[element].change->has_event[j] = TRUE; element_info[element].change->target_element = getMappedElement(getFile16BitBE(file)); @@ -1026,7 +1183,7 @@ 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; + int i, j, x, y; element = getFile16BitBE(file); @@ -1108,11 +1265,15 @@ static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level) for (i = 0; i < ei->num_change_pages; i++) { struct ElementChangeInfo *change = &ei->change_page[i]; + unsigned long event_bits; /* always start with reliable default values */ setElementChangeInfoToDefaults(change); - change->events = getFile32BitBE(file); + event_bits = getFile32BitBE(file); + for (j = 0; j < NUM_CHANGE_EVENTS; j++) + if (event_bits & (1 << j)) + change->has_event[j] = TRUE; change->target_element = getMappedElement(getFile16BitBE(file)); @@ -1145,8 +1306,18 @@ static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level) change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ? CH_PAGE_ANY : (1 << change->trigger_page)); +#if 1 + change->has_action = getFile8Bit(file); + change->action_type = getFile8Bit(file); + change->action_mode = getFile8Bit(file); + change->action_arg = getFile16BitBE(file); + + /* some free bytes for future change property values and padding */ + ReadUnusedBytesFromFile(file, 1); +#else /* some free bytes for future change property values and padding */ ReadUnusedBytesFromFile(file, 6); +#endif #else @@ -1769,16 +1940,27 @@ static void OLD_LoadLevelFromFileInfo_EM(struct LevelInfo *level, void CopyNativeLevel_RND_to_EM(struct LevelInfo *level) { + static int ball_xy[8][2] = + { + { 0, 0 }, + { 1, 0 }, + { 2, 0 }, + { 0, 1 }, + { 2, 1 }, + { 0, 2 }, + { 1, 2 }, + { 2, 2 }, + }; struct LevelInfo_EM *level_em = level->native_em_level; struct LEVEL *lev = level_em->lev; struct PLAYER *ply1 = level_em->ply1; struct PLAYER *ply2 = level_em->ply2; - int i, x, y; + int i, j, x, y; lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH); lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT); - lev->time_initial = level->time; + lev->time_seconds = level->time; lev->required_initial = level->gems_needed; lev->emerald_score = level->score[SC_EMERALD]; @@ -1789,7 +1971,8 @@ void CopyNativeLevel_RND_to_EM(struct LevelInfo *level) lev->eater_score = level->score[SC_YAMYAM]; lev->nut_score = level->score[SC_NUT]; lev->dynamite_score = level->score[SC_DYNAMITE]; - lev->key_score = level->score[SC_TIME_BONUS]; /* ??? CHECK THIS */ + lev->key_score = level->score[SC_KEY]; + lev->exit_score = level->score[SC_TIME_BONUS]; for (i = 0; i < MAX_ELEMENT_CONTENTS; i++) for (y = 0; y < 3; y++) @@ -1801,6 +1984,29 @@ void CopyNativeLevel_RND_to_EM(struct LevelInfo *level) lev->wonderwall_time_initial = level->time_magic_wall; lev->wheel_time = level->time_wheel; + lev->android_move_time = level->android_move_time; + lev->android_clone_time = level->android_clone_time; + lev->ball_random = level->ball_random; + lev->ball_state_initial = level->ball_state_initial; + lev->ball_time = level->ball_time; + + lev->lenses_score = level->lenses_score; + lev->magnify_score = level->magnify_score; + lev->slurp_score = level->slurp_score; + + lev->lenses_time = level->lenses_time; + lev->magnify_time = level->magnify_time; + lev->wind_direction_initial = level->wind_direction_initial; + + for (i = 0; i < NUM_MAGIC_BALL_CONTENTS; i++) + for (j = 0; j < 8; j++) + lev->ball_array[i][j] = + map_element_RND_to_EM(level-> + ball_content[i][ball_xy[j][0]][ball_xy[j][1]]); + + for (i = 0; i < 16; i++) + lev->android_array[i] = FALSE; /* !!! YET TO COME !!! */ + /* first fill the complete playfield with the default border element */ for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++) for (x = 0; x < EM_MAX_CAVE_WIDTH; x++) @@ -1811,6 +2017,9 @@ void CopyNativeLevel_RND_to_EM(struct LevelInfo *level) { int new_element = map_element_RND_to_EM(level->field[x][y]); + if (level->field[x][y] == EL_AMOEBA_DEAD) + new_element = map_element_RND_to_EM(EL_AMOEBA_WET); + level_em->cave[x + 1][y + 1] = new_element; } @@ -1820,34 +2029,47 @@ void CopyNativeLevel_RND_to_EM(struct LevelInfo *level) ply2->x_initial = 0; ply2->y_initial = 0; - /* at last, set the two players to their positions in the playfield */ + /* initialize player positions and delete players from the playfield */ for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++) { if (level->field[x][y] == EL_PLAYER_1) { ply1->x_initial = x + 1; ply1->y_initial = y + 1; + level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_EMPTY); } else if (level->field[x][y] == EL_PLAYER_2) { ply2->x_initial = x + 1; ply2->y_initial = y + 1; + level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_EMPTY); } } } void CopyNativeLevel_EM_to_RND(struct LevelInfo *level) { + static int ball_xy[8][2] = + { + { 0, 0 }, + { 1, 0 }, + { 2, 0 }, + { 0, 1 }, + { 2, 1 }, + { 0, 2 }, + { 1, 2 }, + { 2, 2 }, + }; struct LevelInfo_EM *level_em = level->native_em_level; struct LEVEL *lev = level_em->lev; struct PLAYER *ply1 = level_em->ply1; struct PLAYER *ply2 = level_em->ply2; - int i, x, y; + int i, j, x, y; level->fieldx = MIN(lev->width, MAX_LEV_FIELDX); level->fieldy = MIN(lev->height, MAX_LEV_FIELDY); - level->time = lev->time_initial; + level->time = lev->time_seconds; level->gems_needed = lev->required_initial; sprintf(level->name, "Level %d", level->file_info.nr); @@ -1860,7 +2082,8 @@ void CopyNativeLevel_EM_to_RND(struct LevelInfo *level) level->score[SC_YAMYAM] = lev->eater_score; level->score[SC_NUT] = lev->nut_score; level->score[SC_DYNAMITE] = lev->dynamite_score; - level->score[SC_TIME_BONUS] = lev->key_score; /* ??? CHECK THIS */ + level->score[SC_KEY] = lev->key_score; + level->score[SC_TIME_BONUS] = lev->exit_score; level->num_yamyam_contents = MAX_ELEMENT_CONTENTS; @@ -1874,6 +2097,29 @@ void CopyNativeLevel_EM_to_RND(struct LevelInfo *level) level->time_magic_wall = lev->wonderwall_time_initial; level->time_wheel = lev->wheel_time; + level->android_move_time = lev->android_move_time; + level->android_clone_time = lev->android_clone_time; + level->ball_random = lev->ball_random; + level->ball_state_initial = lev->ball_state_initial; + level->ball_time = lev->ball_time; + + level->lenses_score = lev->lenses_score; + level->magnify_score = lev->magnify_score; + level->slurp_score = lev->slurp_score; + + level->lenses_time = lev->lenses_time; + level->magnify_time = lev->magnify_time; + level->wind_direction_initial = lev->wind_direction_initial; + + for (i = 0; i < NUM_MAGIC_BALL_CONTENTS; i++) + for (j = 0; j < 8; j++) + level->ball_content[i][ball_xy[j][0]][ball_xy[j][1]] = + map_element_EM_to_RND(lev->ball_array[i][j]); + + for (i = 0; i < 16; i++) + level->android_array[i] = FALSE; /* !!! YET TO COME !!! */ + + /* convert the playfield (some elements need special treatment) */ for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++) { int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]); @@ -1884,8 +2130,13 @@ void CopyNativeLevel_EM_to_RND(struct LevelInfo *level) level->field[x][y] = new_element; } - level->field[ply1->x_initial - 1][ply1->y_initial - 1] = EL_PLAYER_1; + /* in case of both players set to the same field, use the first player */ level->field[ply2->x_initial - 1][ply2->y_initial - 1] = EL_PLAYER_2; + level->field[ply1->x_initial - 1][ply1->y_initial - 1] = EL_PLAYER_1; + +#if 0 + printf("::: native Emerald Mine file version: %d\n", level_em->file_version); +#endif } static void LoadLevelFromFileInfo_EM(struct LevelInfo *level, @@ -2280,10 +2531,17 @@ void LoadLevelFromFileInfo(struct LevelInfo *level, break; } + /* if level file is invalid, restore level structure to default values */ + if (level->no_valid_file) + setLevelInfoToDefaults(level); + if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN) level->game_engine_type = GAME_ENGINE_TYPE_RND; - CopyNativeLevel_Native_to_RND(level); + if (level_file_info->type == LEVEL_FILE_TYPE_RND) + CopyNativeLevel_RND_to_Native(level); + else + CopyNativeLevel_Native_to_RND(level); } void LoadLevelFromFilename(struct LevelInfo *level, char *filename) @@ -2361,10 +2619,13 @@ static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename) level->use_spring_bug = TRUE; /* only few elements were able to actively move into acid before 3.1.0 */ + /* trigger settings did not exist before 3.1.0; set to default "any" */ if (level->game_version < VERSION_IDENT(3,1,0,0)) { int i, j; + /* correct "can move into acid" settings (all zero in old levels) */ + level->can_move_into_acid_bits = 0; /* nothing can move into acid */ level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */ @@ -2376,6 +2637,8 @@ static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename) for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE); + /* correct trigger settings (stored as zero == "none" in old levels) */ + for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) { int element = EL_CUSTOM_START + i; @@ -2391,6 +2654,7 @@ static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename) } } +#if 0 /* !!! MOVED TO "game.c", BECAUSE CAN CHANGE INSIDE LEVEL EDITOR !!! */ #if 1 /* USE_NEW_BLOCK_STYLE */ /* blocking the last field when moving was corrected in version 3.1.1 */ if (level->game_version < VERSION_IDENT(3,1,1,0)) @@ -2407,6 +2671,8 @@ static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename) level->sp_block_last_field = TRUE; } #endif +#endif + } else /* always use the latest game engine version */ { @@ -2455,7 +2721,8 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename) int i, j, x, y; /* map custom element change events that have changed in newer versions - (these following values were accidentally changed in version 3.0.1) */ + (these following values were accidentally changed in version 3.0.1) + (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */ if (level->game_version <= VERSION_IDENT(3,0,0,0)) { for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) @@ -2463,7 +2730,7 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename) 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_OBSOLETE; j--) + for (j = CE_BY_OTHER_ACTION; j >= CE_COUNT_AT_ZERO; j--) { if (HAS_CHANGE_EVENT(element, j - 2)) { @@ -2473,7 +2740,7 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename) } /* order of checking and copying events to be mapped is important */ - for (j = CE_OTHER_GETS_COLLECTED; j >= CE_HITTING_SOMETHING; j--) + for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--) { if (HAS_CHANGE_EVENT(element, j - 1)) { @@ -2484,20 +2751,30 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename) } } +#if 0 + /* !!! TESTS SHOWED THAT THIS CODE SEGMENT IS NOT NEEDED FOR ANY LEVEL !!! */ + /* some custom element change events get mapped since version 3.0.3 */ - for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) +#if 1 + if (level->game_version >= VERSION_IDENT(3,0,3,0) && + level->game_version <= VERSION_IDENT(3,2,0,3)) +#endif { - int element = EL_CUSTOM_START + i; - - if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE) || - HAS_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE)) + for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) { - SET_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE, FALSE); - SET_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE, FALSE); + int element = EL_CUSTOM_START + i; - SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE); + if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE) || + HAS_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE)) + { + SET_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE, FALSE); + SET_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE, FALSE); + + SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE); + } } } +#endif /* initialize "can_change" field for old levels with only one change page */ if (level->game_version <= VERSION_IDENT(3,0,2,0)) @@ -2512,20 +2789,23 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename) } /* correct custom element values (for old levels without these options) */ - for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) + if (level->game_version < VERSION_IDENT(3,1,1,0)) { - int element = EL_CUSTOM_START + i; - struct ElementInfo *ei = &element_info[element]; + for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) + { + int element = EL_CUSTOM_START + i; + struct ElementInfo *ei = &element_info[element]; - if (ei->access_direction == MV_NO_MOVING) - ei->access_direction = MV_ALL_DIRECTIONS; + if (ei->access_direction == MV_NO_MOVING) + ei->access_direction = MV_ALL_DIRECTIONS; - for (j = 0; j < ei->num_change_pages; j++) - { - struct ElementChangeInfo *change = &ei->change_page[j]; + for (j = 0; j < ei->num_change_pages; j++) + { + struct ElementChangeInfo *change = &ei->change_page[j]; - if (change->trigger_side == CH_SIDE_NONE) - change->trigger_side = CH_SIDE_ANY; + if (change->trigger_side == CH_SIDE_NONE) + change->trigger_side = CH_SIDE_ANY; + } } } @@ -3030,7 +3310,7 @@ static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level, static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element) { struct ElementInfo *ei = &element_info[element]; - int i, x, y; + int i, j, x, y; putFile16BitBE(file, element); @@ -3092,8 +3372,13 @@ static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element) for (i = 0; i < ei->num_change_pages; i++) { struct ElementChangeInfo *change = &ei->change_page[i]; + unsigned long event_bits = 0; + + for (j = 0; j < NUM_CHANGE_EVENTS; j++) + if (change->has_event[j]) + event_bits |= (1 << j); - putFile32BitBE(file, change->events); + putFile32BitBE(file, event_bits); putFile16BitBE(file, change->target_element); @@ -3124,8 +3409,19 @@ static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element) putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE : log_2(change->trigger_page))); +#if 1 + + putFile8Bit(file, change->has_action); + putFile8Bit(file, change->action_type); + putFile8Bit(file, change->action_mode); + putFile16BitBE(file, change->action_arg); + + /* some free bytes for future change property values and padding */ + WriteUnusedBytesToFile(file, 1); +#else /* some free bytes for future change property values and padding */ WriteUnusedBytesToFile(file, 6); +#endif #else @@ -3442,7 +3738,7 @@ static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape) for (i = 0; i < tape->length; i++) { - if (i >= MAX_TAPELEN) + if (i >= MAX_TAPE_LEN) break; for (j = 0; j < MAX_PLAYERS; j++) @@ -3731,7 +4027,7 @@ void SaveTape(int nr) InitTapeDirectory(leveldir_current->subdir); /* if a tape still exists, ask to overwrite it */ - if (access(filename, F_OK) == 0) + if (fileExists(filename)) { new_tape = FALSE; if (!Request("Replace old tape ?", REQ_ASK)) @@ -3777,7 +4073,7 @@ void SaveTape(int nr) tape.changed = FALSE; if (new_tape) - Request("tape saved !", REQ_CONFIRM); + Request("Tape saved !", REQ_CONFIRM); } void DumpTape(struct TapeInfo *tape) @@ -3810,7 +4106,7 @@ void DumpTape(struct TapeInfo *tape) for (i = 0; i < tape->length; i++) { - if (i >= MAX_TAPELEN) + if (i >= MAX_TAPE_LEN) break; printf("%03d: ", i); @@ -3941,33 +4237,35 @@ void SaveScore(int nr) #define SETUP_TOKEN_QUICK_DOORS 10 #define SETUP_TOKEN_TEAM_MODE 11 #define SETUP_TOKEN_HANDICAP 12 -#define SETUP_TOKEN_TIME_LIMIT 13 -#define SETUP_TOKEN_FULLSCREEN 14 -#define SETUP_TOKEN_ASK_ON_ESCAPE 15 -#define SETUP_TOKEN_GRAPHICS_SET 16 -#define SETUP_TOKEN_SOUNDS_SET 17 -#define SETUP_TOKEN_MUSIC_SET 18 -#define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19 -#define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20 -#define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21 - -#define NUM_GLOBAL_SETUP_TOKENS 22 +#define SETUP_TOKEN_SKIP_LEVELS 13 +#define SETUP_TOKEN_TIME_LIMIT 14 +#define SETUP_TOKEN_FULLSCREEN 15 +#define SETUP_TOKEN_ASK_ON_ESCAPE 16 +#define SETUP_TOKEN_GRAPHICS_SET 17 +#define SETUP_TOKEN_SOUNDS_SET 18 +#define SETUP_TOKEN_MUSIC_SET 19 +#define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 20 +#define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 21 +#define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 22 + +#define NUM_GLOBAL_SETUP_TOKENS 23 /* editor setup */ #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1 -#define SETUP_TOKEN_EDITOR_EL_MORE 2 -#define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3 -#define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4 -#define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5 -#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 SETUP_TOKEN_EDITOR_EL_USER_DEFINED 11 - -#define NUM_EDITOR_SETUP_TOKENS 12 +#define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE_CLUB 2 +#define SETUP_TOKEN_EDITOR_EL_MORE 3 +#define SETUP_TOKEN_EDITOR_EL_SOKOBAN 4 +#define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 5 +#define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 6 +#define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 7 +#define SETUP_TOKEN_EDITOR_EL_CHARS 8 +#define SETUP_TOKEN_EDITOR_EL_CUSTOM 9 +#define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 10 +#define SETUP_TOKEN_EDITOR_EL_HEADLINES 11 +#define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 12 + +#define NUM_EDITOR_SETUP_TOKENS 13 /* shortcut setup */ #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0 @@ -4030,6 +4328,7 @@ static struct TokenInfo global_setup_tokens[] = { TYPE_SWITCH, &si.quick_doors, "quick_doors" }, { TYPE_SWITCH, &si.team_mode, "team_mode" }, { TYPE_SWITCH, &si.handicap, "handicap" }, + { TYPE_SWITCH, &si.skip_levels, "skip_levels" }, { TYPE_SWITCH, &si.time_limit, "time_limit" }, { TYPE_SWITCH, &si.fullscreen, "fullscreen" }, { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" }, @@ -4045,6 +4344,7 @@ static struct TokenInfo editor_setup_tokens[] = { { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" }, { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" }, + { TYPE_SWITCH, &sei.el_emerald_mine_club,"editor.el_emerald_mine_club"}, { TYPE_SWITCH, &sei.el_more, "editor.el_more" }, { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" }, { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" }, @@ -4130,6 +4430,7 @@ static void setSetupInfoToDefaults(struct SetupInfo *si) si->quick_doors = FALSE; si->team_mode = FALSE; si->handicap = TRUE; + si->skip_levels = TRUE; si->time_limit = TRUE; si->fullscreen = FALSE; si->ask_on_escape = TRUE; @@ -4141,16 +4442,17 @@ static void setSetupInfoToDefaults(struct SetupInfo *si) si->override_level_sounds = FALSE; si->override_level_music = FALSE; - si->editor.el_boulderdash = TRUE; - si->editor.el_emerald_mine = TRUE; - si->editor.el_more = TRUE; - si->editor.el_sokoban = TRUE; - si->editor.el_supaplex = TRUE; - si->editor.el_diamond_caves = TRUE; - 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_boulderdash = TRUE; + si->editor.el_emerald_mine = TRUE; + si->editor.el_emerald_mine_club = TRUE; + si->editor.el_more = TRUE; + si->editor.el_sokoban = TRUE; + si->editor.el_supaplex = TRUE; + si->editor.el_diamond_caves = TRUE; + 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->editor.el_user_defined = FALSE; @@ -4457,7 +4759,19 @@ void LoadUserDefinedEditorElementList(int **elements, int *num_elements) { char *value = getHashEntry(element_hash, list->token); - if (value) + if (value == NULL) /* try to find obsolete token mapping */ + { + char *mapped_token = get_mapped_token(list->token); + + if (mapped_token != NULL) + { + value = getHashEntry(element_hash, mapped_token); + + free(mapped_token); + } + } + + if (value != NULL) { (*elements)[(*num_elements)++] = atoi(value); }