X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Ffiles.c;h=3d4670f0b0f76bedc515645cd3e1a7e234839bb7;hb=3e7289558277c65b6ddcbef0f663d100e7d237fc;hp=f44c2ca39afcff12d64029235bc0f65cfabff2d5;hpb=adb5af2f532f105ee670e4805b47252eb5bcfdf1;p=rocksndiamonds.git diff --git a/src/files.c b/src/files.c index f44c2ca3..3d4670f0 100644 --- a/src/files.c +++ b/src/files.c @@ -29,7 +29,7 @@ #define CHUNK_SIZE_NONE -1 /* do not write chunk size */ #define FILE_VERS_CHUNK_SIZE 8 /* size of file version chunk */ #define LEVEL_HEADER_SIZE 80 /* size of level file header */ -#define LEVEL_HEADER_UNUSED 2 /* unused level header bytes */ +#define LEVEL_HEADER_UNUSED 1 /* 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 */ @@ -163,6 +163,7 @@ static void setLevelInfoToDefaults(struct LevelInfo *level) level->sp_block_last_field = TRUE; level->instant_relocation = FALSE; level->can_pass_to_walkable = FALSE; + level->grow_into_diggable = TRUE; level->can_move_into_acid_bits = ~0; /* everything can move into acid */ level->dont_collide_with_bits = ~0; /* always deadly when colliding */ @@ -227,6 +228,8 @@ static void setLevelInfoToDefaults(struct LevelInfo *level) element_info[element].use_gfx_element = FALSE; element_info[element].gfx_element = EL_EMPTY_SPACE; + + element_info[element].modified_settings = FALSE; } if (IS_CUSTOM_ELEMENT(element) || @@ -247,12 +250,14 @@ static void setLevelInfoToDefaults(struct LevelInfo *level) element_info[element].move_pattern = MV_ALL_DIRECTIONS; element_info[element].move_direction_initial = MV_START_AUTOMATIC; element_info[element].move_stepsize = TILEX / 8; + element_info[element].move_enter_element = EL_EMPTY_SPACE; element_info[element].move_leave_element = EL_EMPTY_SPACE; element_info[element].move_leave_type = LEAVE_TYPE_UNLIMITED; element_info[element].slippery_type = SLIPPERY_ANY_RANDOM; + element_info[element].explosion_type = EXPLODES_3X3; element_info[element].explosion_delay = 16; element_info[element].ignition_delay = 8; @@ -266,7 +271,6 @@ static void setLevelInfoToDefaults(struct LevelInfo *level) element_info[element].walk_to_action = 0; element_info[element].smash_targets = 0; element_info[element].deadliness = 0; - element_info[element].consistency = 0; element_info[element].can_explode_by_fire = FALSE; element_info[element].can_explode_smashed = FALSE; @@ -280,8 +284,6 @@ static void setLevelInfoToDefaults(struct LevelInfo *level) /* now set default properties */ SET_PROPERTY(element, EP_CAN_MOVE_INTO_ACID, TRUE); - - element_info[element].modified_settings = FALSE; } if (IS_GROUP_ELEMENT(element) || @@ -705,6 +707,7 @@ static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level) level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE); level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE); + level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE); ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED); @@ -1084,9 +1087,10 @@ static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level) ei->explosion_delay = getFile8Bit(file); ei->ignition_delay = getFile8Bit(file); + ei->explosion_type = getFile8Bit(file); /* some free bytes for future custom property values and padding */ - ReadUnusedBytesFromFile(file, 2); + ReadUnusedBytesFromFile(file, 1); /* read change property values */ @@ -1766,8 +1770,12 @@ static void LoadLevelFromFileInfo_EM(struct LevelInfo *level, static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level, int nr) { + int num_special_ports; int i, x, y; + /* for details of the Supaplex level format, see Herman Perk's Supaplex + documentation file "SPFIX63.DOC" from his Supaplex "SpeedFix" package */ + /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */ for (y = 0; y < SP_LEVEL_YSIZE; y++) { @@ -1792,28 +1800,104 @@ static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level, } } - ReadUnusedBytesFromFile(file, 4); + ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */ - /* Initial gravitation: 1 == "on", anything else (0) == "off" */ + /* initial gravity: 1 == "on", anything else (0) == "off" */ level->initial_gravity = (fgetc(file) == 1 ? TRUE : FALSE); - ReadUnusedBytesFromFile(file, 1); + ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */ /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */ for (i = 0; i < SP_LEVEL_NAME_LEN; i++) level->name[i] = fgetc(file); level->name[SP_LEVEL_NAME_LEN] = '\0'; - /* initial "freeze zonks": 2 == "on", anything else (0) == "off" */ - ReadUnusedBytesFromFile(file, 1); /* !!! NOT SUPPORTED YET !!! */ + /* initial "freeze zonks": 2 == "on", anything else (0, 1) == "off" */ + ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */ /* number of infotrons needed; 0 means that Supaplex will count the total - amount of infotrons in the level and use the low byte of that number. + amount of infotrons in the level and use the low byte of that number (a multiple of 256 infotrons will result in "0 infotrons needed"!) */ level->gems_needed = fgetc(file); - /* information about special gravity port entries */ - ReadUnusedBytesFromFile(file, 65); /* !!! NOT SUPPORTED YET !!! */ + /* number of special ("gravity") port entries below (maximum 10 allowed) */ + num_special_ports = fgetc(file); + + /* database of properties of up to 10 special ports (6 bytes per port) */ + for (i = 0; i < 10; i++) + { + int port_location, port_x, port_y, port_element; + int gravity; + + /* high and low byte of the location of a special port; if (x, y) are the + coordinates of a port in the field and (0, 0) is the top-left corner, + the 16 bit value here calculates as 2 * (x + (y * 60)) (this is twice + of what may be expected: Supaplex works with a game field in memory + which is 2 bytes per tile) */ + port_location = getFile16BitBE(file); + + /* change gravity: 1 == "turn on", anything else (0) == "turn off" */ + gravity = fgetc(file); + + /* "freeze zonks": 2 == "turn on", anything else (0, 1) == "turn off" */ + ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */ + + /* "freeze enemies": 1 == "turn on", anything else (0) == "turn off" */ + ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */ + + ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */ + + if (i >= num_special_ports) + continue; + + port_x = (port_location / 2) % SP_LEVEL_XSIZE; + port_y = (port_location / 2) / SP_LEVEL_XSIZE; + + if (port_x < 0 || port_x >= SP_LEVEL_XSIZE || + port_y < 0 || port_y >= SP_LEVEL_YSIZE) + { + Error(ERR_WARN, "special port position (%d, %d) out of bounds", + port_x, port_y); + + continue; + } + + port_element = level->field[port_x][port_y]; + + if (port_element < EL_SP_GRAVITY_PORT_RIGHT || + port_element > EL_SP_GRAVITY_PORT_UP) + { + Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y); + + continue; + } + + /* change previous (wrong) gravity inverting special port to either + gravity enabling special port or gravity disabling special port */ + level->field[port_x][port_y] += + (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT : + EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT; + } + + ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */ + + /* change special gravity ports without database entries to normal ports */ + for (y = 0; y < SP_LEVEL_YSIZE; y++) + for (x = 0; x < SP_LEVEL_XSIZE; x++) + if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT && + level->field[x][y] <= EL_SP_GRAVITY_PORT_UP) + level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT; + + /* auto-determine number of infotrons if it was stored as "0" -- see above */ + if (level->gems_needed == 0) + { + for (y = 0; y < SP_LEVEL_YSIZE; y++) + for (x = 0; x < SP_LEVEL_XSIZE; x++) + if (level->field[x][y] == EL_SP_INFOTRON) + level->gems_needed++; + + level->gems_needed &= 0xff; /* only use low byte -- see above */ + } level->fieldx = SP_LEVEL_XSIZE; level->fieldy = SP_LEVEL_YSIZE; @@ -2093,6 +2177,11 @@ static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename) pre-2.0 levels, where the game version is still taken from the file format version used to store the level -- see above). */ +#if 1 + /* player was faster than enemies in 1.0.0 and before */ + if (level->file_version == FILE_VERSION_1_0) + level->double_speed = TRUE; +#else /* do some special adjustments to support older level versions */ if (level->file_version == FILE_VERSION_1_0) { @@ -2102,8 +2191,9 @@ static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename) /* player was faster than monsters in (pre-)1.0 levels */ level->double_speed = TRUE; } +#endif - /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */ + /* default behaviour for EM style gems was "slippery" only in 2.0.1 */ if (level->game_version == VERSION_IDENT(2,0,1,0)) level->em_slippery_gems = TRUE; @@ -2261,6 +2351,22 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename) } } + /* initialize "can_explode" field for old levels which did not store this */ + if (level->game_version <= VERSION_IDENT(3,1,0,0)) + { + for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) + { + int element = EL_CUSTOM_START + i; + + if (EXPLODES_1X1_OLD(element)) + element_info[element].explosion_type = EXPLODES_1X1; + + SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) || + EXPLODES_SMASHED(element) || + EXPLODES_IMPACT(element))); + } + } + #if 0 /* set default push delay values (corrected since version 3.0.7-1) */ if (level->game_version < VERSION_IDENT(3,0,7,1)) @@ -2444,6 +2550,7 @@ static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level) putFile8Bit(file, (level->instant_relocation ? 1 : 0)); putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0)); + putFile8Bit(file, (level->grow_into_diggable ? 1 : 0)); WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED); } @@ -2751,9 +2858,10 @@ static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element) putFile8Bit(file, ei->explosion_delay); putFile8Bit(file, ei->ignition_delay); + putFile8Bit(file, ei->explosion_type); /* some free bytes for future custom property values and padding */ - WriteUnusedBytesToFile(file, 2); + WriteUnusedBytesToFile(file, 1); /* write change property values */ @@ -4713,3 +4821,117 @@ void LoadHelpTextInfo() END_HASH_ITERATION(hash, itr) #endif } + + +/* ------------------------------------------------------------------------- * + * convert levels + * ------------------------------------------------------------------------- */ + +#define MAX_NUM_CONVERT_LEVELS 1000 + +void ConvertLevels() +{ + static LevelDirTree *convert_leveldir = NULL; + static int convert_level_nr = -1; + static int num_levels_handled = 0; + static int num_levels_converted = 0; + static boolean levels_failed[MAX_NUM_CONVERT_LEVELS]; + int i; + + convert_leveldir = getTreeInfoFromIdentifier(leveldir_first, + global.convert_leveldir); + + if (convert_leveldir == NULL) + Error(ERR_EXIT, "no such level identifier: '%s'", + global.convert_leveldir); + + leveldir_current = convert_leveldir; + + if (global.convert_level_nr != -1) + { + convert_leveldir->first_level = global.convert_level_nr; + convert_leveldir->last_level = global.convert_level_nr; + } + + convert_level_nr = convert_leveldir->first_level; + + printf_line("=", 79); + printf("Converting levels\n"); + printf_line("-", 79); + printf("Level series identifier: '%s'\n", convert_leveldir->identifier); + printf("Level series name: '%s'\n", convert_leveldir->name); + printf("Level series author: '%s'\n", convert_leveldir->author); + printf("Number of levels: %d\n", convert_leveldir->levels); + printf_line("=", 79); + printf("\n"); + + for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++) + levels_failed[i] = FALSE; + + while (convert_level_nr <= convert_leveldir->last_level) + { + char *level_filename; + boolean new_level; + + level_nr = convert_level_nr++; + + printf("Level %03d: ", level_nr); + + LoadLevel(level_nr); + if (level.no_valid_file) + { + printf("(no level)\n"); + continue; + } + + printf("converting level ... "); + + level_filename = getDefaultLevelFilename(level_nr); + new_level = !fileExists(level_filename); + + if (new_level) + { + SaveLevel(level_nr); + + num_levels_converted++; + + printf("converted.\n"); + } + else + { + if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS) + levels_failed[level_nr] = TRUE; + + printf("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n"); + } + + num_levels_handled++; + } + + printf("\n"); + printf_line("=", 79); + printf("Number of levels handled: %d\n", num_levels_handled); + printf("Number of levels converted: %d (%d%%)\n", num_levels_converted, + (num_levels_handled ? + num_levels_converted * 100 / num_levels_handled : 0)); + printf_line("-", 79); + printf("Summary (for automatic parsing by scripts):\n"); + printf("LEVELDIR '%s', CONVERTED %d/%d (%d%%)", + convert_leveldir->identifier, num_levels_converted, + num_levels_handled, + (num_levels_handled ? + num_levels_converted * 100 / num_levels_handled : 0)); + + if (num_levels_handled != num_levels_converted) + { + printf(", FAILED:"); + for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++) + if (levels_failed[i]) + printf(" %03d", i); + } + + printf("\n"); + printf_line("=", 79); + + CloseAllAndExit(0); +}