&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),
{
-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(1),
- &xx_ei.gfx_element, EL_EMPTY_SPACE
+ &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
},
{
*level = li; /* copy temporary buffer back to level data */
setLevelInfoToDefaults_EM();
+ setLevelInfoToDefaults_SP();
level->native_em_level = &native_em_level;
+ level->native_sp_level = &native_sp_level;
level->file_version = FILE_VERSION_ACTUAL;
level->game_version = GAME_VERSION_ACTUAL;
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->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);
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);
}
}
-static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
- struct LevelFileInfo *level_file_info)
-{
- if (!LoadNativeLevel_EM(level_file_info->filename))
- level->no_valid_file = TRUE;
-}
-
-void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
-{
- if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
- CopyNativeLevel_RND_to_EM(level);
-}
-
-void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
-{
- if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
- CopyNativeLevel_EM_to_RND(level);
-}
-
/* ------------------------------------------------------------------------- */
/* functions for loading SP level */
/* ------------------------------------------------------------------------- */
+#if 0
+
#define NUM_SUPAPLEX_LEVELS_PER_PACKAGE 111
#define SP_LEVEL_SIZE 1536
#define SP_LEVEL_XSIZE 60
}
/* position file stream to the requested level inside the level package */
- if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
+ if (level_file_info->packed &&
+ fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
{
level->no_valid_file = TRUE;
*level = multipart_level;
}
+#endif
+
+void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
+{
+ /* ... yet to be written ... */
+}
+
+void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
+{
+ LevelInfoType *header = &native_sp_level.header;
+ int i, x, y;
+
+ level->fieldx = native_sp_level.width;
+ level->fieldy = native_sp_level.height;
+
+ for (x = 0; x < level->fieldx; x++)
+ {
+ for (y = 0; y < level->fieldy; y++)
+ {
+ int element_old = native_sp_level.playfield[x][y];
+ int element_new;
+
+ if (element_old <= 0x27)
+ element_new = getMappedElement(EL_SP_START + element_old);
+ else if (element_old == 0x28)
+ element_new = EL_INVISIBLE_WALL;
+ else
+ {
+ Error(ERR_WARN, "invalid element %d at position %d, %d",
+ element_old, x, y);
+
+ element_new = EL_UNKNOWN;
+ }
+
+ level->field[x][y] = element_new;
+ }
+ }
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ level->initial_player_gravity[i] =
+ (header->InitialGravity == 1 ? TRUE : FALSE);
+
+ for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
+ level->name[i] = header->LevelTitle[i];
+ level->name[SP_LEVEL_NAME_LEN] = '\0';
+
+ level->gems_needed = header->InfotronsNeeded;
+
+ for (i = 0; i < header->SpecialPortCount; i++)
+ {
+ SpecialPortType *port = &header->SpecialPort[i];
+ int port_location = port->PortLocation;
+ int gravity = port->Gravity;
+ int port_x, port_y, port_element;
+
+ port_x = (port_location / 2) % level->fieldx;
+ port_y = (port_location / 2) / level->fieldx;
+
+ if (port_x < 0 || port_x >= level->fieldx ||
+ port_y < 0 || port_y >= level->fieldy)
+ {
+ 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;
+ }
+
+ /* change special gravity ports without database entries to normal ports */
+ for (x = 0; x < level->fieldx; x++)
+ for (y = 0; y < level->fieldy; y++)
+ 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;
+
+ level->time = 0; /* no time limit */
+ level->amoeba_speed = 0;
+ level->time_magic_wall = 0;
+ level->time_wheel = 0;
+ level->amoeba_content = EL_EMPTY;
+
+#if 1
+ /* original Supaplex does not use score values -- use default values */
+#else
+ for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
+ level->score[i] = 0;
+#endif
+
+ /* there are no yamyams in supaplex levels */
+ for (i = 0; i < level->num_yamyam_contents; i++)
+ for (x = 0; x < 3; x++)
+ for (y = 0; y < 3; y++)
+ level->yamyam_content[i].e[x][y] = EL_EMPTY;
+}
+
+
+/* ------------------------------------------------------------------------- */
+/* functions for loading DC level */
+/* ------------------------------------------------------------------------- */
#define DC_LEVEL_HEADER_SIZE 344
#endif
+/* ------------------------------------------------------------------------- */
+/* functions for handling native levels */
+/* ------------------------------------------------------------------------- */
+
+static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
+ struct LevelFileInfo *level_file_info)
+{
+ if (!LoadNativeLevel_EM(level_file_info->filename))
+ level->no_valid_file = TRUE;
+}
+
+static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
+ struct LevelFileInfo *level_file_info)
+{
+ int pos = 0;
+
+ /* determine position of requested level inside level package */
+ if (level_file_info->packed)
+ pos = level_file_info->nr - leveldir_current->first_level;
+
+ if (!LoadNativeLevel_SP(level_file_info->filename, pos))
+ level->no_valid_file = TRUE;
+}
+
+void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
+{
+ if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
+ CopyNativeLevel_RND_to_EM(level);
+ else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
+ CopyNativeLevel_RND_to_SP(level);
+}
+
+void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
+{
+ if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
+ CopyNativeLevel_EM_to_RND(level);
+ else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
+ CopyNativeLevel_SP_to_RND(level);
+}
+
+
/* ------------------------------------------------------------------------- */
/* functions for loading generic level */
/* ------------------------------------------------------------------------- */
case LEVEL_FILE_TYPE_SP:
LoadLevelFromFileInfo_SP(level, level_file_info);
+#if 1
+ level->game_engine_type = GAME_ENGINE_TYPE_SP;
+#endif
break;
case LEVEL_FILE_TYPE_DC:
change->target_element = EL_PLAYER_1;
}
+#if 1
+ /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
+ if (level->game_version < VERSION_IDENT(3,2,5,0))
+ {
+ /* 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 (and introduces graphical bugs to Zelda I, and also breaks a
+ few other levels like Alan Bond's "FMV"), allow the previous, incorrect
+ behaviour for existing levels and tapes that make use of this bug */
+
+ level->use_action_after_change_bug = TRUE;
+ }
+#else
+ /* !!! THIS DOES NOT FIX "Zelda I" (GRAPHICALLY) AND "Alan's FMV" LEVELS */
/* try to detect and fix "Zelda II" levels, which are broken with 3.2.5 */
{
int element = EL_CUSTOM_16;
strncmp(ei->description, "scanline - row 1", 16) == 0)
level->use_action_after_change_bug = TRUE;
}
+#endif
/* 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)
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);
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);
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);
Bitmap *bitmap2;
int i;
+ InitElementPropertiesGfxElement();
+
bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
{
Bitmap *src_bitmap;
int src_x, src_y;
- int graphic = el2edimg(i);
+ int element = getMappedElement(i);
+ int graphic = el2edimg(element);
char basename1[16];
char basename2[16];
char *filename1;