&li.gems_needed, 0
},
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_32_BIT(2),
+ &li.random_seed, 0
+ },
+
{
-1, -1,
TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
&li.dont_collide_with_bits, ~0 /* default: always deadly */
},
+ {
+ -1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
+ &li.em_explodes_by_fire, FALSE
+ },
+
{
-1, -1,
TYPE_INTEGER, CONF_VALUE_16_BIT(5),
TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
&li.continuous_snapping, TRUE
},
+ {
+ EL_PLAYER_1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
+ &li.shifted_relocation, FALSE
+ },
/* (these values are different for each player) */
{
TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
&li.explosion_element[0], EL_PLAYER_1
},
+ {
+ EL_PLAYER_1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
+ &li.use_initial_inventory[0], FALSE
+ },
+ {
+ EL_PLAYER_1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
+ &li.initial_inventory_size[0], 1
+ },
+ {
+ EL_PLAYER_1, -1,
+ TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
+ &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
+ &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
+ },
{
EL_PLAYER_2, -1,
TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
&li.explosion_element[1], EL_PLAYER_2
},
+ {
+ EL_PLAYER_2, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
+ &li.use_initial_inventory[1], FALSE
+ },
+ {
+ EL_PLAYER_2, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
+ &li.initial_inventory_size[1], 1
+ },
+ {
+ EL_PLAYER_2, -1,
+ TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
+ &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
+ &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
+ },
{
EL_PLAYER_3, -1,
TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
&li.explosion_element[2], EL_PLAYER_3
},
+ {
+ EL_PLAYER_3, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
+ &li.use_initial_inventory[2], FALSE
+ },
+ {
+ EL_PLAYER_3, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
+ &li.initial_inventory_size[2], 1
+ },
+ {
+ EL_PLAYER_3, -1,
+ TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
+ &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
+ &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
+ },
{
EL_PLAYER_4, -1,
TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
&li.explosion_element[3], EL_PLAYER_4
},
+ {
+ EL_PLAYER_4, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
+ &li.use_initial_inventory[3], FALSE
+ },
+ {
+ EL_PLAYER_4, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
+ &li.initial_inventory_size[3], 1
+ },
+ {
+ EL_PLAYER_4, -1,
+ TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
+ &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
+ &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
+ },
{
EL_EMERALD, -1,
&xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
},
+ {
+ -1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
+ &xx_envelope.autowrap, FALSE
+ },
+ {
+ -1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
+ &xx_envelope.centered, FALSE
+ },
+
{
-1, -1,
TYPE_STRING, CONF_VALUE_BYTES(1),
{
-1, -1,
TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
- &xx_ei.gfx_element, EL_EMPTY_SPACE,
- &yy_ei.gfx_element
+ &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
+ &yy_ei.gfx_element_initial
},
{
{
-1, -1,
TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
- &xx_change.trigger_element, EL_EMPTY_SPACE
+ &xx_change.initial_trigger_element, EL_EMPTY_SPACE
},
{
&xx_change.action_arg, CA_ARG_UNDEFINED
},
+ {
+ -1, -1,
+ TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
+ &xx_change.action_element, EL_EMPTY_SPACE
+ },
+
{
-1, -1,
TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
{
-1, -1,
TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
- &xx_ei.gfx_element, EL_EMPTY_SPACE
+ &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
},
{
level->changed = FALSE;
+ /* set all bug compatibility flags to "false" => do not emulate this bug */
+ level->use_action_after_change_bug = FALSE;
+
if (leveldir_current == NULL) /* only when dumping level */
return;
strncmp(basename, "LEVELS.D", 8) == 0))
return LEVEL_FILE_TYPE_SP;
+ /* check for typical filename of a Diamond Caves II level package file */
+ if (strSuffix(basename, ".dc") ||
+ strSuffix(basename, ".dc2"))
+ return LEVEL_FILE_TYPE_DC;
+
/* ---------- try to determine file type from filesize ---------- */
checked_free(filename);
case EL_KEY_OBSOLETE:
element = EL_KEY_1;
+ break;
case EL_EM_KEY_1_FILE_OBSOLETE:
element = EL_EM_KEY_1;
ReadUnusedBytesFromFile(file, 7);
ei->use_gfx_element = getFile8Bit(file);
- ei->gfx_element = getMappedElement(getFile16BitBE(file));
+ ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
ei->collect_score_initial = getFile8Bit(file);
ei->collect_count_initial = getFile8Bit(file);
ei->change->delay_random = getFile16BitBE(file);
ei->change->delay_frames = getFile16BitBE(file);
- ei->change->trigger_element = getMappedElement(getFile16BitBE(file));
+ ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
ei->change->explode = getFile8Bit(file);
ei->change->use_target_content = getFile8Bit(file);
ei->use_last_ce_value = getFile8Bit(file);
ei->use_gfx_element = getFile8Bit(file);
- ei->gfx_element = getMappedElement(getFile16BitBE(file));
+ ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
ei->collect_score_initial = getFile8Bit(file);
ei->collect_count_initial = getFile8Bit(file);
change->delay_random = getFile16BitBE(file);
change->delay_frames = getFile16BitBE(file);
- change->trigger_element = getMappedElement(getFile16BitBE(file));
+ change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
change->explode = getFile8Bit(file);
change->use_target_content = getFile8Bit(file);
group->num_elements = getFile8Bit(file);
ei->use_gfx_element = getFile8Bit(file);
- ei->gfx_element = getMappedElement(getFile16BitBE(file));
+ ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
group->choice_mode = getFile8Bit(file);
{
level->no_valid_file = TRUE;
- Error(ERR_WARN, "cannot fseek level '%s' -- using empty level", filename);
+ Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level", filename);
return;
}
break;
case 0x0e77: /* quicksand (boulder) */
- element = EL_QUICKSAND_FULL;
+ element = EL_QUICKSAND_FAST_FULL;
break;
case 0x0e99: /* slow quicksand (boulder) */
break;
case 0x0ed2:
- element = EL_EXIT_OPEN;
+ element = EL_EM_EXIT_OPEN;
break;
case 0x0ee3:
- element = EL_EXIT_CLOSED;
+ element = EL_EM_EXIT_CLOSED;
break;
case 0x0eeb:
- element = EL_STEEL_EXIT_OPEN;
+ element = EL_EM_STEEL_EXIT_OPEN;
break;
case 0x0efc:
- element = EL_STEEL_EXIT_CLOSED;
+ element = EL_EM_STEEL_EXIT_CLOSED;
break;
case 0x0f4f: /* dynamite (lit 1) */
- element = EL_DYNAMITE_ACTIVE;
+ element = EL_EM_DYNAMITE_ACTIVE;
break;
case 0x0f57: /* dynamite (lit 2) */
- element = EL_DYNAMITE_ACTIVE;
+ element = EL_EM_DYNAMITE_ACTIVE;
break;
case 0x0f5f: /* dynamite (lit 3) */
- element = EL_DYNAMITE_ACTIVE;
+ element = EL_EM_DYNAMITE_ACTIVE;
break;
case 0x0f67: /* dynamite (lit 4) */
- element = EL_DYNAMITE_ACTIVE;
+ element = EL_EM_DYNAMITE_ACTIVE;
break;
case 0x0f81:
break;
case 0x0fb9:
- element = EL_MAGIC_WALL;
+ element = EL_DC_MAGIC_WALL;
break;
case 0x0fd0:
break;
case 0x1437:
- element = EL_SWITCHGATE_SWITCH_UP;
+ element = EL_DC_SWITCHGATE_SWITCH_UP;
break;
case 0x143a:
break;
case 0x14ce: /* growing steel wall (left/right) */
- element = EL_EXPANDABLE_WALL_HORIZONTAL;
+ element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
break;
case 0x14df: /* growing steel wall (up/down) */
- element = EL_EXPANDABLE_WALL_VERTICAL;
+ element = EL_EXPANDABLE_STEELWALL_VERTICAL;
break;
case 0x14e8: /* growing steel wall (up/down/left/right) */
- element = EL_EXPANDABLE_WALL_ANY;
+ element = EL_EXPANDABLE_STEELWALL_ANY;
break;
case 0x14e9:
- element = EL_SHIELD_NORMAL;
+ element = EL_SHIELD_DEADLY;
break;
case 0x1501:
break;
case 0x1578: /* quicksand (empty) */
- element = EL_QUICKSAND_EMPTY;
+ element = EL_QUICKSAND_FAST_EMPTY;
break;
case 0x1579: /* slow quicksand (empty) */
/* EL_SAND */
/* 0x1590 - 0x159f: */
- /* EL_LANDMINE */
+ /* EL_DC_LANDMINE */
case 0x15a0:
- element = EL_DYNAMITE;
+ element = EL_EM_DYNAMITE;
break;
case 0x15a1: /* key (red) */
break;
case 0x15a5: /* key (white) */
- element = EL_KEY_WHITE;
+ element = EL_DC_KEY_WHITE;
break;
case 0x15a6:
break;
case 0x1689: /* gate (white) */
- element = EL_DOOR_WHITE;
+ element = EL_DC_GATE_WHITE;
break;
case 0x168a: /* secret gate (white) */
- element = EL_DOOR_WHITE_GRAY;
+ element = EL_DC_GATE_WHITE_GRAY;
break;
case 0x168b: /* secret gate (no key) */
- element = EL_UNKNOWN;
+ element = EL_DC_GATE_FAKE_GRAY;
break;
case 0x168c:
break;
case 0x168d:
- element = EL_TIMEGATE_SWITCH;
+ element = EL_DC_TIMEGATE_SWITCH;
break;
case 0x168e:
else if (element >= 0x157c && element <= 0x158b)
element = EL_SAND;
else if (element >= 0x1590 && element <= 0x159f)
- element = EL_LANDMINE;
+ element = EL_DC_LANDMINE;
else if (element >= 0x16bc && element <= 0x16cb)
element = EL_INVISIBLE_SAND;
else
return getMappedElement(element);
}
-static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
- struct LevelFileInfo *level_file_info)
+#if 1
+
+static void LoadLevelFromFileStream_DC(FILE *file, struct LevelInfo *level,
+ int nr)
{
- char *filename = level_file_info->filename;
- FILE *file;
-#if 0
- int nr = level_file_info->nr - leveldir_current->first_level;
-#endif
byte header[DC_LEVEL_HEADER_SIZE];
int envelope_size;
int envelope_header_pos = 62;
int num_yamyam_contents;
int i, x, y;
- if (!(file = fopen(filename, MODE_READ)))
- {
- level->no_valid_file = TRUE;
+ getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
- Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
+ for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
+ {
+ unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
- return;
+ header[i * 2 + 0] = header_word >> 8;
+ header[i * 2 + 1] = header_word & 0xff;
}
-#if 0
- /* position file stream to the requested level inside the level package */
- if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
+ /* read some values from level header to check level decoding integrity */
+ fieldx = header[6] | (header[7] << 8);
+ fieldy = header[8] | (header[9] << 8);
+ num_yamyam_contents = header[60] | (header[61] << 8);
+
+ /* do some simple sanity checks to ensure that level was correctly decoded */
+ if (fieldx < 1 || fieldx > 256 ||
+ fieldy < 1 || fieldy > 256 ||
+ num_yamyam_contents < 1 || num_yamyam_contents > 8)
{
level->no_valid_file = TRUE;
- Error(ERR_WARN, "cannot fseek level '%s' -- using empty level", filename);
+ Error(ERR_WARN, "cannot decode level from stream -- using empty level");
return;
}
-#endif
-
- getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
-
- for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
- {
- unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
-
- header[i * 2 + 0] = header_word >> 8;
- header[i * 2 + 1] = header_word & 0xff;
- }
/* maximum envelope header size is 31 bytes */
envelope_header_len = header[envelope_header_pos];
level->envelope[0].text[envelope_size] = '\0';
+ level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
+ level->envelope[0].ysize = 10;
+ level->envelope[0].autowrap = TRUE;
+ level->envelope[0].centered = TRUE;
+
for (i = 0; i < level_name_len; i++)
level->name[i] = header[level_name_pos + 1 + i];
level->name[level_name_len] = '\0';
level->extra_time = header[56] | (header[57] << 8);
level->shield_normal_time = header[58] | (header[59] << 8);
- fclose(file);
+ /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
+ can slip down from flat walls, like normal walls and steel walls */
+ level->em_slippery_gems = TRUE;
+#if 0
/* Diamond Caves II levels are always surrounded by indestructible wall, but
not necessarily in a rectangular way -- fill with invisible steel wall */
+ /* !!! not always true !!! keep level and set BorderElement instead !!! */
+
for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
{
#if 1
level->field, level->fieldx, level->fieldy);
#endif
}
+#endif
}
+static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
+ struct LevelFileInfo *level_file_info)
+{
+ char *filename = level_file_info->filename;
+ FILE *file;
+ int num_magic_bytes = 8;
+ char magic_bytes[num_magic_bytes + 1];
+ int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
-/* ------------------------------------------------------------------------- */
-/* functions for loading generic level */
-/* ------------------------------------------------------------------------- */
+ if (!(file = fopen(filename, MODE_READ)))
+ {
+ level->no_valid_file = TRUE;
-void LoadLevelFromFileInfo(struct LevelInfo *level,
- struct LevelFileInfo *level_file_info)
-{
- /* always start with reliable default values */
- setLevelInfoToDefaults(level);
+ Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
- switch (level_file_info->type)
+ return;
+ }
+
+ // fseek(file, 0x0000, SEEK_SET);
+
+ if (level_file_info->packed)
{
- case LEVEL_FILE_TYPE_RND:
- LoadLevelFromFileInfo_RND(level, level_file_info);
- break;
+ /* read "magic bytes" from start of file */
+ fgets(magic_bytes, num_magic_bytes + 1, file);
- case LEVEL_FILE_TYPE_EM:
- LoadLevelFromFileInfo_EM(level, level_file_info);
- level->game_engine_type = GAME_ENGINE_TYPE_EM;
- break;
+ /* check "magic bytes" for correct file format */
+ if (!strPrefix(magic_bytes, "DC2"))
+ {
+ level->no_valid_file = TRUE;
- case LEVEL_FILE_TYPE_SP:
- LoadLevelFromFileInfo_SP(level, level_file_info);
- break;
+ Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
+ filename);
- case LEVEL_FILE_TYPE_DC:
- LoadLevelFromFileInfo_DC(level, level_file_info);
- break;
+ return;
+ }
- default:
- LoadLevelFromFileInfo_RND(level, level_file_info);
- break;
- }
+ if (strPrefix(magic_bytes, "DC2Win95") ||
+ strPrefix(magic_bytes, "DC2Win98"))
+ {
+ int position_first_level = 0x00fa;
+ int extra_bytes = 4;
+ int skip_bytes;
- /* if level file is invalid, restore level structure to default values */
- if (level->no_valid_file)
- setLevelInfoToDefaults(level);
+ /* advance file stream to first level inside the level package */
+ skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
- if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
- level->game_engine_type = GAME_ENGINE_TYPE_RND;
+ /* each block of level data is followed by block of non-level data */
+ num_levels_to_skip *= 2;
- if (level_file_info->type != LEVEL_FILE_TYPE_RND)
- CopyNativeLevel_Native_to_RND(level);
-}
+ /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
+ while (num_levels_to_skip >= 0)
+ {
+ /* advance file stream to next level inside the level package */
+ if (fseek(file, skip_bytes, SEEK_CUR) != 0)
+ {
+ level->no_valid_file = TRUE;
-void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
-{
- static struct LevelFileInfo level_file_info;
+ Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
+ filename);
- /* always start with reliable default values */
- setFileInfoToDefaults(&level_file_info);
+ return;
+ }
- level_file_info.nr = 0; /* unknown level number */
- level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
- level_file_info.filename = filename;
+ /* skip apparently unused extra bytes following each level */
+ ReadUnusedBytesFromFile(file, extra_bytes);
- LoadLevelFromFileInfo(level, &level_file_info);
-}
+ /* read size of next level in level package */
+ skip_bytes = getFile32BitLE(file);
-static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
-{
- int i, j;
+ num_levels_to_skip--;
+ }
+ }
+ else
+ {
+ level->no_valid_file = TRUE;
- if (leveldir_current == NULL) /* only when dumping level */
- return;
+ Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
+ filename);
- /* all engine modifications also valid for levels which use latest engine */
- if (level->game_version < VERSION_IDENT(3,2,0,5))
- {
- /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
- level->score[SC_TIME_BONUS] /= 10;
+ return;
+ }
}
-#if 0
- leveldir_current->latest_engine = TRUE; /* !!! TEST ONLY !!! */
-#endif
+ LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
- if (leveldir_current->latest_engine)
- {
- /* ---------- use latest game engine ----------------------------------- */
+ fclose(file);
+}
- /* For all levels which are forced to use the latest game engine version
- (normally all but user contributed, private and undefined levels), set
- the game engine version to the actual version; 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 emulation
- of the corresponding game more accurate, while (hopefully) not breaking
- existing levels created from other players. */
+#else
- level->game_version = GAME_VERSION_ACTUAL;
+static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
+ struct LevelFileInfo *level_file_info)
+{
+ char *filename = level_file_info->filename;
+ FILE *file;
+#if 0
+ int nr = level_file_info->nr - leveldir_current->first_level;
+#endif
+ byte header[DC_LEVEL_HEADER_SIZE];
+ int envelope_size;
+ int envelope_header_pos = 62;
+ int envelope_content_pos = 94;
+ int level_name_pos = 251;
+ int level_author_pos = 292;
+ int envelope_header_len;
+ int envelope_content_len;
+ int level_name_len;
+ int level_author_len;
+ int fieldx, fieldy;
+ int num_yamyam_contents;
+ int i, x, y;
- /* 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 (!(file = fopen(filename, MODE_READ)))
+ {
+ level->no_valid_file = TRUE;
- if (level->file_version < FILE_VERSION_2_0)
- level->em_slippery_gems = TRUE;
+ Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
return;
}
- /* ---------- use game engine the level was created with ----------------- */
-
+#if 0
+ /* position file stream to the requested level inside the level package */
+ if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
+ {
+ level->no_valid_file = TRUE;
+
+ Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level", filename);
+
+ return;
+ }
+#endif
+
+ getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
+
+ for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
+ {
+ unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
+
+ header[i * 2 + 0] = header_word >> 8;
+ header[i * 2 + 1] = header_word & 0xff;
+ }
+
+ /* read some values from level header to check level decoding integrity */
+ fieldx = header[6] | (header[7] << 8);
+ fieldy = header[8] | (header[9] << 8);
+ num_yamyam_contents = header[60] | (header[61] << 8);
+
+ /* do some simple sanity checks to ensure that level was correctly decoded */
+ if (fieldx < 1 || fieldx > 256 ||
+ fieldy < 1 || fieldy > 256 ||
+ num_yamyam_contents < 1 || num_yamyam_contents > 8)
+ {
+ level->no_valid_file = TRUE;
+
+ Error(ERR_WARN, "cannot read level from file '%s' -- using empty level",
+ filename);
+
+ return;
+ }
+
+ /* maximum envelope header size is 31 bytes */
+ envelope_header_len = header[envelope_header_pos];
+ /* maximum envelope content size is 110 (156?) bytes */
+ envelope_content_len = header[envelope_content_pos];
+
+ /* maximum level title size is 40 bytes */
+ level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
+ /* maximum level author size is 30 (51?) bytes */
+ level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
+
+ envelope_size = 0;
+
+ for (i = 0; i < envelope_header_len; i++)
+ if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
+ level->envelope[0].text[envelope_size++] =
+ header[envelope_header_pos + 1 + i];
+
+ if (envelope_header_len > 0 && envelope_content_len > 0)
+ {
+ if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
+ level->envelope[0].text[envelope_size++] = '\n';
+ if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
+ level->envelope[0].text[envelope_size++] = '\n';
+ }
+
+ for (i = 0; i < envelope_content_len; i++)
+ if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
+ level->envelope[0].text[envelope_size++] =
+ header[envelope_content_pos + 1 + i];
+
+ level->envelope[0].text[envelope_size] = '\0';
+
+ level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
+ level->envelope[0].ysize = 10;
+ level->envelope[0].autowrap = TRUE;
+ level->envelope[0].centered = TRUE;
+
+ for (i = 0; i < level_name_len; i++)
+ level->name[i] = header[level_name_pos + 1 + i];
+ level->name[level_name_len] = '\0';
+
+ for (i = 0; i < level_author_len; i++)
+ level->author[i] = header[level_author_pos + 1 + i];
+ level->author[level_author_len] = '\0';
+
+ num_yamyam_contents = header[60] | (header[61] << 8);
+ level->num_yamyam_contents =
+ MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
+
+ for (i = 0; i < num_yamyam_contents; i++)
+ {
+ for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
+ {
+ unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
+#if 1
+ int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
+#else
+ int element_dc = word;
+#endif
+
+ if (i < MAX_ELEMENT_CONTENTS)
+ level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
+ }
+ }
+
+ fieldx = header[6] | (header[7] << 8);
+ fieldy = header[8] | (header[9] << 8);
+ level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
+ level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
+
+ for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
+ {
+ unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
+#if 1
+ int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
+#else
+ int element_dc = word;
+#endif
+
+ if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
+ level->field[x][y] = getMappedElement_DC(element_dc);
+ }
+
+ x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
+ y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
+ level->field[x][y] = EL_PLAYER_1;
+
+ x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
+ y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
+ level->field[x][y] = EL_PLAYER_2;
+
+ level->gems_needed = header[18] | (header[19] << 8);
+
+ level->score[SC_EMERALD] = header[20] | (header[21] << 8);
+ level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
+ level->score[SC_PEARL] = header[24] | (header[25] << 8);
+ level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
+ level->score[SC_NUT] = header[28] | (header[29] << 8);
+ level->score[SC_ROBOT] = header[30] | (header[31] << 8);
+ level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
+ level->score[SC_BUG] = header[34] | (header[35] << 8);
+ level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
+ level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
+ level->score[SC_KEY] = header[40] | (header[41] << 8);
+ level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
+
+ level->time = header[44] | (header[45] << 8);
+
+ level->amoeba_speed = header[46] | (header[47] << 8);
+ level->time_light = header[48] | (header[49] << 8);
+ level->time_timegate = header[50] | (header[51] << 8);
+ level->time_wheel = header[52] | (header[53] << 8);
+ level->time_magic_wall = header[54] | (header[55] << 8);
+ level->extra_time = header[56] | (header[57] << 8);
+ level->shield_normal_time = header[58] | (header[59] << 8);
+
+ fclose(file);
+
+ /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
+ can slip down from flat walls, like normal walls and steel walls */
+ level->em_slippery_gems = TRUE;
+
+#if 0
+ /* Diamond Caves II levels are always surrounded by indestructible wall, but
+ not necessarily in a rectangular way -- fill with invisible steel wall */
+
+ /* !!! not always true !!! keep level and set BorderElement instead !!! */
+
+ for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
+ {
+#if 1
+ if ((x == 0 || x == level->fieldx - 1 ||
+ y == 0 || y == level->fieldy - 1) &&
+ level->field[x][y] == EL_EMPTY)
+ level->field[x][y] = EL_INVISIBLE_STEELWALL;
+#else
+ if ((x == 0 || x == level->fieldx - 1 ||
+ y == 0 || y == level->fieldy - 1) &&
+ level->field[x][y] == EL_EMPTY)
+ FloodFillLevel(x, y, EL_INVISIBLE_STEELWALL,
+ level->field, level->fieldx, level->fieldy);
+#endif
+ }
+#endif
+}
+
+#endif
+
+
+/* ------------------------------------------------------------------------- */
+/* functions for loading generic level */
+/* ------------------------------------------------------------------------- */
+
+void LoadLevelFromFileInfo(struct LevelInfo *level,
+ struct LevelFileInfo *level_file_info)
+{
+ /* always start with reliable default values */
+ setLevelInfoToDefaults(level);
+
+ switch (level_file_info->type)
+ {
+ case LEVEL_FILE_TYPE_RND:
+ LoadLevelFromFileInfo_RND(level, level_file_info);
+ break;
+
+ case LEVEL_FILE_TYPE_EM:
+ LoadLevelFromFileInfo_EM(level, level_file_info);
+ level->game_engine_type = GAME_ENGINE_TYPE_EM;
+ break;
+
+ case LEVEL_FILE_TYPE_SP:
+ LoadLevelFromFileInfo_SP(level, level_file_info);
+ break;
+
+ case LEVEL_FILE_TYPE_DC:
+ LoadLevelFromFileInfo_DC(level, level_file_info);
+ break;
+
+ default:
+ LoadLevelFromFileInfo_RND(level, level_file_info);
+ 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;
+
+ if (level_file_info->type != LEVEL_FILE_TYPE_RND)
+ CopyNativeLevel_Native_to_RND(level);
+}
+
+void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
+{
+ static struct LevelFileInfo level_file_info;
+
+ /* always start with reliable default values */
+ setFileInfoToDefaults(&level_file_info);
+
+ level_file_info.nr = 0; /* unknown level number */
+ level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
+ level_file_info.filename = filename;
+
+ LoadLevelFromFileInfo(level, &level_file_info);
+}
+
+static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
+{
+ int i, j;
+
+ if (leveldir_current == NULL) /* only when dumping level */
+ return;
+
+ /* all engine modifications also valid for levels which use latest engine */
+ if (level->game_version < VERSION_IDENT(3,2,0,5))
+ {
+ /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
+ level->score[SC_TIME_BONUS] /= 10;
+ }
+
+#if 0
+ leveldir_current->latest_engine = TRUE; /* !!! TEST ONLY !!! */
+#endif
+
+ if (leveldir_current->latest_engine)
+ {
+ /* ---------- use latest game engine ----------------------------------- */
+
+ /* For all levels which are forced to use the latest game engine version
+ (normally all but user contributed, private and undefined levels), set
+ the game engine version to the actual version; 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 emulation
+ of the corresponding game 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;
+
+ return;
+ }
+
+ /* ---------- use game engine the level was created with ----------------- */
+
/* For all levels which are not forced to use the latest game engine
version (normally user contributed, private and undefined levels),
use the version of the game engine the levels were created for.
/* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
{
- int element = EL_CUSTOM_START + 255;
+ int element = EL_CUSTOM_256;
struct ElementInfo *ei = &element_info[element];
struct ElementChangeInfo *change = &ei->change_page[0];
change->target_element == EL_SOKOBAN_FIELD_PLAYER)
change->target_element = EL_PLAYER_1;
}
+
+ /* try to detect and fix "Zelda II" levels, which are broken with 3.2.5 */
+ {
+ int element = EL_CUSTOM_16;
+ struct ElementInfo *ei = &element_info[element];
+
+ /* This is needed to fix a problem that was caused by a bugfix in function
+ game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
+ corrects the behaviour when a custom element changes to another custom
+ element with a higher element number that has change actions defined.
+ Normally, only one change per frame is allowed for custom elements.
+ Therefore, it is checked if a custom element already changed in the
+ current frame; if it did, subsequent changes are suppressed.
+ Unfortunately, this is only checked for element changes, but not for
+ change actions, which are still executed. As the function above loops
+ through all custom elements from lower to higher, an element change
+ resulting in a lower CE number won't be checked again, while a target
+ element with a higher number will also be checked, and potential change
+ actions will get executed for this CE, too (which is wrong), while
+ further changes are ignored (which is correct). As this bugfix breaks
+ Zelda II (but no other levels), allow the previous, incorrect behaviour
+ for this outstanding level set to not break the game or existing tapes */
+
+ if (strncmp(leveldir_current->identifier, "zelda2", 6) == 0 ||
+ strncmp(ei->description, "scanline - row 1", 16) == 0)
+ level->use_action_after_change_bug = TRUE;
+ }
+
+ /* not centering level after relocating player was default only in 3.2.3 */
+ if (level->game_version == VERSION_IDENT(3,2,3,0)) /* (no pre-releases) */
+ level->shifted_relocation = TRUE;
+
+ /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
+ if (level->game_version < VERSION_IDENT(3,2,6,0))
+ level->em_explodes_by_fire = TRUE;
}
static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
/* initialize element properties for level editor etc. */
InitElementPropertiesEngine(level->game_version);
InitElementPropertiesAfterLoading(level->game_version);
+ InitElementPropertiesGfxElement();
}
static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
lev_fieldy = level->fieldy;
/* determine border element for this level */
- SetBorderElement();
+ if (level->file_info.type == LEVEL_FILE_TYPE_DC)
+ BorderElement = EL_EMPTY; /* (in editor, SetBorderElement() is used) */
+ else
+ SetBorderElement();
}
static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
WriteUnusedBytesToFile(file, 7);
putFile8Bit(file, ei->use_gfx_element);
- putFile16BitBE(file, ei->gfx_element);
+ putFile16BitBE(file, ei->gfx_element_initial);
putFile8Bit(file, ei->collect_score_initial);
putFile8Bit(file, ei->collect_count_initial);
putFile16BitBE(file, ei->change->delay_random);
putFile16BitBE(file, ei->change->delay_frames);
- putFile16BitBE(file, ei->change->trigger_element);
+ putFile16BitBE(file, ei->change->initial_trigger_element);
putFile8Bit(file, ei->change->explode);
putFile8Bit(file, ei->change->use_target_content);
putFile8Bit(file, ei->use_last_ce_value);
putFile8Bit(file, ei->use_gfx_element);
- putFile16BitBE(file, ei->gfx_element);
+ putFile16BitBE(file, ei->gfx_element_initial);
putFile8Bit(file, ei->collect_score_initial);
putFile8Bit(file, ei->collect_count_initial);
putFile16BitBE(file, change->delay_random);
putFile16BitBE(file, change->delay_frames);
- putFile16BitBE(file, change->trigger_element);
+ putFile16BitBE(file, change->initial_trigger_element);
putFile8Bit(file, change->explode);
putFile8Bit(file, change->use_target_content);
putFile8Bit(file, group->num_elements);
putFile8Bit(file, ei->use_gfx_element);
- putFile16BitBE(file, ei->gfx_element);
+ putFile16BitBE(file, ei->gfx_element_initial);
putFile8Bit(file, group->choice_mode);
#define SETUP_TOKEN_SOUND_SIMPLE 4
#define SETUP_TOKEN_TOONS 5
#define SETUP_TOKEN_SCROLL_DELAY 6
-#define SETUP_TOKEN_SOFT_SCROLLING 7
-#define SETUP_TOKEN_FADE_SCREENS 8
-#define SETUP_TOKEN_AUTORECORD 9
-#define SETUP_TOKEN_SHOW_TITLESCREEN 10
-#define SETUP_TOKEN_QUICK_DOORS 11
-#define SETUP_TOKEN_TEAM_MODE 12
-#define SETUP_TOKEN_HANDICAP 13
-#define SETUP_TOKEN_SKIP_LEVELS 14
-#define SETUP_TOKEN_TIME_LIMIT 15
-#define SETUP_TOKEN_FULLSCREEN 16
-#define SETUP_TOKEN_FULLSCREEN_MODE 17
-#define SETUP_TOKEN_ASK_ON_ESCAPE 18
-#define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 19
-#define SETUP_TOKEN_QUICK_SWITCH 20
-#define SETUP_TOKEN_INPUT_ON_FOCUS 21
-#define SETUP_TOKEN_PREFER_AGA_GRAPHICS 22
-#define SETUP_TOKEN_GRAPHICS_SET 23
-#define SETUP_TOKEN_SOUNDS_SET 24
-#define SETUP_TOKEN_MUSIC_SET 25
-#define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 26
-#define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 27
-#define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 28
-
-#define NUM_GLOBAL_SETUP_TOKENS 29
+#define SETUP_TOKEN_SCROLL_DELAY_VALUE 7
+#define SETUP_TOKEN_SOFT_SCROLLING 8
+#define SETUP_TOKEN_FADE_SCREENS 9
+#define SETUP_TOKEN_AUTORECORD 10
+#define SETUP_TOKEN_SHOW_TITLESCREEN 11
+#define SETUP_TOKEN_QUICK_DOORS 12
+#define SETUP_TOKEN_TEAM_MODE 13
+#define SETUP_TOKEN_HANDICAP 14
+#define SETUP_TOKEN_SKIP_LEVELS 15
+#define SETUP_TOKEN_TIME_LIMIT 16
+#define SETUP_TOKEN_FULLSCREEN 17
+#define SETUP_TOKEN_FULLSCREEN_MODE 18
+#define SETUP_TOKEN_ASK_ON_ESCAPE 19
+#define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 20
+#define SETUP_TOKEN_QUICK_SWITCH 21
+#define SETUP_TOKEN_INPUT_ON_FOCUS 22
+#define SETUP_TOKEN_PREFER_AGA_GRAPHICS 23
+#define SETUP_TOKEN_GAME_FRAME_DELAY 24
+#define SETUP_TOKEN_GRAPHICS_SET 25
+#define SETUP_TOKEN_SOUNDS_SET 26
+#define SETUP_TOKEN_MUSIC_SET 27
+#define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 28
+#define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 29
+#define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 30
+
+#define NUM_GLOBAL_SETUP_TOKENS 31
/* editor setup */
#define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
#define NUM_PLAYER_SETUP_TOKENS 16
/* system setup */
-#define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
-#define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
+#define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER 0
+#define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 1
+#define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 2
-#define NUM_SYSTEM_SETUP_TOKENS 2
+#define NUM_SYSTEM_SETUP_TOKENS 3
/* options setup */
#define SETUP_TOKEN_OPTIONS_VERBOSE 0
{ TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
{ TYPE_SWITCH, &si.toons, "toons" },
{ TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
+ { TYPE_INTEGER,&si.scroll_delay_value,"scroll_delay_value" },
{ TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
{ TYPE_SWITCH, &si.fade_screens, "fade_screens" },
{ TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
{ TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
{ TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
{ TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
+ { TYPE_INTEGER,&si.game_frame_delay, "game_frame_delay" },
{ TYPE_STRING, &si.graphics_set, "graphics_set" },
{ TYPE_STRING, &si.sounds_set, "sounds_set" },
{ TYPE_STRING, &si.music_set, "music_set" },
- { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
- { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
- { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
+ { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
+ { TYPE_SWITCH3,&si.override_level_sounds, "override_level_sounds" },
+ { TYPE_SWITCH3,&si.override_level_music, "override_level_music" },
};
static boolean not_used = FALSE;
static struct TokenInfo system_setup_tokens[] =
{
+ { TYPE_STRING, &syi.sdl_videodriver, "system.sdl_videodriver" },
{ TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
{ TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
};
si->sound_music = TRUE;
si->sound_simple = TRUE;
si->toons = TRUE;
- si->double_buffering = TRUE;
- si->direct_draw = !si->double_buffering;
si->scroll_delay = TRUE;
+ si->scroll_delay_value = STD_SCROLL_DELAY;
si->soft_scrolling = TRUE;
si->fade_screens = TRUE;
si->autorecord = TRUE;
si->quick_switch = FALSE;
si->input_on_focus = FALSE;
si->prefer_aga_graphics = TRUE;
+ si->game_frame_delay = GAME_FRAME_DELAY;
- si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
- si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
- si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
+ si->graphics_set = getStringCopy(GFX_DEFAULT_SUBDIR);
+ si->sounds_set = getStringCopy(SND_DEFAULT_SUBDIR);
+ si->music_set = getStringCopy(MUS_DEFAULT_SUBDIR);
si->override_level_graphics = FALSE;
si->override_level_sounds = FALSE;
si->override_level_music = FALSE;
si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
}
+ si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
si->options.verbose = FALSE;
+
+#if defined(CREATE_SPECIAL_EDITION_RND_JUE)
+ si->handicap = FALSE;
+ si->fullscreen = TRUE;
+ si->override_level_graphics = AUTO;
+ si->override_level_sounds = AUTO;
+ si->override_level_music = AUTO;
+#endif
}
static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
decodeSetupFileHash(setup_file_hash);
- setup.direct_draw = !setup.double_buffering;
-
freeSetupFileHash(setup_file_hash);
/* needed to work around problems with fixed length strings */
player_name_new = get_corrected_login_name(setup.player_name);
free(setup.player_name);
setup.player_name = player_name_new;
+
+ /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
+ if (setup.scroll_delay == FALSE)
+ {
+ setup.scroll_delay_value = MIN_SCROLL_DELAY;
+ setup.scroll_delay = TRUE; /* now always "on" */
+ }
+
+ /* make sure that scroll delay value stays inside valid range */
+ setup.scroll_delay_value =
+ MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
}
else
Error(ERR_WARN, "using default setup values");
freeSetupFileHash(setup_file_hash);
}
-static void LoadSpecialMenuDesignSettingsFromFilename(char *filename)
+static int getElementFromToken(char *token)
{
- SetupFileHash *setup_file_hash;
+#if 1
+ char *value = getHashEntry(element_token_hash, token);
+
+ if (value != NULL)
+ return atoi(value);
+#else
int i;
+ /* !!! OPTIMIZE THIS BY USING HASH !!! */
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ if (strEqual(token, element_info[i].token_name))
+ return i;
+#endif
+
+ Error(ERR_WARN, "unknown element token '%s'", token);
+
+ return EL_UNDEFINED;
+}
+
+static int get_token_parameter_value(char *token, char *value_raw)
+{
+ char *suffix;
+
+ if (token == NULL || value_raw == NULL)
+ return ARG_UNDEFINED_VALUE;
+
+ suffix = strrchr(token, '.');
+ if (suffix == NULL)
+ suffix = token;
+
+#if 1
+ if (strEqual(suffix, ".element"))
+ return getElementFromToken(value_raw);
+#endif
+
+#if 0
+ if (strncmp(suffix, ".font", 5) == 0)
+ {
+ int i;
+
+ /* !!! OPTIMIZE THIS BY USING HASH !!! */
+ for (i = 0; i < NUM_FONTS; i++)
+ if (strEqual(value_raw, font_info[i].token_name))
+ return i;
+
+ /* if font not found, use reliable default value */
+ return FONT_INITIAL_1;
+ }
+#endif
+
+ /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
+ return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
+}
+
+void InitMenuDesignSettings_Static()
+{
+#if 0
+ static SetupFileHash *image_config_hash = NULL;
+#endif
+ int i;
+
+#if 0
+ if (image_config_hash == NULL)
+ {
+ image_config_hash = newSetupFileHash();
+
+ for (i = 0; image_config[i].token != NULL; i++)
+ setHashEntry(image_config_hash,
+ image_config[i].token,
+ image_config[i].value);
+ }
+#endif
+
+#if 1
+ /* always start with reliable default values from static default config */
+ for (i = 0; image_config_vars[i].token != NULL; i++)
+ {
+ char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
+
+ if (value != NULL)
+ *image_config_vars[i].value =
+ get_token_parameter_value(image_config_vars[i].token, value);
+ }
+
+#else
+
+ int j;
+
+ /* always start with reliable default values from static default config */
+ for (i = 0; image_config_vars[i].token != NULL; i++)
+ for (j = 0; image_config[j].token != NULL; j++)
+ if (strEqual(image_config_vars[i].token, image_config[j].token))
+ *image_config_vars[i].value =
+ get_token_parameter_value(image_config_vars[i].token,
+ image_config[j].value);
+#endif
+}
+
+static void InitMenuDesignSettings_SpecialPreProcessing()
+{
+ int i;
+
+ /* the following initializes hierarchical values from static configuration */
+
+ /* special case: initialize "ARG_DEFAULT" values in static default config */
+ /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
+ titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
+ titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
+ titlemessage_initial_default.post_delay = title_initial_default.post_delay;
+ titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
+ titlemessage_default.fade_mode = title_default.fade_mode;
+ titlemessage_default.fade_delay = title_default.fade_delay;
+ titlemessage_default.post_delay = title_default.post_delay;
+ titlemessage_default.auto_delay = title_default.auto_delay;
+
+ /* special case: initialize "ARG_DEFAULT" values in static default config */
+ /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
+ for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
+ {
+ titlemessage_initial[i] = titlemessage_initial_default;
+ titlemessage[i] = titlemessage_default;
+ }
+
+ /* special case: initialize "ARG_DEFAULT" values in static default config */
+ /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
+ for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
+ {
+ menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
+ menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
+ }
+}
+
+static void InitMenuDesignSettings_SpecialPostProcessing()
+{
+ /* special case: initialize later added SETUP list size from LEVELS value */
+ if (menu.list_size[GAME_MODE_SETUP] == -1)
+ menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
+}
+
+static void LoadMenuDesignSettingsFromFilename(char *filename)
+{
+ static struct TitleMessageInfo tmi;
+ static struct TokenInfo titlemessage_tokens[] =
+ {
+ { TYPE_INTEGER, &tmi.x, ".x" },
+ { TYPE_INTEGER, &tmi.y, ".y" },
+ { TYPE_INTEGER, &tmi.width, ".width" },
+ { TYPE_INTEGER, &tmi.height, ".height" },
+ { TYPE_INTEGER, &tmi.chars, ".chars" },
+ { TYPE_INTEGER, &tmi.lines, ".lines" },
+ { TYPE_INTEGER, &tmi.align, ".align" },
+ { TYPE_INTEGER, &tmi.valign, ".valign" },
+ { TYPE_INTEGER, &tmi.font, ".font" },
+ { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
+ { TYPE_BOOLEAN, &tmi.centered, ".centered" },
+ { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
+ { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
+ { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
+ { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
+ { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
+ { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
+
+ { -1, NULL, NULL }
+ };
+ static struct
+ {
+ struct TitleMessageInfo *array;
+ char *text;
+ }
+ titlemessage_arrays[] =
+ {
+ { titlemessage_initial, "[titlemessage_initial]" },
+ { titlemessage, "[titlemessage]" },
+
+ { NULL, NULL }
+ };
+ SetupFileHash *setup_file_hash;
+ int i, j, k;
+
#if 0
- printf("LoadSpecialMenuDesignSettings from file '%s' ...\n", filename);
+ printf("LoadMenuDesignSettings from file '%s' ...\n", filename);
#endif
if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
return;
+ /* the following initializes hierarchical values from dynamic configuration */
+
/* special case: initialize with default values that may be overwritten */
+ /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
{
- char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
- char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
- char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
+ char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
+ char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
+ char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
- if (value_x != NULL)
- menu.draw_xoffset[i] = get_integer_from_string(value_x);
- if (value_y != NULL)
- menu.draw_yoffset[i] = get_integer_from_string(value_y);
- if (list_size != NULL)
- menu.list_size[i] = get_integer_from_string(list_size);
+ if (value_1 != NULL)
+ menu.draw_xoffset[i] = get_integer_from_string(value_1);
+ if (value_2 != NULL)
+ menu.draw_yoffset[i] = get_integer_from_string(value_2);
+ if (value_3 != NULL)
+ menu.list_size[i] = get_integer_from_string(value_3);
}
/* special case: initialize with default values that may be overwritten */
+ /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
{
- char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
- char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
+ char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
+ char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
- if (value_x != NULL)
- menu.draw_xoffset_info[i] = get_integer_from_string(value_x);
- if (value_y != NULL)
- menu.draw_yoffset_info[i] = get_integer_from_string(value_y);
+ if (value_1 != NULL)
+ menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
+ if (value_2 != NULL)
+ menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
+ }
+
+ /* special case: initialize with default values that may be overwritten */
+ /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
+ for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
+ {
+ char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
+ char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
+
+ if (value_1 != NULL)
+ menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
+ if (value_2 != NULL)
+ menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
+ }
+
+ /* special case: initialize with default values that may be overwritten */
+ /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
+ for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
+ {
+ char *token_1 = "menu.enter_screen.fade_mode";
+ char *token_2 = "menu.enter_screen.fade_delay";
+ char *token_3 = "menu.enter_screen.post_delay";
+ char *token_4 = "menu.leave_screen.fade_mode";
+ char *token_5 = "menu.leave_screen.fade_delay";
+ char *token_6 = "menu.leave_screen.post_delay";
+ char *value_1 = getHashEntry(setup_file_hash, token_1);
+ char *value_2 = getHashEntry(setup_file_hash, token_2);
+ char *value_3 = getHashEntry(setup_file_hash, token_3);
+ char *value_4 = getHashEntry(setup_file_hash, token_4);
+ char *value_5 = getHashEntry(setup_file_hash, token_5);
+ char *value_6 = getHashEntry(setup_file_hash, token_6);
+
+ if (value_1 != NULL)
+ menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
+ value_1);
+ if (value_2 != NULL)
+ menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
+ value_2);
+ if (value_3 != NULL)
+ menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
+ value_3);
+ if (value_4 != NULL)
+ menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
+ value_4);
+ if (value_5 != NULL)
+ menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
+ value_5);
+ if (value_6 != NULL)
+ menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
+ value_6);
+ }
+
+ /* special case: initialize with default values that may be overwritten */
+ /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
+ for (i = 0; titlemessage_arrays[i].array != NULL; i++)
+ {
+ struct TitleMessageInfo *array = titlemessage_arrays[i].array;
+ char *base_token = titlemessage_arrays[i].text;
+
+ for (j = 0; titlemessage_tokens[j].type != -1; j++)
+ {
+ char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
+ char *value = getHashEntry(setup_file_hash, token);
+
+ if (value != NULL)
+ {
+ int parameter_value = get_token_parameter_value(token, value);
+
+ for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
+ {
+ tmi = array[k];
+
+ if (titlemessage_tokens[j].type == TYPE_INTEGER)
+ *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
+ else
+ *(int *)titlemessage_tokens[j].value = (int)parameter_value;
+
+ array[k] = tmi;
+ }
+ }
+
+ free(token);
+ }
}
/* read (and overwrite with) values that may be specified in config file */
{
char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
- if (value != NULL)
+ /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
+ if (value != NULL && !strEqual(value, ARG_DEFAULT))
*image_config_vars[i].value =
- get_auto_parameter_value(image_config_vars[i].token, value);
+ get_token_parameter_value(image_config_vars[i].token, value);
}
freeSetupFileHash(setup_file_hash);
}
-void LoadSpecialMenuDesignSettings()
+void LoadMenuDesignSettings()
{
char *filename_base = UNDEFINED_FILENAME, *filename_local;
- int i, j;
- /* always start with reliable default values from default config */
- for (i = 0; image_config_vars[i].token != NULL; i++)
- for (j = 0; image_config[j].token != NULL; j++)
- if (strEqual(image_config_vars[i].token, image_config[j].token))
- *image_config_vars[i].value =
- get_auto_parameter_value(image_config_vars[i].token,
- image_config[j].value);
+ InitMenuDesignSettings_Static();
+ InitMenuDesignSettings_SpecialPreProcessing();
+#if 1
+ if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
+#else
if (!SETUP_OVERRIDE_ARTWORK(setup, ARTWORK_TYPE_GRAPHICS))
+#endif
{
/* first look for special settings configured in level series config */
filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
if (fileExists(filename_base))
- LoadSpecialMenuDesignSettingsFromFilename(filename_base);
+ LoadMenuDesignSettingsFromFilename(filename_base);
}
filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
if (filename_local != NULL && !strEqual(filename_base, filename_local))
- LoadSpecialMenuDesignSettingsFromFilename(filename_local);
+ LoadMenuDesignSettingsFromFilename(filename_local);
+
+ InitMenuDesignSettings_SpecialPostProcessing();
}
void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
{
if (num_unknown_tokens == 0)
{
- Error(ERR_RETURN_LINE, "-");
- Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
- Error(ERR_RETURN, "- config file: '%s'", filename);
+ Error(ERR_INFO_LINE, "-");
+ Error(ERR_INFO, "warning: unknown token(s) found in config file:");
+ Error(ERR_INFO, "- config file: '%s'", filename);
num_unknown_tokens++;
}
- Error(ERR_RETURN, "- token: '%s'", list->token);
+ Error(ERR_INFO, "- token: '%s'", list->token);
}
}
if (num_unknown_tokens > 0)
- Error(ERR_RETURN_LINE, "-");
+ Error(ERR_INFO_LINE, "-");
while (*num_elements % 4) /* pad with empty elements, if needed */
(*elements)[(*num_elements)++] = EL_EMPTY;
/* ---------- music file info found ---------- */
- memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
+ clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
for (i = 0; token_to_value_ptr[i].token != NULL; i++)
{
{
if (token_nr == 0)
{
- Error(ERR_RETURN_LINE, "-");
- Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
- Error(ERR_RETURN, "- config file: '%s'", filename);
+ Error(ERR_INFO_LINE, "-");
+ Error(ERR_INFO, "warning: unknown token(s) found in config file:");
+ Error(ERR_INFO, "- config file: '%s'", filename);
}
- Error(ERR_RETURN, "- token: '%s'", token);
+ Error(ERR_INFO, "- token: '%s'", token);
}
void print_unknown_token_end(int token_nr)
{
if (token_nr > 0)
- Error(ERR_RETURN_LINE, "-");
+ Error(ERR_INFO_LINE, "-");
}
void LoadHelpAnimInfo()
}
-/* ------------------------------------------------------------------------- *
- * convert levels
- * ------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------- */
+/* convert levels */
+/* ------------------------------------------------------------------------- */
#define MAX_NUM_CONVERT_LEVELS 1000
CloseAllAndExit(0);
}
+
+
+/* ------------------------------------------------------------------------- */
+/* create and save images for use in level sketches (raw BMP format) */
+/* ------------------------------------------------------------------------- */
+
+void CreateLevelSketchImages()
+{
+#if defined(TARGET_SDL)
+ Bitmap *bitmap1;
+ Bitmap *bitmap2;
+ int i;
+
+ InitElementPropertiesGfxElement();
+
+ bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
+ bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
+
+ for (i = 0; i < NUM_FILE_ELEMENTS; i++)
+ {
+ Bitmap *src_bitmap;
+ int src_x, src_y;
+ int element = getMappedElement(i);
+ int graphic = el2edimg(element);
+ char basename1[16];
+ char basename2[16];
+ char *filename1;
+ char *filename2;
+
+ sprintf(basename1, "%03d.bmp", i);
+ sprintf(basename2, "%03ds.bmp", i);
+
+ filename1 = getPath2(global.create_images_dir, basename1);
+ filename2 = getPath2(global.create_images_dir, basename2);
+
+ getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
+ BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY, 0, 0);
+
+ if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
+ Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
+
+ getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
+ BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
+
+ if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
+ Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
+
+ free(filename1);
+ free(filename2);
+
+ if (options.debug)
+ printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
+ }
+
+ FreeBitmap(bitmap1);
+ FreeBitmap(bitmap2);
+
+ if (options.debug)
+ printf("\n");
+
+ Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
+
+ CloseAllAndExit(0);
+#endif
+}
+
+
+/* ------------------------------------------------------------------------- */
+/* create and save images for custom and group elements (raw BMP format) */
+/* ------------------------------------------------------------------------- */
+
+void CreateCustomElementImages()
+{
+#if defined(TARGET_SDL)
+ char *filename = "graphics.classic/RocksCE.bmp";
+ Bitmap *bitmap;
+ Bitmap *src_bitmap;
+ int dummy_graphic = IMG_CUSTOM_99;
+ int yoffset_ce = 0;
+ int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
+ int src_x, src_y;
+ int i;
+
+ bitmap = CreateBitmap(TILEX * 16 * 2,
+ TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
+ DEFAULT_DEPTH);
+
+ getGraphicSource(dummy_graphic, 0, &src_bitmap, &src_x, &src_y);
+
+ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+ {
+ int x = i % 16;
+ int y = i / 16;
+ int ii = i + 1;
+ int j;
+
+ BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
+ TILEX * x, TILEY * y + yoffset_ce);
+
+ BlitBitmap(src_bitmap, bitmap, 0, TILEY, TILEX, TILEY,
+ TILEX * x + TILEX * 16, TILEY * y + yoffset_ce);
+
+ for (j = 2; j >= 0; j--)
+ {
+ int c = ii % 10;
+
+ BlitBitmap(src_bitmap, bitmap, TILEX + c * 7, 0, 6, 10,
+ TILEX * x + 6 + j * 7,
+ TILEY * y + 11 + yoffset_ce);
+
+ BlitBitmap(src_bitmap, bitmap, TILEX + c * 8, TILEY, 6, 10,
+ TILEX * 16 + TILEX * x + 6 + j * 8,
+ TILEY * y + 10 + yoffset_ce);
+
+ ii /= 10;
+ }
+ }
+
+ for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
+ {
+ int x = i % 16;
+ int y = i / 16;
+ int ii = i + 1;
+ int j;
+
+ BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
+ TILEX * x, TILEY * y + yoffset_ge);
+
+ BlitBitmap(src_bitmap, bitmap, 0, TILEY, TILEX, TILEY,
+ TILEX * x + TILEX * 16, TILEY * y + yoffset_ge);
+
+ for (j = 1; j >= 0; j--)
+ {
+ int c = ii % 10;
+
+ BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
+ TILEX * x + 6 + j * 10,
+ TILEY * y + 11 + yoffset_ge);
+
+ BlitBitmap(src_bitmap, bitmap, TILEX + c * 8, TILEY + 12, 6, 10,
+ TILEX * 16 + TILEX * x + 10 + j * 8,
+ TILEY * y + 10 + yoffset_ge);
+
+ ii /= 10;
+ }
+ }
+
+ if (SDL_SaveBMP(bitmap->surface, filename) != 0)
+ Error(ERR_EXIT, "cannot save CE graphics file '%s'", filename);
+
+ FreeBitmap(bitmap);
+
+ CloseAllAndExit(0);
+#endif
+}
+
+#if 0
+void CreateLevelSketchImages_TEST()
+{
+ void CreateCustomElementImages()
+}
+#endif