+int el_act_dir2img(int element, int action, int direction)
+{
+ element = GFX_ELEMENT(element);
+
+ if (direction == MV_NONE)
+ return element_info[element].graphic[action];
+
+ direction = MV_DIR_TO_BIT(direction);
+
+ return element_info[element].direction_graphic[action][direction];
+}
+#else
+int el_act_dir2img(int element, int action, int direction)
+{
+ element = GFX_ELEMENT(element);
+ direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
+
+ /* direction_graphic[][] == graphic[] for undefined direction graphics */
+ return element_info[element].direction_graphic[action][direction];
+}
+#endif
+
+#if 0
+static int el_act_dir2crm(int element, int action, int direction)
+{
+ element = GFX_ELEMENT(element);
+
+ if (direction == MV_NONE)
+ return element_info[element].crumbled[action];
+
+ direction = MV_DIR_TO_BIT(direction);
+
+ return element_info[element].direction_crumbled[action][direction];
+}
+#else
+static int el_act_dir2crm(int element, int action, int direction)
+{
+ element = GFX_ELEMENT(element);
+ direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
+
+ /* direction_graphic[][] == graphic[] for undefined direction graphics */
+ return element_info[element].direction_crumbled[action][direction];
+}
+#endif
+
+int el_act2img(int element, int action)
+{
+ element = GFX_ELEMENT(element);
+
+ return element_info[element].graphic[action];
+}
+
+int el_act2crm(int element, int action)
+{
+ element = GFX_ELEMENT(element);
+
+ return element_info[element].crumbled[action];
+}
+
+int el_dir2img(int element, int direction)
+{
+ element = GFX_ELEMENT(element);
+
+ return el_act_dir2img(element, ACTION_DEFAULT, direction);
+}
+
+int el2baseimg(int element)
+{
+ return element_info[element].graphic[ACTION_DEFAULT];
+}
+
+int el2img(int element)
+{
+ element = GFX_ELEMENT(element);
+
+ return element_info[element].graphic[ACTION_DEFAULT];
+}
+
+int el2edimg(int element)
+{
+ element = GFX_ELEMENT(element);
+
+ return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
+}
+
+int el2preimg(int element)
+{
+ element = GFX_ELEMENT(element);
+
+ return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
+}
+
+int el2panelimg(int element)
+{
+ element = GFX_ELEMENT(element);
+
+ return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
+}
+
+int font2baseimg(int font_nr)
+{
+ return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
+}
+
+int getBeltNrFromBeltElement(int element)
+{
+ return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
+ element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
+ element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
+}
+
+int getBeltNrFromBeltActiveElement(int element)
+{
+ return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
+ element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
+ element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
+}
+
+int getBeltNrFromBeltSwitchElement(int element)
+{
+ return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
+ element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
+ element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
+}
+
+int getBeltDirNrFromBeltElement(int element)
+{
+ static int belt_base_element[4] =
+ {
+ EL_CONVEYOR_BELT_1_LEFT,
+ EL_CONVEYOR_BELT_2_LEFT,
+ EL_CONVEYOR_BELT_3_LEFT,
+ EL_CONVEYOR_BELT_4_LEFT
+ };
+
+ int belt_nr = getBeltNrFromBeltElement(element);
+ int belt_dir_nr = element - belt_base_element[belt_nr];
+
+ return (belt_dir_nr % 3);
+}
+
+int getBeltDirNrFromBeltSwitchElement(int element)
+{
+ static int belt_base_element[4] =
+ {
+ EL_CONVEYOR_BELT_1_SWITCH_LEFT,
+ EL_CONVEYOR_BELT_2_SWITCH_LEFT,
+ EL_CONVEYOR_BELT_3_SWITCH_LEFT,
+ EL_CONVEYOR_BELT_4_SWITCH_LEFT
+ };
+
+ int belt_nr = getBeltNrFromBeltSwitchElement(element);
+ int belt_dir_nr = element - belt_base_element[belt_nr];
+
+ return (belt_dir_nr % 3);
+}
+
+int getBeltDirFromBeltElement(int element)
+{
+ static int belt_move_dir[3] =
+ {
+ MV_LEFT,
+ MV_NONE,
+ MV_RIGHT
+ };
+
+ int belt_dir_nr = getBeltDirNrFromBeltElement(element);
+
+ return belt_move_dir[belt_dir_nr];
+}
+
+int getBeltDirFromBeltSwitchElement(int element)
+{
+ static int belt_move_dir[3] =
+ {
+ MV_LEFT,
+ MV_NONE,
+ MV_RIGHT
+ };
+
+ int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
+
+ return belt_move_dir[belt_dir_nr];
+}
+
+int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
+{
+ static int belt_base_element[4] =
+ {
+ EL_CONVEYOR_BELT_1_LEFT,
+ EL_CONVEYOR_BELT_2_LEFT,
+ EL_CONVEYOR_BELT_3_LEFT,
+ EL_CONVEYOR_BELT_4_LEFT
+ };
+
+ return belt_base_element[belt_nr] + belt_dir_nr;
+}
+
+int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
+{
+ int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
+
+ return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
+}
+
+int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
+{
+ static int belt_base_element[4] =
+ {
+ EL_CONVEYOR_BELT_1_SWITCH_LEFT,
+ EL_CONVEYOR_BELT_2_SWITCH_LEFT,
+ EL_CONVEYOR_BELT_3_SWITCH_LEFT,
+ EL_CONVEYOR_BELT_4_SWITCH_LEFT
+ };
+
+ return belt_base_element[belt_nr] + belt_dir_nr;
+}
+
+int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
+{
+ int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
+
+ return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
+}
+
+int getNumActivePlayers_EM()
+{
+ int num_players = 0;
+ int i;
+
+ if (!tape.playing)
+ return -1;
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ if (tape.player_participates[i])
+ num_players++;
+
+ return num_players;
+}
+
+int getGameFrameDelay_EM(int native_em_game_frame_delay)
+{
+ int game_frame_delay_value;
+
+ game_frame_delay_value =
+ (tape.playing && tape.fast_forward ? FfwdFrameDelay :
+ GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
+ GameFrameDelay);
+
+ if (tape.playing && tape.warp_forward && !tape.pausing)
+ game_frame_delay_value = 0;
+
+ return game_frame_delay_value;
+}
+
+unsigned int InitRND(long seed)
+{
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ return InitEngineRandom_EM(seed);
+ else
+ return InitEngineRandom_RND(seed);
+}
+
+#if 1
+static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
+static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
+#endif
+
+inline static int get_effective_element_EM(int tile, int frame_em)
+{
+ int element = object_mapping[tile].element_rnd;
+ int action = object_mapping[tile].action;
+ boolean is_backside = object_mapping[tile].is_backside;
+ boolean action_removing = (action == ACTION_DIGGING ||
+ action == ACTION_SNAPPING ||
+ action == ACTION_COLLECTING);
+
+ if (frame_em < 7)
+ {
+ switch (tile)
+ {
+ case Yacid_splash_eB:
+ case Yacid_splash_wB:
+ return (frame_em > 5 ? EL_EMPTY : element);
+
+ default:
+ return element;
+ }
+ }
+ else /* frame_em == 7 */
+ {
+ switch (tile)
+ {
+ case Yacid_splash_eB:
+ case Yacid_splash_wB:
+ return EL_EMPTY;
+
+ case Yemerald_stone:
+ return EL_EMERALD;
+
+ case Ydiamond_stone:
+ return EL_ROCK;
+
+ case Xdrip_stretch:
+ case Xdrip_stretchB:
+ case Ydrip_s1:
+ case Ydrip_s1B:
+ case Xball_1B:
+ case Xball_2:
+ case Xball_2B:
+ case Yball_eat:
+ case Ykey_1_eat:
+ case Ykey_2_eat:
+ case Ykey_3_eat:
+ case Ykey_4_eat:
+ case Ykey_5_eat:
+ case Ykey_6_eat:
+ case Ykey_7_eat:
+ case Ykey_8_eat:
+ case Ylenses_eat:
+ case Ymagnify_eat:
+ case Ygrass_eat:
+ case Ydirt_eat:
+ case Xsand_stonein_1:
+ case Xsand_stonein_2:
+ case Xsand_stonein_3:
+ case Xsand_stonein_4:
+ return element;
+
+ default:
+ return (is_backside || action_removing ? EL_EMPTY : element);
+ }
+ }
+}
+
+inline static boolean check_linear_animation_EM(int tile)
+{
+ switch (tile)
+ {
+ case Xsand_stonesand_1:
+ case Xsand_stonesand_quickout_1:
+ case Xsand_sandstone_1:
+ case Xsand_stonein_1:
+ case Xsand_stoneout_1:
+ case Xboom_1:
+ case Xdynamite_1:
+ case Ybug_w_n:
+ case Ybug_n_e:
+ case Ybug_e_s:
+ case Ybug_s_w:
+ case Ybug_e_n:
+ case Ybug_s_e:
+ case Ybug_w_s:
+ case Ybug_n_w:
+ case Ytank_w_n:
+ case Ytank_n_e:
+ case Ytank_e_s:
+ case Ytank_s_w:
+ case Ytank_e_n:
+ case Ytank_s_e:
+ case Ytank_w_s:
+ case Ytank_n_w:
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
+ boolean has_crumbled_graphics,
+ int crumbled, int sync_frame)
+{
+ /* if element can be crumbled, but certain action graphics are just empty
+ space (like instantly snapping sand to empty space in 1 frame), do not
+ treat these empty space graphics as crumbled graphics in EMC engine */
+ if (crumbled == IMG_EMPTY_SPACE)
+ has_crumbled_graphics = FALSE;
+
+ if (has_crumbled_graphics)
+ {
+ struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
+ int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
+ g_crumbled->anim_delay,
+ g_crumbled->anim_mode,
+ g_crumbled->anim_start_frame,
+ sync_frame);
+
+ getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
+ &g_em->crumbled_src_x, &g_em->crumbled_src_y);
+
+ g_em->crumbled_border_size = graphic_info[crumbled].border_size;
+
+ g_em->has_crumbled_graphics = TRUE;
+ }
+ else
+ {
+ g_em->crumbled_bitmap = NULL;
+ g_em->crumbled_src_x = 0;
+ g_em->crumbled_src_y = 0;
+ g_em->crumbled_border_size = 0;
+
+ g_em->has_crumbled_graphics = FALSE;
+ }
+}
+
+void ResetGfxAnimation_EM(int x, int y, int tile)
+{
+ GfxFrame[x][y] = 0;
+}
+
+void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
+ int tile, int frame_em, int x, int y)
+{
+ int action = object_mapping[tile].action;
+#if 1
+ int direction = object_mapping[tile].direction;
+ int effective_element = get_effective_element_EM(tile, frame_em);
+ int graphic = (direction == MV_NONE ?
+ el_act2img(effective_element, action) :
+ el_act_dir2img(effective_element, action, direction));
+ struct GraphicInfo *g = &graphic_info[graphic];
+ int sync_frame;
+#endif
+ boolean action_removing = (action == ACTION_DIGGING ||
+ action == ACTION_SNAPPING ||
+ action == ACTION_COLLECTING);
+ boolean action_moving = (action == ACTION_FALLING ||
+ action == ACTION_MOVING ||
+ action == ACTION_PUSHING ||
+ action == ACTION_EATING ||
+ action == ACTION_FILLING ||
+ action == ACTION_EMPTYING);
+ boolean action_falling = (action == ACTION_FALLING ||
+ action == ACTION_FILLING ||
+ action == ACTION_EMPTYING);
+
+#if 0
+ if (tile == Xsand_stonesand_1 ||
+ tile == Xsand_stonesand_2 ||
+ tile == Xsand_stonesand_3 ||
+ tile == Xsand_stonesand_4)
+ printf("::: 1: quicksand frame %d [%d]\n", GfxFrame[x][y], tile);
+#endif