X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=blobdiff_plain;f=src%2Ftools.c;h=794174c1d1ccbe0ccf34b87a474b9fb59d596c4e;hp=6a1306e45e903f9238a6526b4a6a86f68bcc1ce2;hb=3ea7bc006da99f1b13d817285820023b30ce2e38;hpb=8ae535c1c76e1c33572afbcd4b2d3d9c3d7ba63e diff --git a/src/tools.c b/src/tools.c index 6a1306e4..e33344dd 100644 --- a/src/tools.c +++ b/src/tools.c @@ -17,16 +17,17 @@ #include "init.h" #include "game.h" #include "events.h" -#include "cartoons.h" +#include "anim.h" #include "network.h" #include "tape.h" #include "screens.h" -/* select level set with EMC X11 graphics before activating EM GFX debugging */ -#define DEBUG_EM_GFX 0 +// select level set with EMC X11 graphics before activating EM GFX debugging +#define DEBUG_EM_GFX FALSE +#define DEBUG_FRAME_TIME FALSE -/* tool button identifiers */ +// tool button identifiers #define TOOL_CTRL_ID_YES 0 #define TOOL_CTRL_ID_NO 1 #define TOOL_CTRL_ID_CONFIRM 2 @@ -37,7 +38,7 @@ #define NUM_TOOL_BUTTONS 7 -/* constants for number of doors and door parts */ +// constants for number of doors and door parts #define NUM_DOORS 2 #define NUM_PANELS NUM_DOORS // #define NUM_PANELS 0 @@ -65,83 +66,83 @@ static struct DoorPartControlInfo door_part_controls[] = { { DOOR_1, - IMG_DOOR_1_GFX_PART_1, + IMG_GFX_DOOR_1_PART_1, &door_1.part_1 }, { DOOR_1, - IMG_DOOR_1_GFX_PART_2, + IMG_GFX_DOOR_1_PART_2, &door_1.part_2 }, { DOOR_1, - IMG_DOOR_1_GFX_PART_3, + IMG_GFX_DOOR_1_PART_3, &door_1.part_3 }, { DOOR_1, - IMG_DOOR_1_GFX_PART_4, + IMG_GFX_DOOR_1_PART_4, &door_1.part_4 }, { DOOR_1, - IMG_DOOR_1_GFX_PART_5, + IMG_GFX_DOOR_1_PART_5, &door_1.part_5 }, { DOOR_1, - IMG_DOOR_1_GFX_PART_6, + IMG_GFX_DOOR_1_PART_6, &door_1.part_6 }, { DOOR_1, - IMG_DOOR_1_GFX_PART_7, + IMG_GFX_DOOR_1_PART_7, &door_1.part_7 }, { DOOR_1, - IMG_DOOR_1_GFX_PART_8, + IMG_GFX_DOOR_1_PART_8, &door_1.part_8 }, { DOOR_2, - IMG_DOOR_2_GFX_PART_1, + IMG_GFX_DOOR_2_PART_1, &door_2.part_1 }, { DOOR_2, - IMG_DOOR_2_GFX_PART_2, + IMG_GFX_DOOR_2_PART_2, &door_2.part_2 }, { DOOR_2, - IMG_DOOR_2_GFX_PART_3, + IMG_GFX_DOOR_2_PART_3, &door_2.part_3 }, { DOOR_2, - IMG_DOOR_2_GFX_PART_4, + IMG_GFX_DOOR_2_PART_4, &door_2.part_4 }, { DOOR_2, - IMG_DOOR_2_GFX_PART_5, + IMG_GFX_DOOR_2_PART_5, &door_2.part_5 }, { DOOR_2, - IMG_DOOR_2_GFX_PART_6, + IMG_GFX_DOOR_2_PART_6, &door_2.part_6 }, { DOOR_2, - IMG_DOOR_2_GFX_PART_7, + IMG_GFX_DOOR_2_PART_7, &door_2.part_7 }, { DOOR_2, - IMG_DOOR_2_GFX_PART_8, + IMG_GFX_DOOR_2_PART_8, &door_2.part_8 }, @@ -164,8 +165,8 @@ static struct DoorPartControlInfo door_part_controls[] = }; -/* forward declaration for internal use */ -static void UnmapToolButtons(); +// forward declaration for internal use +static void UnmapToolButtons(void); static void HandleToolButtons(struct GadgetInfo *); static int el_act_dir2crm(int, int, int); static int el_act2crm(int, int); @@ -173,9 +174,6 @@ static int el_act2crm(int, int); static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS]; static int request_gadget_id = -1; -static unsigned int sync_frame_delay = 0; -static unsigned int sync_frame_delay_value = GAME_FRAME_DELAY; - static char *print_if_not_empty(int element) { static char *s = NULL; @@ -194,17 +192,230 @@ static char *print_if_not_empty(int element) return s; } -void DumpTile(int x, int y) +int correctLevelPosX_EM(int lx) { - int sx = SCREENX(x); - int sy = SCREENY(y); + lx -= 1; + lx -= (BorderElement != EL_EMPTY ? 1 : 0); - if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + return lx; +} + +int correctLevelPosY_EM(int ly) +{ + ly -= 1; + ly -= (BorderElement != EL_EMPTY ? 1 : 0); + + return ly; +} + +int getFieldbufferOffsetX_RND(int dir, int pos) +{ + int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0); + int dx = (dir & MV_HORIZONTAL ? pos : 0); + int dx_var = dx * TILESIZE_VAR / TILESIZE; + int fx = FX; + + if (EVEN(SCR_FIELDX)) + { + int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0); + int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var; + + if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2) + fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR; + else + fx += (dx_var > 0 ? TILEX_VAR : 0); + } + else { - x--; - y--; + fx += dx_var; } + if (full_lev_fieldx <= SCR_FIELDX) + { + if (EVEN(SCR_FIELDX)) + fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0); + else + fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0); + } + + return fx; +} + +int getFieldbufferOffsetY_RND(int dir, int pos) +{ + int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0); + int dy = (dir & MV_VERTICAL ? pos : 0); + int dy_var = dy * TILESIZE_VAR / TILESIZE; + int fy = FY; + + if (EVEN(SCR_FIELDY)) + { + int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0); + int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var; + + if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2) + fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR; + else + fy += (dy_var > 0 ? TILEY_VAR : 0); + } + else + { + fy += dy_var; + } + + if (full_lev_fieldy <= SCR_FIELDY) + { + if (EVEN(SCR_FIELDY)) + fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0); + else + fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0); + } + + return fy; +} + +static int getLevelFromScreenX_RND(int sx) +{ + int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos); + int dx = fx - FX; + int px = sx - SX; + int lx = LEVELX((px + dx) / TILESIZE_VAR); + + return lx; +} + +static int getLevelFromScreenY_RND(int sy) +{ + int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos); + int dy = fy - FY; + int py = sy - SY; + int ly = LEVELY((py + dy) / TILESIZE_VAR); + + return ly; +} + +static int getLevelFromScreenX_EM(int sx) +{ + int level_xsize = level.native_em_level->lev->width; + int full_xsize = level_xsize * TILESIZE_VAR; + + sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0); + + int fx = getFieldbufferOffsetX_EM(); + int dx = fx; + int px = sx - SX; + int lx = LEVELX((px + dx) / TILESIZE_VAR); + + lx = correctLevelPosX_EM(lx); + + return lx; +} + +static int getLevelFromScreenY_EM(int sy) +{ + int level_ysize = level.native_em_level->lev->height; + int full_ysize = level_ysize * TILESIZE_VAR; + + sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0); + + int fy = getFieldbufferOffsetY_EM(); + int dy = fy; + int py = sy - SY; + int ly = LEVELY((py + dy) / TILESIZE_VAR); + + ly = correctLevelPosY_EM(ly); + + return ly; +} + +static int getLevelFromScreenX_SP(int sx) +{ + int menBorder = setup.sp_show_border_elements; + int level_xsize = level.native_sp_level->width; + int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR; + + sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0); + + int fx = getFieldbufferOffsetX_SP(); + int dx = fx - FX; + int px = sx - SX; + int lx = LEVELX((px + dx) / TILESIZE_VAR); + + return lx; +} + +static int getLevelFromScreenY_SP(int sy) +{ + int menBorder = setup.sp_show_border_elements; + int level_ysize = level.native_sp_level->height; + int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR; + + sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0); + + int fy = getFieldbufferOffsetY_SP(); + int dy = fy - FY; + int py = sy - SY; + int ly = LEVELY((py + dy) / TILESIZE_VAR); + + return ly; +} + +static int getLevelFromScreenX_MM(int sx) +{ + int level_xsize = level.native_mm_level->fieldx; + int full_xsize = level_xsize * TILESIZE_VAR; + + sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0); + + int px = sx - SX; + int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1; + + return lx; +} + +static int getLevelFromScreenY_MM(int sy) +{ + int level_ysize = level.native_mm_level->fieldy; + int full_ysize = level_ysize * TILESIZE_VAR; + + sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0); + + int py = sy - SY; + int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1; + + return ly; +} + +int getLevelFromScreenX(int x) +{ + if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + return getLevelFromScreenX_EM(x); + if (level.game_engine_type == GAME_ENGINE_TYPE_SP) + return getLevelFromScreenX_SP(x); + if (level.game_engine_type == GAME_ENGINE_TYPE_MM) + return getLevelFromScreenX_MM(x); + else + return getLevelFromScreenX_RND(x); +} + +int getLevelFromScreenY(int y) +{ + if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + return getLevelFromScreenY_EM(y); + if (level.game_engine_type == GAME_ENGINE_TYPE_SP) + return getLevelFromScreenY_SP(y); + if (level.game_engine_type == GAME_ENGINE_TYPE_MM) + return getLevelFromScreenY_MM(y); + else + return getLevelFromScreenY_RND(y); +} + +void DumpTile(int x, int y) +{ + int sx = SCREENX(x); + int sy = SCREENY(y); + char *token_name; + printf_line("-", 79); printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y); printf_line("-", 79); @@ -217,8 +428,9 @@ void DumpTile(int x, int y) return; } - printf(" Feld: %d\t['%s']\n", Feld[x][y], - element_info[Feld[x][y]].token_name); + token_name = element_info[Feld[x][y]].token_name; + + printf(" Feld: %d\t['%s']\n", Feld[x][y], token_name); printf(" Back: %s\n", print_if_not_empty(Back[x][y])); printf(" Store: %s\n", print_if_not_empty(Store[x][y])); printf(" Store2: %s\n", print_if_not_empty(Store2[x][y])); @@ -231,12 +443,21 @@ void DumpTile(int x, int y) printf(" GfxElement: %d\n", GfxElement[x][y]); printf(" GfxAction: %d\n", GfxAction[x][y]); printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter); + printf(" Player x/y: %d, %d\n", local_player->jx, local_player->jy); printf("\n"); } +void DumpTileFromScreen(int sx, int sy) +{ + int lx = getLevelFromScreenX(sx); + int ly = getLevelFromScreenY(sy); + + DumpTile(lx, ly); +} + void SetDrawtoField(int mode) { - if (mode == DRAW_FIELDBUFFER) + if (mode == DRAW_TO_FIELDBUFFER) { FX = 2 * TILEX_VAR; FY = 2 * TILEY_VAR; @@ -247,7 +468,7 @@ void SetDrawtoField(int mode) drawto_field = fieldbuffer; } - else /* DRAW_BACKBUFFER */ + else // DRAW_TO_BACKBUFFER { FX = SX; FY = SY; @@ -260,7 +481,7 @@ void SetDrawtoField(int mode) } } -static void RedrawPlayfield_RND() +static void RedrawPlayfield_RND(void) { if (game.envelope_active) return; @@ -269,7 +490,7 @@ static void RedrawPlayfield_RND() DrawAllPlayers(); } -void RedrawPlayfield() +void RedrawPlayfield(void) { if (game_status != GAME_MODE_PLAYING) return; @@ -278,6 +499,8 @@ void RedrawPlayfield() RedrawPlayfield_EM(TRUE); else if (level.game_engine_type == GAME_ENGINE_TYPE_SP) RedrawPlayfield_SP(TRUE); + else if (level.game_engine_type == GAME_ENGINE_TYPE_MM) + RedrawPlayfield_MM(); else if (level.game_engine_type == GAME_ENGINE_TYPE_RND) RedrawPlayfield_RND(); @@ -288,184 +511,302 @@ void RedrawPlayfield() } static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height, - boolean blit_to_screen) + int draw_target) { - Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus(); + Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status); + Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr; + + if (x == -1 && y == -1) + return; - if (blit_to_screen) - BlitToScreenMasked(bitmap, x, y, width, height, x, y); + if (draw_target == DRAW_TO_SCREEN) + BlitToScreenMasked(src_bitmap, x, y, width, height, x, y); else - BlitBitmapMasked(bitmap, backbuffer, x, y, width, height, x, y); + BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y); } -static void DrawMaskedBorderExt_FIELD(boolean blit_to_screen) +static void DrawMaskedBorderExt_FIELD(int draw_target) { - if (global.border_status >= GAME_MODE_TITLE && + if (global.border_status >= GAME_MODE_MAIN && global.border_status <= GAME_MODE_PLAYING && border.draw_masked[global.border_status]) DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, - blit_to_screen); + draw_target); } -static void DrawMaskedBorderExt_DOOR_1(boolean blit_to_screen) +static void DrawMaskedBorderExt_DOOR_1(int draw_target) { - // only draw border over closed doors when drawing to backbuffer - if (!blit_to_screen && (GetDoorState() & DOOR_OPEN_1)) + // when drawing to backbuffer, never draw border over open doors + if (draw_target == DRAW_TO_BACKBUFFER && + (GetDoorState() & DOOR_OPEN_1)) return; if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] && (global.border_status != GAME_MODE_EDITOR || border.draw_masked[GFX_SPECIAL_ARG_EDITOR])) - DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, blit_to_screen); + DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target); } -static void DrawMaskedBorderExt_DOOR_2(boolean blit_to_screen) +static void DrawMaskedBorderExt_DOOR_2(int draw_target) { - // only draw border over closed doors when drawing to backbuffer - if (!blit_to_screen && (GetDoorState() & DOOR_OPEN_2)) + // when drawing to backbuffer, never draw border over open doors + if (draw_target == DRAW_TO_BACKBUFFER && + (GetDoorState() & DOOR_OPEN_2)) return; if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] && global.border_status != GAME_MODE_EDITOR) - DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, blit_to_screen); + DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target); } -static void DrawMaskedBorderExt_DOOR_3(boolean blit_to_screen) +static void DrawMaskedBorderExt_DOOR_3(int draw_target) { - /* currently not available */ + // currently not available } -static void DrawMaskedBorderExt_ALL(boolean blit_to_screen) +static void DrawMaskedBorderExt_ALL(int draw_target) { - DrawMaskedBorderExt_FIELD(blit_to_screen); - DrawMaskedBorderExt_DOOR_1(blit_to_screen); - DrawMaskedBorderExt_DOOR_2(blit_to_screen); - DrawMaskedBorderExt_DOOR_3(blit_to_screen); + DrawMaskedBorderExt_FIELD(draw_target); + DrawMaskedBorderExt_DOOR_1(draw_target); + DrawMaskedBorderExt_DOOR_2(draw_target); + DrawMaskedBorderExt_DOOR_3(draw_target); } -static void DrawMaskedBorderExt(int redraw_mask, boolean blit_to_screen) +static void DrawMaskedBorderExt(int redraw_mask, int draw_target) { - /* never draw masked screen borders on borderless screens */ - if (game_status == GAME_MODE_LOADING || - game_status == GAME_MODE_TITLE) + // never draw masked screen borders on borderless screens + if (global.border_status == GAME_MODE_LOADING || + global.border_status == GAME_MODE_TITLE) return; if (redraw_mask & REDRAW_ALL) - DrawMaskedBorderExt_ALL(blit_to_screen); + DrawMaskedBorderExt_ALL(draw_target); else { if (redraw_mask & REDRAW_FIELD) - DrawMaskedBorderExt_FIELD(blit_to_screen); + DrawMaskedBorderExt_FIELD(draw_target); if (redraw_mask & REDRAW_DOOR_1) - DrawMaskedBorderExt_DOOR_1(blit_to_screen); + DrawMaskedBorderExt_DOOR_1(draw_target); if (redraw_mask & REDRAW_DOOR_2) - DrawMaskedBorderExt_DOOR_2(blit_to_screen); + DrawMaskedBorderExt_DOOR_2(draw_target); if (redraw_mask & REDRAW_DOOR_3) - DrawMaskedBorderExt_DOOR_3(blit_to_screen); + DrawMaskedBorderExt_DOOR_3(draw_target); } } -void DrawMaskedBorder_FIELD() +void DrawMaskedBorder_FIELD(void) { - DrawMaskedBorderExt_FIELD(FALSE); + DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER); } void DrawMaskedBorder(int redraw_mask) { - DrawMaskedBorderExt(redraw_mask, FALSE); + DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER); } -void DrawMaskedBorderToScreen(int redraw_mask) +void DrawMaskedBorderToTarget(int draw_target) { - DrawMaskedBorderExt(redraw_mask, TRUE); + if (draw_target == DRAW_TO_BACKBUFFER || + draw_target == DRAW_TO_SCREEN) + { + DrawMaskedBorderExt(REDRAW_ALL, draw_target); + } + else + { + int last_border_status = global.border_status; + + if (draw_target == DRAW_TO_FADE_SOURCE) + { + global.border_status = gfx.fade_border_source_status; + gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source; + } + else if (draw_target == DRAW_TO_FADE_TARGET) + { + global.border_status = gfx.fade_border_target_status; + gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target; + } + + DrawMaskedBorderExt(REDRAW_ALL, draw_target); + + global.border_status = last_border_status; + gfx.masked_border_bitmap_ptr = backbuffer; + } } -void BlitScreenToBitmap_RND(Bitmap *target_bitmap) +void DrawTileCursor(int draw_target) { - int fx = FX, fy = FY; - int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0); - int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0); + Bitmap *fade_bitmap; + Bitmap *src_bitmap; + int src_x, src_y; + int dst_x, dst_y; + int graphic = IMG_GLOBAL_TILE_CURSOR; + int frame = 0; + int tilesize = TILESIZE_VAR; + int width = tilesize; + int height = tilesize; - int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0); - int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0); - int dx_var = dx * TILESIZE_VAR / TILESIZE; - int dy_var = dy * TILESIZE_VAR / TILESIZE; - int ffx, ffy; + if (game_status != GAME_MODE_PLAYING) + return; - ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var; - ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var; + if (!tile_cursor.enabled || + !tile_cursor.active) + return; - if (EVEN(SCR_FIELDX)) - { - if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR) - fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR; - else - fx += (dx_var > 0 ? TILEX_VAR : 0); - } - else + if (tile_cursor.moving) { - fx += dx_var; - } + int step = TILESIZE_VAR / 4; + int dx = tile_cursor.target_x - tile_cursor.x; + int dy = tile_cursor.target_y - tile_cursor.y; - if (EVEN(SCR_FIELDY)) - { - if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR) - fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR; + if (ABS(dx) < step) + tile_cursor.x = tile_cursor.target_x; else - fy += (dy_var > 0 ? TILEY_VAR : 0); - } - else - { - fy += dy_var; - } + tile_cursor.x += SIGN(dx) * step; - if (full_lev_fieldx <= SCR_FIELDX) - { - if (EVEN(SCR_FIELDX)) - fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0); + if (ABS(dy) < step) + tile_cursor.y = tile_cursor.target_y; else - fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0); - } + tile_cursor.y += SIGN(dy) * step; - if (full_lev_fieldy <= SCR_FIELDY) - { - if (EVEN(SCR_FIELDY)) - fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0); - else - fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0); + if (tile_cursor.x == tile_cursor.target_x && + tile_cursor.y == tile_cursor.target_y) + tile_cursor.moving = FALSE; } + dst_x = tile_cursor.x; + dst_y = tile_cursor.y; + + frame = getGraphicAnimationFrame(graphic, -1); + + getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y); + + fade_bitmap = + (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source : + draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL); + + if (draw_target == DRAW_TO_SCREEN) + BlitToScreenMasked(src_bitmap, src_x, src_y, width, height, dst_x, dst_y); + else + BlitBitmapMasked(src_bitmap, fade_bitmap, src_x, src_y, width, height, + dst_x, dst_y); +} + +void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy) +{ BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY); } +void BlitScreenToBitmap_RND(Bitmap *target_bitmap) +{ + int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos); + int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos); + + BlitScreenToBitmapExt_RND(target_bitmap, fx, fy); +} + void BlitScreenToBitmap(Bitmap *target_bitmap) { if (level.game_engine_type == GAME_ENGINE_TYPE_EM) BlitScreenToBitmap_EM(target_bitmap); else if (level.game_engine_type == GAME_ENGINE_TYPE_SP) BlitScreenToBitmap_SP(target_bitmap); + else if (level.game_engine_type == GAME_ENGINE_TYPE_MM) + BlitScreenToBitmap_MM(target_bitmap); else if (level.game_engine_type == GAME_ENGINE_TYPE_RND) BlitScreenToBitmap_RND(target_bitmap); redraw_mask |= REDRAW_FIELD; } -void DrawFramesPerSecond() +static void DrawFramesPerSecond(void) { char text[100]; int font_nr = FONT_TEXT_2; int font_width = getFontWidth(font_nr); + int draw_deactivation_mask = GetDrawDeactivationMask(); + boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE); + + // draw FPS with leading space (needed if field buffer deactivated) + sprintf(text, " %04.1f fps", global.frames_per_second); - sprintf(text, "%04.1f fps", global.frames_per_second); + // override draw deactivation mask (required for invisible warp mode) + SetDrawDeactivationMask(REDRAW_NONE); - DrawTextExt(backbuffer, WIN_XSIZE - font_width * strlen(text), 0, text, - font_nr, BLIT_OPAQUE); + // draw opaque FPS if field buffer deactivated, else draw masked FPS + DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text, + font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE)); + + // set draw deactivation mask to previous value + SetDrawDeactivationMask(draw_deactivation_mask); + + // force full-screen redraw in this frame + redraw_mask = REDRAW_ALL; +} + +#if DEBUG_FRAME_TIME +static void PrintFrameTimeDebugging(void) +{ + static unsigned int last_counter = 0; + unsigned int counter = Counter(); + int diff_1 = counter - last_counter; + int diff_2 = diff_1 - GAME_FRAME_DELAY; + int diff_2_max = 20; + int diff_2_cut = MIN(ABS(diff_2), diff_2_max); + char diff_bar[2 * diff_2_max + 5]; + int pos = 0; + int i; + + diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' '); + + for (i = 0; i < diff_2_max; i++) + diff_bar[pos++] = (diff_2 >= 0 ? ' ' : + i >= diff_2_max - diff_2_cut ? '-' : ' '); + + diff_bar[pos++] = '|'; + + for (i = 0; i < diff_2_max; i++) + diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' '); + + diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' '); + + diff_bar[pos++] = '\0'; + + Error(ERR_INFO, "%06d [%02d] [%c%02d] %s", + counter, + diff_1, + (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2), + diff_bar); + + last_counter = counter; +} +#endif + +static int unifiedRedrawMask(int mask) +{ + if (mask & REDRAW_ALL) + return REDRAW_ALL; + + if (mask & REDRAW_FIELD && mask & REDRAW_DOORS) + return REDRAW_ALL; + + return mask; } -void BackToFront() +static boolean equalRedrawMasks(int mask_1, int mask_2) { + return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2); +} + +void BackToFront(void) +{ + static int last_redraw_mask = REDRAW_NONE; + + // force screen redraw in every frame to continue drawing global animations + // (but always use the last redraw mask to prevent unwanted side effects) if (redraw_mask == REDRAW_NONE) - return; + redraw_mask = last_redraw_mask; + + last_redraw_mask = redraw_mask; #if 1 // masked border now drawn immediately when blitting backbuffer to window @@ -478,6 +819,10 @@ void BackToFront() if (redraw_mask & REDRAW_FPS) DrawFramesPerSecond(); + // remove playfield redraw before potentially merging with doors redraw + if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE)) + redraw_mask &= ~REDRAW_FIELD; + // redraw complete window if both playfield and (some) doors need redraw if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS) redraw_mask = REDRAW_ALL; @@ -498,39 +843,69 @@ void BackToFront() } else if (redraw_mask & REDRAW_DOORS) { + // merge door areas to prevent calling screen redraw more than once + int x1 = WIN_XSIZE; + int y1 = WIN_YSIZE; + int x2 = 0; + int y2 = 0; + if (redraw_mask & REDRAW_DOOR_1) - BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY); + { + x1 = MIN(x1, DX); + y1 = MIN(y1, DY); + x2 = MAX(x2, DX + DXSIZE); + y2 = MAX(y2, DY + DYSIZE); + } if (redraw_mask & REDRAW_DOOR_2) - BlitBitmap(backbuffer, window, VX, VY, VXSIZE, VYSIZE, VX, VY); + { + x1 = MIN(x1, VX); + y1 = MIN(y1, VY); + x2 = MAX(x2, VX + VXSIZE); + y2 = MAX(y2, VY + VYSIZE); + } if (redraw_mask & REDRAW_DOOR_3) - BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY); + { + x1 = MIN(x1, EX); + y1 = MIN(y1, EY); + x2 = MAX(x2, EX + EXSIZE); + y2 = MAX(y2, EY + EYSIZE); + } + + // make sure that at least one pixel is blitted, and inside the screen + // (else nothing is blitted, causing the animations not to be updated) + x1 = MIN(MAX(0, x1), WIN_XSIZE - 1); + y1 = MIN(MAX(0, y1), WIN_YSIZE - 1); + x2 = MIN(MAX(1, x2), WIN_XSIZE); + y2 = MIN(MAX(1, y2), WIN_YSIZE); + + BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1); } redraw_mask = REDRAW_NONE; -} -static void FadeCrossSaveBackbuffer() -{ - BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0); +#if DEBUG_FRAME_TIME + PrintFrameTimeDebugging(); +#endif } -static void FadeCrossRestoreBackbuffer() +void BackToFront_WithFrameDelay(unsigned int frame_delay_value) { - int redraw_mask_last = redraw_mask; + unsigned int frame_delay_value_old = GetVideoFrameDelay(); - BlitBitmap(bitmap_db_cross, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0); + SetVideoFrameDelay(frame_delay_value); - // do not change redraw mask when restoring backbuffer after cross-fading - redraw_mask = redraw_mask_last; + BackToFront(); + + SetVideoFrameDelay(frame_delay_value_old); } +static int fade_type_skip = FADE_TYPE_NONE; + static void FadeExt(int fade_mask, int fade_mode, int fade_type) { - static int fade_type_skip = FADE_TYPE_NONE; void (*draw_border_function)(void) = NULL; - Bitmap *bitmap = (fade_mode & FADE_TYPE_TRANSFORM ? bitmap_db_cross : NULL); int x, y, width, height; int fade_delay, post_delay; @@ -538,25 +913,15 @@ static void FadeExt(int fade_mask, int fade_mode, int fade_type) { if (fade_type_skip != FADE_TYPE_NONE) { - /* skip all fade operations until specified fade operation */ + // skip all fade operations until specified fade operation if (fade_type & fade_type_skip) fade_type_skip = FADE_TYPE_NONE; return; } -#if 1 - FadeCrossSaveBackbuffer(); -#endif - if (fading.fade_mode & FADE_TYPE_TRANSFORM) - { -#if 0 - FadeCrossSaveBackbuffer(); -#endif - return; - } } redraw_mask |= fade_mask; @@ -573,7 +938,7 @@ static void FadeExt(int fade_mask, int fade_mode, int fade_type) if (fade_type_skip != FADE_TYPE_NONE) { - /* skip all fade operations until specified fade operation */ + // skip all fade operations until specified fade operation if (fade_type & fade_type_skip) fade_type_skip = FADE_TYPE_NONE; @@ -593,11 +958,11 @@ static void FadeExt(int fade_mask, int fade_mode, int fade_type) height = FADE_SYSIZE; if (border.draw_masked_when_fading) - draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */ + draw_border_function = DrawMaskedBorder_FIELD; // update when fading else - DrawMaskedBorder_FIELD(); /* draw once */ + DrawMaskedBorder_FIELD(); // draw once } - else /* REDRAW_ALL */ + else // REDRAW_ALL { x = 0; y = 0; @@ -619,17 +984,57 @@ static void FadeExt(int fade_mask, int fade_mode, int fade_type) return; } - FadeRectangle(bitmap, x, y, width, height, fade_mode, fade_delay, post_delay, + FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay, draw_border_function); - if (fade_type == FADE_TYPE_FADE_OUT) - FadeCrossRestoreBackbuffer(); - redraw_mask &= ~fade_mask; + + ClearAutoRepeatKeyEvents(); +} + +static void SetScreenStates_BeforeFadingIn(void) +{ + // temporarily set screen mode for animations to screen after fading in + global.anim_status = global.anim_status_next; + + // store backbuffer with all animations that will be started after fading in + if (fade_type_skip != FADE_MODE_SKIP_FADE_IN) + PrepareFadeBitmap(DRAW_TO_FADE_TARGET); + + // set screen mode for animations back to fading + global.anim_status = GAME_MODE_PSEUDO_FADING; +} + +static void SetScreenStates_AfterFadingIn(void) +{ + // store new source screen (to use correct masked border for fading) + gfx.fade_border_source_status = global.border_status; + + global.anim_status = global.anim_status_next; +} + +static void SetScreenStates_BeforeFadingOut(void) +{ + // store new target screen (to use correct masked border for fading) + gfx.fade_border_target_status = game_status; + + // set screen mode for animations to fading + global.anim_status = GAME_MODE_PSEUDO_FADING; + + // store backbuffer with all animations that will be stopped for fading out + if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT) + PrepareFadeBitmap(DRAW_TO_FADE_SOURCE); +} + +static void SetScreenStates_AfterFadingOut(void) +{ + global.border_status = game_status; } void FadeIn(int fade_mask) { + SetScreenStates_BeforeFadingIn(); + #if 1 DrawMaskedBorder(REDRAW_ALL); #endif @@ -643,10 +1048,31 @@ void FadeIn(int fade_mask) FADE_SY = REAL_SY; FADE_SXSIZE = FULL_SXSIZE; FADE_SYSIZE = FULL_SYSIZE; + + // activate virtual buttons depending on upcoming game status + if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) && + game_status == GAME_MODE_PLAYING && !tape.playing) + SetOverlayActive(TRUE); + + SetScreenStates_AfterFadingIn(); + + // force update of global animation status in case of rapid screen changes + redraw_mask = REDRAW_ALL; + BackToFront(); } void FadeOut(int fade_mask) { + // update screen if areas covered by "fade_mask" and "redraw_mask" differ + if (!equalRedrawMasks(fade_mask, redraw_mask) && + fade_type_skip != FADE_MODE_SKIP_FADE_OUT) + BackToFront(); + + SetScreenStates_BeforeFadingOut(); + + SetTileCursorActive(FALSE); + SetOverlayActive(FALSE); + #if 0 DrawMaskedBorder(REDRAW_ALL); #endif @@ -656,7 +1082,7 @@ void FadeOut(int fade_mask) else FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT); - global.border_status = game_status; + SetScreenStates_AfterFadingOut(); } static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set) @@ -669,38 +1095,38 @@ static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set) fading = fading_leave_stored; } -void FadeSetEnterMenu() +void FadeSetEnterMenu(void) { fading = menu.enter_menu; - FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */ + FadeSetLeaveNext(fading, TRUE); // (keep same fade mode) } -void FadeSetLeaveMenu() +void FadeSetLeaveMenu(void) { fading = menu.leave_menu; - FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */ + FadeSetLeaveNext(fading, TRUE); // (keep same fade mode) } -void FadeSetEnterScreen() +void FadeSetEnterScreen(void) { fading = menu.enter_screen[game_status]; - FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */ + FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store } -void FadeSetNextScreen() +void FadeSetNextScreen(void) { fading = menu.next_screen[game_status]; // (do not overwrite fade mode set by FadeSetEnterScreen) - // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */ + // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode) } -void FadeSetLeaveScreen() +void FadeSetLeaveScreen(void) { - FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */ + FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall } void FadeSetFromType(int type) @@ -713,51 +1139,53 @@ void FadeSetFromType(int type) FadeSetLeaveMenu(); } -void FadeSetDisabled() +void FadeSetDisabled(void) { static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 }; fading = fading_none; } -void FadeSkipNextFadeIn() +void FadeSkipNextFadeIn(void) { FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP); } -void FadeSkipNextFadeOut() +void FadeSkipNextFadeOut(void) { FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP); } -Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic) +static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic) { + if (graphic == IMG_UNDEFINED) + return NULL; + boolean redefined = getImageListEntryFromImageID(graphic)->redefined; - return (graphic == IMG_UNDEFINED ? NULL : - graphic_info[graphic].bitmap != NULL || redefined ? + return (graphic_info[graphic].bitmap != NULL || redefined ? graphic_info[graphic].bitmap : graphic_info[default_graphic].bitmap); } -Bitmap *getBackgroundBitmap(int graphic) +static Bitmap *getBackgroundBitmap(int graphic) { return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND); } -Bitmap *getGlobalBorderBitmap(int graphic) +static Bitmap *getGlobalBorderBitmap(int graphic) { return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER); } -Bitmap *getGlobalBorderBitmapFromGameStatus() +Bitmap *getGlobalBorderBitmapFromStatus(int status) { int graphic = - (game_status == GAME_MODE_MAIN || - game_status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN : - game_status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES : - game_status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR : - game_status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING : + (status == GAME_MODE_MAIN || + status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN : + status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES : + status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR : + status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING : IMG_GLOBAL_BORDER); return getGlobalBorderBitmap(graphic); @@ -796,7 +1224,7 @@ void SetDoorBackgroundImage(int graphic) SetDoorBackgroundBitmap(getBackgroundBitmap(graphic)); } -void SetPanelBackground() +void SetPanelBackground(void) { struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL]; @@ -808,7 +1236,7 @@ void SetPanelBackground() void DrawBackground(int x, int y, int width, int height) { - /* "drawto" might still point to playfield buffer here (hall of fame) */ + // "drawto" might still point to playfield buffer here (hall of fame) ClearRectangleOnBackground(backbuffer, x, y, width, height); if (IN_GFX_FIELD_FULL(x, y)) @@ -850,20 +1278,25 @@ static int dx_last = -1, dy_last = -1; static int dxsize_last = -1, dysize_last = -1; static int vx_last = -1, vy_last = -1; static int vxsize_last = -1, vysize_last = -1; +static int ex_last = -1, ey_last = -1; +static int exsize_last = -1, eysize_last = -1; -boolean CheckIfGlobalBorderHasChanged() +boolean CheckIfGlobalBorderHasChanged(void) { // if game status has not changed, global border has not changed either if (game_status == game_status_last) return FALSE; // determine and store new global border bitmap for current game status - global_border_bitmap = getGlobalBorderBitmapFromGameStatus(); + global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status); return (global_border_bitmap_last != global_border_bitmap); } -boolean CheckIfGlobalBorderRedrawIsNeeded() +#define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0 + +#if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED +static boolean CheckIfGlobalBorderRedrawIsNeeded(void) { // if game status has not changed, nothing has to be redrawn if (game_status == game_status_last) @@ -892,10 +1325,16 @@ boolean CheckIfGlobalBorderRedrawIsNeeded() vxsize_last != VXSIZE || vysize_last != VYSIZE) return TRUE; + // redraw if position or size of editor area has changed + if (ex_last != EX || ey_last != EY || + exsize_last != EXSIZE || eysize_last != EYSIZE) + return TRUE; + return FALSE; } +#endif -void RedrawGlobalBorderFromBitmap(Bitmap *bitmap) +static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap) { if (bitmap) BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0); @@ -903,50 +1342,68 @@ void RedrawGlobalBorderFromBitmap(Bitmap *bitmap) ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE); } -void RedrawGlobalBorder() +void RedrawGlobalBorder(void) { - Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus(); + Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status); RedrawGlobalBorderFromBitmap(bitmap); redraw_mask = REDRAW_ALL; } -static void RedrawGlobalBorderIfNeeded() +static void RedrawGlobalBorderIfNeeded(void) { +#if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED if (game_status == game_status_last) return; +#endif // copy current draw buffer to later copy back areas that have not changed if (game_status_last != GAME_MODE_TITLE) - BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0); + BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0); +#if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED if (CheckIfGlobalBorderRedrawIsNeeded()) +#endif { // redraw global screen border (or clear, if defined to be empty) RedrawGlobalBorderFromBitmap(global_border_bitmap); + if (game_status == GAME_MODE_EDITOR) + DrawSpecialEditorDoor(); + // copy previous playfield and door areas, if they are defined on both // previous and current screen and if they still have the same size if (real_sx_last != -1 && real_sy_last != -1 && REAL_SX != -1 && REAL_SY != -1 && full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE) - BlitBitmap(bitmap_db_store, backbuffer, + BlitBitmap(bitmap_db_store_1, backbuffer, real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY); if (dx_last != -1 && dy_last != -1 && DX != -1 && DY != -1 && dxsize_last == DXSIZE && dysize_last == DYSIZE) - BlitBitmap(bitmap_db_store, backbuffer, + BlitBitmap(bitmap_db_store_1, backbuffer, dx_last, dy_last, DXSIZE, DYSIZE, DX, DY); - if (vx_last != -1 && vy_last != -1 && - VX != -1 && VY != -1 && - vxsize_last == VXSIZE && vysize_last == VYSIZE) - BlitBitmap(bitmap_db_store, backbuffer, - vx_last, vy_last, VXSIZE, VYSIZE, VX, VY); + if (game_status != GAME_MODE_EDITOR) + { + if (vx_last != -1 && vy_last != -1 && + VX != -1 && VY != -1 && + vxsize_last == VXSIZE && vysize_last == VYSIZE) + BlitBitmap(bitmap_db_store_1, backbuffer, + vx_last, vy_last, VXSIZE, VYSIZE, VX, VY); + } + else + { + if (ex_last != -1 && ey_last != -1 && + EX != -1 && EY != -1 && + exsize_last == EXSIZE && eysize_last == EYSIZE) + BlitBitmap(bitmap_db_store_1, backbuffer, + ex_last, ey_last, EXSIZE, EYSIZE, EX, EY); + } redraw_mask = REDRAW_ALL; } @@ -967,25 +1424,29 @@ static void RedrawGlobalBorderIfNeeded() vy_last = VY; vxsize_last = VXSIZE; vysize_last = VYSIZE; + ex_last = EX; + ey_last = EY; + exsize_last = EXSIZE; + eysize_last = EYSIZE; } -void ClearField() +void ClearField(void) { RedrawGlobalBorderIfNeeded(); - /* !!! "drawto" might still point to playfield buffer here (see above) !!! */ - /* (when entering hall of fame after playing) */ + // !!! "drawto" might still point to playfield buffer here (see above) !!! + // (when entering hall of fame after playing) DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE); - /* !!! maybe this should be done before clearing the background !!! */ + // !!! maybe this should be done before clearing the background !!! if (game_status == GAME_MODE_PLAYING) { ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE); - SetDrawtoField(DRAW_FIELDBUFFER); + SetDrawtoField(DRAW_TO_FIELDBUFFER); } else { - SetDrawtoField(DRAW_BACKBUFFER); + SetDrawtoField(DRAW_TO_BACKBUFFER); } } @@ -994,12 +1455,16 @@ void MarkTileDirty(int x, int y) redraw_mask |= REDRAW_FIELD; } -void SetBorderElement() +void SetBorderElement(void) { int x, y; BorderElement = EL_EMPTY; + // the MM game engine does not use a visible border element + if (level.game_engine_type == GAME_ENGINE_TYPE_MM) + return; + for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++) { for (x = 0; x < lev_fieldx; x++) @@ -1013,16 +1478,17 @@ void SetBorderElement() } } -void FloodFillLevel(int from_x, int from_y, int fill_element, - short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY], - int max_fieldx, int max_fieldy) +void FloodFillLevelExt(int from_x, int from_y, int fill_element, + int max_array_fieldx, int max_array_fieldy, + short field[max_array_fieldx][max_array_fieldy], + int max_fieldx, int max_fieldy) { int i,x,y; int old_element; static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } }; static int safety = 0; - /* check if starting field still has the desired content */ + // check if starting field still has the desired content if (field[from_x][from_y] == fill_element) return; @@ -1040,12 +1506,22 @@ void FloodFillLevel(int from_x, int from_y, int fill_element, y = from_y + check[i][1]; if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element) - FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy); + FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy, + field, max_fieldx, max_fieldy); } safety--; } +void FloodFillLevel(int from_x, int from_y, int fill_element, + short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY], + int max_fieldx, int max_fieldy) +{ + FloodFillLevelExt(from_x, from_y, fill_element, + MAX_LEV_FIELDX, MAX_LEV_FIELDY, field, + max_fieldx, max_fieldy); +} + void SetRandomAnimationValue(int x, int y) { gfx.anim_random_frame = GfxRandom[x][y]; @@ -1053,7 +1529,7 @@ void SetRandomAnimationValue(int x, int y) int getGraphicAnimationFrame(int graphic, int sync_frame) { - /* animation synchronized with global frame counter, not move position */ + // animation synchronized with global frame counter, not move position if (graphic_info[graphic].anim_global_sync || sync_frame < 0) sync_frame = FrameCounter; @@ -1064,59 +1540,68 @@ int getGraphicAnimationFrame(int graphic, int sync_frame) sync_frame); } -void getSizedGraphicSourceExt(int graphic, int frame, int tilesize, - Bitmap **bitmap, int *x, int *y, - boolean get_backside) +void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap) { struct GraphicInfo *g = &graphic_info[graphic]; - Bitmap *src_bitmap = g->bitmap; - int src_x = g->src_x + (get_backside ? g->offset2_x : 0); - int src_y = g->src_y + (get_backside ? g->offset2_y : 0); int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE); - // if no in-game graphics defined, always use standard graphic size - if (g->bitmaps[IMG_BITMAP_GAME] == NULL) - tilesize = TILESIZE; - if (tilesize == gfx.standard_tile_size) - src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD]; + *bitmap = g->bitmaps[IMG_BITMAP_STANDARD]; else if (tilesize == game.tile_size) - src_bitmap = g->bitmaps[IMG_BITMAP_GAME]; + *bitmap = g->bitmaps[IMG_BITMAP_GAME]; else - src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)]; + *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)]; +} + +void getGraphicSourceXY(int graphic, int frame, int *x, int *y, + boolean get_backside) +{ + struct GraphicInfo *g = &graphic_info[graphic]; + int src_x = g->src_x + (get_backside ? g->offset2_x : 0); + int src_y = g->src_y + (get_backside ? g->offset2_y : 0); - if (g->offset_y == 0) /* frames are ordered horizontally */ + if (g->offset_y == 0) // frames are ordered horizontally { int max_width = g->anim_frames_per_line * g->width; int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x; - src_x = pos % max_width; - src_y = src_y % g->height + pos / max_width * g->height; + *x = pos % max_width; + *y = src_y % g->height + pos / max_width * g->height; } - else if (g->offset_x == 0) /* frames are ordered vertically */ + else if (g->offset_x == 0) // frames are ordered vertically { int max_height = g->anim_frames_per_line * g->height; int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y; - src_x = src_x % g->width + pos / max_height * g->width; - src_y = pos % max_height; + *x = src_x % g->width + pos / max_height * g->width; + *y = pos % max_height; } - else /* frames are ordered diagonally */ + else // frames are ordered diagonally { - src_x = src_x + frame * g->offset_x; - src_y = src_y + frame * g->offset_y; + *x = src_x + frame * g->offset_x; + *y = src_y + frame * g->offset_y; } - - *bitmap = src_bitmap; - *x = src_x * tilesize / g->tile_size; - *y = src_y * tilesize / g->tile_size; } -void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap, - int *x, int *y, boolean get_backside) +void getSizedGraphicSourceExt(int graphic, int frame, int tilesize, + Bitmap **bitmap, int *x, int *y, + boolean get_backside) { - getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, - get_backside); + struct GraphicInfo *g = &graphic_info[graphic]; + + // if no graphics defined at all, use fallback graphics + if (g->bitmaps == NULL) + *g = graphic_info[IMG_CHAR_EXCLAM]; + + // if no in-game graphics defined, always use standard graphic size + if (g->bitmaps[IMG_BITMAP_GAME] == NULL) + tilesize = TILESIZE; + + getGraphicSourceBitmap(graphic, tilesize, bitmap); + getGraphicSourceXY(graphic, frame, x, y, get_backside); + + *x = *x * tilesize / g->tile_size; + *y = *y * tilesize / g->tile_size; } void getSizedGraphicSource(int graphic, int frame, int tilesize, @@ -1136,43 +1621,11 @@ void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y) getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y); } -inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap, - int *x, int *y, boolean get_backside) +static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap, + int *x, int *y, boolean get_backside) { - struct GraphicInfo *g = &graphic_info[graphic]; - int src_x = g->src_x + (get_backside ? g->offset2_x : 0); - int src_y = g->src_y + (get_backside ? g->offset2_y : 0); - - if (TILESIZE_VAR != TILESIZE) - return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y, - get_backside); - - *bitmap = g->bitmap; - - if (g->offset_y == 0) /* frames are ordered horizontally */ - { - int max_width = g->anim_frames_per_line * g->width; - int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x; - - *x = pos % max_width; - *y = src_y % g->height + pos / max_width * g->height; - } - else if (g->offset_x == 0) /* frames are ordered vertically */ - { - int max_height = g->anim_frames_per_line * g->height; - int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y; - - *x = src_x % g->width + pos / max_height * g->width; - *y = pos % max_height; - } - else /* frames are ordered diagonally */ - { - *x = src_x + frame * g->offset_x; - *y = src_y + frame * g->offset_y; - } - - *x = *x * TILESIZE_VAR / g->tile_size; - *y = *y * TILESIZE_VAR / g->tile_size; + getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y, + get_backside); } void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y) @@ -1298,6 +1751,14 @@ void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize) MarkTileDirty(x / tilesize, y / tilesize); } +void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame, + int tilesize) +{ + DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize, + graphic, frame, tilesize); + MarkTileDirty(x / tilesize, y / tilesize); +} + void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame, int tilesize) { @@ -1308,6 +1769,16 @@ void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame, BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y); } +void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic, + int frame, int tilesize) +{ + Bitmap *src_bitmap; + int src_x, src_y; + + getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y); + BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y); +} + void DrawMiniGraphic(int x, int y, int graphic) { DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic); @@ -1323,9 +1794,9 @@ void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic) BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y); } -inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy, - int graphic, int frame, - int cut_mode, int mask_mode) +static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy, + int graphic, int frame, + int cut_mode, int mask_mode) { Bitmap *src_bitmap; int src_x, src_y; @@ -1333,35 +1804,35 @@ inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy, int width = TILEX, height = TILEY; int cx = 0, cy = 0; - if (dx || dy) /* shifted graphic */ + if (dx || dy) // shifted graphic { - if (x < BX1) /* object enters playfield from the left */ + if (x < BX1) // object enters playfield from the left { x = BX1; width = dx; cx = TILEX - dx; dx = 0; } - else if (x > BX2) /* object enters playfield from the right */ + else if (x > BX2) // object enters playfield from the right { x = BX2; width = -dx; dx = TILEX + dx; } - else if (x == BX1 && dx < 0) /* object leaves playfield to the left */ + else if (x == BX1 && dx < 0) // object leaves playfield to the left { width += dx; cx = -dx; dx = 0; } - else if (x == BX2 && dx > 0) /* object leaves playfield to the right */ + else if (x == BX2 && dx > 0) // object leaves playfield to the right width -= dx; - else if (dx) /* general horizontal movement */ + else if (dx) // general horizontal movement MarkTileDirty(x + SIGN(dx), y); - if (y < BY1) /* object enters playfield from the top */ + if (y < BY1) // object enters playfield from the top { - if (cut_mode == CUT_BELOW) /* object completely above top border */ + if (cut_mode == CUT_BELOW) // object completely above top border return; y = BY1; @@ -1369,13 +1840,13 @@ inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy, cy = TILEY - dy; dy = 0; } - else if (y > BY2) /* object enters playfield from the bottom */ + else if (y > BY2) // object enters playfield from the bottom { y = BY2; height = -dy; dy = TILEY + dy; } - else if (y == BY1 && dy < 0) /* object leaves playfield to the top */ + else if (y == BY1 && dy < 0) // object leaves playfield to the top { height += dy; cy = -dy; @@ -1383,17 +1854,17 @@ inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy, } else if (dy > 0 && cut_mode == CUT_ABOVE) { - if (y == BY2) /* object completely above bottom border */ + if (y == BY2) // object completely above bottom border return; height = dy; cy = TILEY - dy; dy = TILEY; MarkTileDirty(x, y + 1); - } /* object leaves playfield to the bottom */ + } // object leaves playfield to the bottom else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW)) height -= dy; - else if (dy) /* general vertical movement */ + else if (dy) // general vertical movement MarkTileDirty(x, y + SIGN(dy)); } @@ -1434,9 +1905,9 @@ inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy, } } -inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy, - int graphic, int frame, - int cut_mode, int mask_mode) +static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy, + int graphic, int frame, + int cut_mode, int mask_mode) { Bitmap *src_bitmap; int src_x, src_y; @@ -1447,22 +1918,22 @@ inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy, int x2 = x + SIGN(dx); int y2 = y + SIGN(dy); - /* movement with two-tile animations must be sync'ed with movement position, - not with current GfxFrame (which can be higher when using slow movement) */ + // movement with two-tile animations must be sync'ed with movement position, + // not with current GfxFrame (which can be higher when using slow movement) int anim_pos = (dx ? ABS(dx) : ABS(dy)); int anim_frames = graphic_info[graphic].anim_frames; - /* (we also need anim_delay here for movement animations with less frames) */ + // (we also need anim_delay here for movement animations with less frames) int anim_delay = graphic_info[graphic].anim_delay; int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE; - boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */ - boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */ + boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling! + boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling! - /* re-calculate animation frame for two-tile movement animation */ + // re-calculate animation frame for two-tile movement animation frame = getGraphicAnimationFrame(graphic, sync_frame); - /* check if movement start graphic inside screen area and should be drawn */ + // check if movement start graphic inside screen area and should be drawn if (draw_start_tile && IN_SCR_FIELD(x1, y1)) { getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE); @@ -1480,7 +1951,7 @@ inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy, MarkTileDirty(x1, y1); } - /* check if movement end graphic inside screen area and should be drawn */ + // check if movement end graphic inside screen area and should be drawn if (draw_end_tile && IN_SCR_FIELD(x2, y2)) { getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE); @@ -1510,14 +1981,14 @@ static void DrawGraphicShifted(int x, int y, int dx, int dy, return; } - if (graphic_info[graphic].double_movement) /* EM style movement images */ + if (graphic_info[graphic].double_movement) // EM style movement images DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode); else DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode); } -void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic, - int frame, int cut_mode) +static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, + int graphic, int frame, int cut_mode) { DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING); } @@ -1536,14 +2007,14 @@ void DrawScreenElementExt(int x, int y, int dx, int dy, int element, graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]); frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]); - /* do not use double (EM style) movement graphic when not moving */ + // do not use double (EM style) movement graphic when not moving if (graphic_info[graphic].double_movement && !dx && !dy) { graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]); frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]); } } - else /* border element */ + else // border element { graphic = el2img(element); frame = getGraphicAnimationFrame(graphic, -1); @@ -1610,7 +2081,7 @@ void DrawLevelFieldThruMask(int x, int y) DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING); } -/* !!! implementation of quicksand is totally broken !!! */ +// !!! implementation of quicksand is totally broken !!! #define IS_CRUMBLED_TILE(x, y, e) \ (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \ !IS_MOVING(x, y) || \ @@ -1625,6 +2096,9 @@ static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy, int width, height, cx, cy; int sx = SCREENX(x), sy = SCREENY(y); int crumbled_border_size = graphic_info[graphic].border_size; + int crumbled_tile_size = graphic_info[graphic].tile_size; + int crumbled_border_size_var = + crumbled_border_size * TILESIZE_VAR / crumbled_tile_size; int i; getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y); @@ -1638,22 +2112,22 @@ static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy, int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) : BorderElement); - /* check if neighbour field is of same crumble type */ + // check if neighbour field is of same crumble type boolean same = (IS_CRUMBLED_TILE(xx, yy, element) && graphic_info[graphic].class == graphic_info[el_act2crm(element, ACTION_DEFAULT)].class); - /* return if check prevents inner corner */ + // return if check prevents inner corner if (same == (dxx == dx && dyy == dy)) return; } - /* if we reach this point, we have an inner corner */ + // if we reach this point, we have an inner corner getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y); - width = crumbled_border_size * TILESIZE_VAR / TILESIZE; - height = crumbled_border_size * TILESIZE_VAR / TILESIZE; + width = crumbled_border_size_var; + height = crumbled_border_size_var; cx = (dx > 0 ? TILESIZE_VAR - width : 0); cy = (dy > 0 ? TILESIZE_VAR - height : 0); @@ -1669,13 +2143,15 @@ static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame, int width, height, bx, by, cx, cy; int sx = SCREENX(x), sy = SCREENY(y); int crumbled_border_size = graphic_info[graphic].border_size; - int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE; + int crumbled_tile_size = graphic_info[graphic].tile_size; + int crumbled_border_size_var = + crumbled_border_size * TILESIZE_VAR / crumbled_tile_size; int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var; int i; getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y); - /* draw simple, sloppy, non-corner-accurate crumbled border */ + // draw simple, sloppy, non-corner-accurate crumbled border width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR); height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR); @@ -1686,12 +2162,12 @@ static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy); - /* (remaining middle border part must be at least as big as corner part) */ + // (remaining middle border part must be at least as big as corner part) if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) || - crumbled_border_size >= TILESIZE / 3) + crumbled_border_size_var >= TILESIZE_VAR / 3) return; - /* correct corners of crumbled border, if needed */ + // correct corners of crumbled border, if needed for (i = -1; i <= 1; i += 2) { @@ -1700,12 +2176,12 @@ static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame, int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) : BorderElement); - /* check if neighbour field is of same crumble type */ + // check if neighbour field is of same crumble type if (IS_CRUMBLED_TILE(xx, yy, element) && graphic_info[graphic].class == graphic_info[el_act2crm(element, ACTION_DEFAULT)].class) { - /* no crumbled corner, but continued crumbled border */ + // no crumbled corner, but continued crumbled border int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0); int c2 = (i == 1 ? crumbled_border_pos_var : 0); @@ -1756,12 +2232,12 @@ static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame) element = TILE_GFX_ELEMENT(x, y); - /* crumble field itself */ - if (IS_CRUMBLED_TILE(x, y, element)) + if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself { if (!IN_SCR_FIELD(sx, sy)) return; + // crumble field borders towards direct neighbour fields for (i = 0; i < 4; i++) { int xx = x + xy[i][0]; @@ -1770,7 +2246,7 @@ static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame) element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) : BorderElement); - /* check if neighbour field is of same crumble type */ + // check if neighbour field is of same crumble type if (IS_CRUMBLED_TILE(xx, yy, element) && graphic_info[graphic].class == graphic_info[el_act2crm(element, ACTION_DEFAULT)].class) @@ -1779,6 +2255,7 @@ static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame) DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i); } + // crumble inner field corners towards corner neighbour fields if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) && graphic_info[graphic].anim_frames == 2) { @@ -1793,8 +2270,9 @@ static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame) MarkTileDirty(sx, sy); } - else /* center field not crumbled -- crumble neighbour fields */ + else // center field is not crumbled -- crumble neighbour fields { + // crumble field borders of direct neighbour fields for (i = 0; i < 4; i++) { int xx = x + xy[i][0]; @@ -1806,7 +2284,9 @@ static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame) !IN_SCR_FIELD(sxx, syy)) continue; - if (Feld[xx][yy] == EL_ELEMENT_SNAPPING) + // do not crumble fields that are being digged or snapped + if (Feld[xx][yy] == EL_EMPTY || + Feld[xx][yy] == EL_ELEMENT_SNAPPING) continue; element = TILE_GFX_ELEMENT(xx, yy); @@ -1820,6 +2300,37 @@ static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame) MarkTileDirty(sxx, syy); } + + // crumble inner field corners of corner neighbour fields + for (i = 0; i < 4; i++) + { + int dx = (i & 1 ? +1 : -1); + int dy = (i & 2 ? +1 : -1); + int xx = x + dx; + int yy = y + dy; + int sxx = sx + dx; + int syy = sy + dy; + + if (!IN_LEV_FIELD(xx, yy) || + !IN_SCR_FIELD(sxx, syy)) + continue; + + if (Feld[xx][yy] == EL_ELEMENT_SNAPPING) + continue; + + element = TILE_GFX_ELEMENT(xx, yy); + + if (!IS_CRUMBLED_TILE(xx, yy, element)) + continue; + + graphic = el_act2crm(element, ACTION_DEFAULT); + + if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) && + graphic_info[graphic].anim_frames == 2) + DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic); + + MarkTileDirty(sxx, syy); + } } } @@ -1869,6 +2380,7 @@ void DrawLevelFieldCrumbledNeighbours(int x, int y) }; int i; + // crumble direct neighbour fields (required for field borders) for (i = 0; i < 4; i++) { int xx = x + xy[i][0]; @@ -1884,6 +2396,30 @@ void DrawLevelFieldCrumbledNeighbours(int x, int y) DrawLevelField(xx, yy); } + + // crumble corner neighbour fields (required for inner field corners) + for (i = 0; i < 4; i++) + { + int dx = (i & 1 ? +1 : -1); + int dy = (i & 2 ? +1 : -1); + int xx = x + dx; + int yy = y + dy; + int sxx = sx + dx; + int syy = sy + dy; + + if (!IN_LEV_FIELD(xx, yy) || + !IN_SCR_FIELD(sxx, syy) || + !GFX_CRUMBLED(Feld[xx][yy]) || + IS_MOVING(xx, yy)) + continue; + + int element = TILE_GFX_ELEMENT(xx, yy); + int graphic = el_act2crm(element, ACTION_DEFAULT); + + if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) && + graphic_info[graphic].anim_frames == 2) + DrawLevelField(xx, yy); + } } static int getBorderElement(int x, int y) @@ -1985,6 +2521,10 @@ void DrawScreenField(int x, int y) int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0); DrawLevelElementThruMask(newlx, newly, EL_ACID); + + // prevent target field from being drawn again (but without masking) + // (this would happen if target field is scanned after moving element) + Stop[newlx][newly] = TRUE; } } else if (IS_BLOCKED(lx, ly)) @@ -2042,22 +2582,100 @@ void DrawLevelField(int x, int y) if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy))) DrawScreenField(SCREENX(newx), SCREENY(newy)); } - else if (IS_BLOCKED(x, y)) + else if (IS_BLOCKED(x, y)) + { + int oldx, oldy; + + Blocked2Moving(x, y, &oldx, &oldy); + if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy))) + DrawScreenField(SCREENX(oldx), SCREENY(oldy)); + } +} + +static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize, + int (*el2img_function)(int), boolean masked, + int element_bits_draw) +{ + int element_base = map_mm_wall_element(element); + int element_bits = (IS_DF_WALL(element) ? + element - EL_DF_WALL_START : + IS_MM_WALL(element) ? + element - EL_MM_WALL_START : EL_EMPTY) & 0x000f; + int graphic = el2img_function(element_base); + int tilesize_draw = tilesize / 2; + Bitmap *src_bitmap; + int src_x, src_y; + int i; + + getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y); + + for (i = 0; i < 4; i++) + { + int dst_draw_x = dst_x + (i % 2) * tilesize_draw; + int dst_draw_y = dst_y + (i / 2) * tilesize_draw; + + if (!(element_bits_draw & (1 << i))) + continue; + + if (element_bits & (1 << i)) + { + if (masked) + BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, + tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y); + else + BlitBitmap(src_bitmap, drawto, src_x, src_y, + tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y); + } + else + { + if (!masked) + ClearRectangle(drawto, dst_draw_x, dst_draw_y, + tilesize_draw, tilesize_draw); + } + } +} + +void DrawSizedWallParts_MM(int x, int y, int element, int tilesize, + boolean masked, int element_bits_draw) +{ + DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize, + element, tilesize, el2edimg, masked, element_bits_draw); +} + +static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize, + int (*el2img_function)(int)) +{ + DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE, + 0x000f); +} + +static void DrawSizedElementExt(int x, int y, int element, int tilesize, + boolean masked) +{ + if (IS_MM_WALL(element)) + { + DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize, + element, tilesize, el2edimg, masked, 0x000f); + } + else { - int oldx, oldy; + int graphic = el2edimg(element); - Blocked2Moving(x, y, &oldx, &oldy); - if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy))) - DrawScreenField(SCREENX(oldx), SCREENY(oldy)); + if (masked) + DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize); + else + DrawSizedGraphic(x, y, graphic, 0, tilesize); } } void DrawSizedElement(int x, int y, int element, int tilesize) { - int graphic; + DrawSizedElementExt(x, y, element, tilesize, FALSE); +} - graphic = el2edimg(element); - DrawSizedGraphic(x, y, graphic, 0, tilesize); +void DrawSizedElementThruMask(int x, int y, int element, int tilesize) +{ + DrawSizedElementExt(x, y, element, tilesize, TRUE); } void DrawMiniElement(int x, int y, int element) @@ -2093,9 +2711,9 @@ void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y) DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y))); } -void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty, - int x, int y, int xsize, int ysize, - int tile_width, int tile_height) +static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty, + int x, int y, int xsize, int ysize, + int tile_width, int tile_height) { Bitmap *src_bitmap; int src_x, src_y; @@ -2132,8 +2750,9 @@ void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty, dst_x, dst_y); } -void DrawEnvelopeBackground(int graphic, int startx, int starty, - int x, int y, int xsize, int ysize, int font_nr) +static void DrawEnvelopeBackground(int graphic, int startx, int starty, + int x, int y, int xsize, int ysize, + int font_nr) { int font_width = getFontWidth(font_nr); int font_height = getFontHeight(font_nr); @@ -2142,7 +2761,7 @@ void DrawEnvelopeBackground(int graphic, int startx, int starty, font_width, font_height); } -void AnimateEnvelope(int envelope_nr, int anim_mode, int action) +static void AnimateEnvelope(int envelope_nr, int anim_mode, int action) { int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr; Bitmap *src_bitmap = graphic_info[graphic].bitmap; @@ -2151,7 +2770,7 @@ void AnimateEnvelope(int envelope_nr, int anim_mode, int action) boolean no_delay = (tape.warp_forward); unsigned int anim_delay = 0; int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay); - int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2; + int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2); int font_nr = FONT_ENVELOPE_1 + envelope_nr; int font_width = getFontWidth(font_nr); int font_height = getFontHeight(font_nr); @@ -2178,11 +2797,11 @@ void AnimateEnvelope(int envelope_nr, int anim_mode, int action) int sy = SY + (SYSIZE - ysize * font_height) / 2; int xx, yy; - SetDrawtoField(DRAW_FIELDBUFFER); + SetDrawtoField(DRAW_TO_FIELDBUFFER); BlitScreenToBitmap(backbuffer); - SetDrawtoField(DRAW_BACKBUFFER); + SetDrawtoField(DRAW_TO_BACKBUFFER); for (yy = 0; yy < ysize; yy++) for (xx = 0; xx < xsize; xx++) @@ -2199,6 +2818,8 @@ void AnimateEnvelope(int envelope_nr, int anim_mode, int action) SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame); } + + ClearAutoRepeatKeyEvents(); } void ShowEnvelope(int envelope_nr) @@ -2215,7 +2836,7 @@ void ShowEnvelope(int envelope_nr) int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL: anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode); - game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */ + game.envelope_active = TRUE; // needed for RedrawPlayfield() events PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE); @@ -2239,7 +2860,7 @@ void ShowEnvelope(int envelope_nr) game.envelope_active = FALSE; - SetDrawtoField(DRAW_FIELDBUFFER); + SetDrawtoField(DRAW_TO_FIELDBUFFER); redraw_mask |= REDRAW_FIELD; BackToFront(); @@ -2312,9 +2933,8 @@ static void setRequestPosition(int *x, int *y, boolean add_border_size) setRequestPositionExt(x, y, request.width, request.height, add_border_size); } -void DrawEnvelopeRequest(char *text) +static void DrawEnvelopeRequest(char *text) { - int last_game_status = game_status; /* save current game status */ char *text_final = text; char *text_door_style = NULL; int graphic = IMG_BACKGROUND_REQUEST; @@ -2381,26 +3001,26 @@ void DrawEnvelopeRequest(char *text) x, y, x_steps, y_steps, tile_size, tile_size); - /* force DOOR font inside door area */ - game_status = GAME_MODE_PSEUDO_DOOR; + // force DOOR font inside door area + SetFontStatus(GAME_MODE_PSEUDO_DOOR); DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr, line_length, -1, max_lines, line_spacing, mask_mode, request.autowrap, request.centered, FALSE); - game_status = last_game_status; /* restore current game status */ + ResetFontStatus(); for (i = 0; i < NUM_TOOL_BUTTONS; i++) RedrawGadget(tool_gadget[i]); // store readily prepared envelope request for later use when animating - BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0); + BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0); if (text_door_style) free(text_door_style); } -void AnimateEnvelopeRequest(int anim_mode, int action) +static void AnimateEnvelopeRequest(int anim_mode, int action) { int graphic = IMG_BACKGROUND_REQUEST; boolean draw_masked = graphic_info[graphic].draw_masked; @@ -2409,7 +3029,7 @@ void AnimateEnvelopeRequest(int anim_mode, int action) boolean ffwd_delay = (tape.playing && tape.fast_forward); boolean no_delay = (tape.warp_forward); int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal); - int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2; + int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2); unsigned int anim_delay = 0; int tile_size = MAX(request.step_offset, 1); @@ -2455,7 +3075,7 @@ void AnimateEnvelopeRequest(int anim_mode, int action) setRequestPosition(&src_x, &src_y, FALSE); setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE); - BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0); + BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0); for (yy = 0; yy < 2; yy++) { @@ -2469,30 +3089,31 @@ void AnimateEnvelopeRequest(int anim_mode, int action) int yy_size = (yy ? tile_size : ysize_size_top); if (draw_masked) - BlitBitmapMasked(bitmap_db_cross, backbuffer, + BlitBitmapMasked(bitmap_db_store_2, backbuffer, src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy); else - BlitBitmap(bitmap_db_cross, backbuffer, + BlitBitmap(bitmap_db_store_2, backbuffer, src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy); } } redraw_mask |= REDRAW_FIELD; - DoAnimation(); BackToFront(); SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame); } + + ClearAutoRepeatKeyEvents(); } -void ShowEnvelopeRequest(char *text, unsigned int req_state, int action) +static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action) { int graphic = IMG_BACKGROUND_REQUEST; int sound_opening = SND_REQUEST_OPENING; int sound_closing = SND_REQUEST_CLOSING; - int anim_mode_1 = request.anim_mode; /* (higher priority) */ - int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */ + int anim_mode_1 = request.anim_mode; // (higher priority) + int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority) int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2); int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL: anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode); @@ -2500,13 +3121,13 @@ void ShowEnvelopeRequest(char *text, unsigned int req_state, int action) if (game_status == GAME_MODE_PLAYING) BlitScreenToBitmap(backbuffer); - SetDrawtoField(DRAW_BACKBUFFER); + SetDrawtoField(DRAW_TO_BACKBUFFER); // SetDrawBackgroundMask(REDRAW_NONE); if (action == ACTION_OPENING) { - BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0); + BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0); if (req_state & REQ_ASK) { @@ -2526,12 +3147,9 @@ void ShowEnvelopeRequest(char *text, unsigned int req_state, int action) } DrawEnvelopeRequest(text); - - if (game_status != GAME_MODE_MAIN) - InitAnimation(); } - game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */ + game.envelope_active = TRUE; // needed for RedrawPlayfield() events if (action == ACTION_OPENING) { @@ -2556,36 +3174,36 @@ void ShowEnvelopeRequest(char *text, unsigned int req_state, int action) game.envelope_active = FALSE; if (action == ACTION_CLOSING) - { - if (game_status != GAME_MODE_MAIN) - StopAnimation(); - - BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0); - } + BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0); // SetDrawBackgroundMask(last_draw_background_mask); redraw_mask |= REDRAW_FIELD; - if (game_status == GAME_MODE_MAIN) - DoAnimation(); - BackToFront(); if (action == ACTION_CLOSING && game_status == GAME_MODE_PLAYING && level.game_engine_type == GAME_ENGINE_TYPE_RND) - SetDrawtoField(DRAW_FIELDBUFFER); + SetDrawtoField(DRAW_TO_FIELDBUFFER); } -void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize) +static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize) { - Bitmap *src_bitmap; - int src_x, src_y; - int graphic = el2preimg(element); + if (IS_MM_WALL(element)) + { + DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg); + } + else + { + Bitmap *src_bitmap; + int src_x, src_y; + int graphic = el2preimg(element); - getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y); - BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y); + getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y); + BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, + dst_x, dst_y); + } } void DrawLevel(int draw_background_mask) @@ -2627,7 +3245,7 @@ void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y) redraw_mask |= REDRAW_FIELD; } -static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y) +static void DrawPreviewLevelPlayfield(int from_x, int from_y) { boolean show_level_border = (BorderElement != EL_EMPTY); int level_xsize = lev_fieldx + (show_level_border ? 2 : 0); @@ -2692,9 +3310,8 @@ static int getMaxTextLength(struct TextPosInfo *pos, int font_nr) return max_text_width / font_width; } -static void DrawPreviewLevelLabelExt(int mode) +static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos) { - struct TextPosInfo *pos = &menu.main.text.level_info_2; char label_text[MAX_OUTPUT_LINESIZE + 1]; int max_len_label_text; int font_nr = pos->font; @@ -2737,6 +3354,19 @@ static void DrawPreviewLevelLabelExt(int mode) redraw_mask |= REDRAW_FIELD; } +static void DrawPreviewLevelLabel(int mode) +{ + DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2); +} + +static void DrawPreviewLevelInfo(int mode) +{ + if (mode == MICROLABEL_LEVEL_NAME) + DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name); + else if (mode == MICROLABEL_LEVEL_AUTHOR) + DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author); +} + static void DrawPreviewLevelExt(boolean restart) { static unsigned int scroll_delay = 0; @@ -2747,7 +3377,6 @@ static void DrawPreviewLevelExt(boolean restart) boolean show_level_border = (BorderElement != EL_EMPTY); int level_xsize = lev_fieldx + (show_level_border ? 2 : 0); int level_ysize = lev_fieldy + (show_level_border ? 2 : 0); - int last_game_status = game_status; /* save current game status */ if (restart) { @@ -2769,10 +3398,13 @@ static void DrawPreviewLevelExt(boolean restart) label_state = 1; label_counter = 0; - DrawPreviewLevelPlayfieldExt(from_x, from_y); - DrawPreviewLevelLabelExt(label_state); + DrawPreviewLevelPlayfield(from_x, from_y); + DrawPreviewLevelLabel(label_state); + + DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME); + DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR); - /* initialize delay counters */ + // initialize delay counters DelayReached(&scroll_delay, 0); DelayReached(&label_delay, 0); @@ -2793,12 +3425,10 @@ static void DrawPreviewLevelExt(boolean restart) DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align); } - game_status = last_game_status; /* restore current game status */ - return; } - /* scroll preview level, if needed */ + // scroll preview level, if needed if (preview.anim_mode != ANIM_NONE && (level_xsize > preview.xsize || level_ysize > preview.ysize) && DelayReached(&scroll_delay, scroll_delay_value)) @@ -2851,11 +3481,11 @@ static void DrawPreviewLevelExt(boolean restart) break; } - DrawPreviewLevelPlayfieldExt(from_x, from_y); + DrawPreviewLevelPlayfield(from_x, from_y); } - /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */ - /* redraw micro level label, if needed */ + // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! + // redraw micro level label, if needed if (!strEqual(level.name, NAMELESS_LEVEL_NAME) && !strEqual(level.author, ANONYMOUS_NAME) && !strEqual(level.author, leveldir_current->name) && @@ -2892,25 +3522,200 @@ static void DrawPreviewLevelExt(boolean restart) label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ? MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY); - DrawPreviewLevelLabelExt(label_state); + DrawPreviewLevelLabel(label_state); + } +} + +void DrawPreviewPlayers(void) +{ + if (game_status != GAME_MODE_MAIN) + return; + + // do not draw preview players if level preview redefined, but players aren't + if (preview.redefined && !menu.main.preview_players.redefined) + return; + + boolean player_found[MAX_PLAYERS]; + int num_players = 0; + int i, x, y; + + for (i = 0; i < MAX_PLAYERS; i++) + player_found[i] = FALSE; + + // check which players can be found in the level (simple approach) + for (x = 0; x < lev_fieldx; x++) + { + for (y = 0; y < lev_fieldy; y++) + { + int element = level.field[x][y]; + + if (ELEM_IS_PLAYER(element)) + { + int player_nr = GET_PLAYER_NR(element); + + player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1); + + if (!player_found[player_nr]) + num_players++; + + player_found[player_nr] = TRUE; + } + } } - game_status = last_game_status; /* restore current game status */ + struct TextPosInfo *pos = &menu.main.preview_players; + int tile_size = pos->tile_size; + int border_size = pos->border_size; + int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size); + int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0); + int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw); + int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw); + int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size; + int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size; + int all_players_width = (num_players - 1) * player_xoffset + tile_size; + int all_players_height = (num_players - 1) * player_yoffset + tile_size; + int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align); + int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign); + int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align); + int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign); + + // clear area in which the players will be drawn + ClearRectangleOnBackground(drawto, max_xpos, max_ypos, + max_players_width, max_players_height); + + if (!network.enabled && !setup.team_mode) + return; + + // only draw players if level is suited for team mode + if (num_players < 2) + return; + + // draw all players that were found in the level + for (i = 0; i < MAX_PLAYERS; i++) + { + if (player_found[i]) + { + int graphic = el2img(EL_PLAYER_1 + i); + + DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size); + + xpos += player_xoffset; + ypos += player_yoffset; + } + } } -void DrawPreviewLevelInitial() +void DrawPreviewLevelInitial(void) { DrawPreviewLevelExt(TRUE); + DrawPreviewPlayers(); } -void DrawPreviewLevelAnimation() +void DrawPreviewLevelAnimation(void) { DrawPreviewLevelExt(FALSE); } -inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y, - int graphic, int sync_frame, - int mask_mode) +static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size, + int border_size, int font_nr) +{ + int graphic = el2img(EL_PLAYER_1 + player_nr); + int font_height = getFontHeight(font_nr); + int player_height = MAX(tile_size, font_height); + int xoffset_text = tile_size + border_size; + int yoffset_text = (player_height - font_height) / 2; + int yoffset_graphic = (player_height - tile_size) / 2; + char *player_name = getNetworkPlayerName(player_nr + 1); + + DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0, + tile_size); + DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr); +} + +static void DrawNetworkPlayersExt(boolean force) +{ + if (game_status != GAME_MODE_MAIN) + return; + + if (!network.connected && !force) + return; + + // do not draw network players if level preview redefined, but players aren't + if (preview.redefined && !menu.main.network_players.redefined) + return; + + int num_players = 0; + int i; + + for (i = 0; i < MAX_PLAYERS; i++) + if (stored_player[i].connected_network) + num_players++; + + struct TextPosInfo *pos = &menu.main.network_players; + int tile_size = pos->tile_size; + int border_size = pos->border_size; + int xoffset_text = tile_size + border_size; + int font_nr = pos->font; + int font_width = getFontWidth(font_nr); + int font_height = getFontHeight(font_nr); + int player_height = MAX(tile_size, font_height); + int player_yoffset = player_height + border_size; + int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width; + int max_players_height = MAX_PLAYERS * player_yoffset - border_size; + int all_players_height = num_players * player_yoffset - border_size; + int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align); + int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign); + int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign); + + ClearRectangleOnBackground(drawto, max_xpos, max_ypos, + max_players_width, max_players_height); + + // first draw local network player ... + for (i = 0; i < MAX_PLAYERS; i++) + { + if (stored_player[i].connected_network && + stored_player[i].connected_locally) + { + char *player_name = getNetworkPlayerName(i + 1); + int player_width = xoffset_text + getTextWidth(player_name, font_nr); + int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align); + + DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr); + + ypos += player_yoffset; + } + } + + // ... then draw all other network players + for (i = 0; i < MAX_PLAYERS; i++) + { + if (stored_player[i].connected_network && + !stored_player[i].connected_locally) + { + char *player_name = getNetworkPlayerName(i + 1); + int player_width = xoffset_text + getTextWidth(player_name, font_nr); + int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align); + + DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr); + + ypos += player_yoffset; + } + } +} + +void DrawNetworkPlayers(void) +{ + DrawNetworkPlayersExt(FALSE); +} + +void ClearNetworkPlayers(void) +{ + DrawNetworkPlayersExt(TRUE); +} + +static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y, + int graphic, int sync_frame, + int mask_mode) { int frame = getGraphicAnimationFrame(graphic, sync_frame); @@ -2931,7 +3736,7 @@ void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y, DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame); } -inline static void DrawGraphicAnimation(int x, int y, int graphic) +static void DrawGraphicAnimation(int x, int y, int graphic) { int lx = LEVELX(x), ly = LEVELY(y); @@ -3012,14 +3817,14 @@ static int getPlayerGraphic(struct PlayerInfo *player, int move_dir) { if (player->use_murphy) { - /* this works only because currently only one player can be "murphy" ... */ + // this works only because currently only one player can be "murphy" ... static int last_horizontal_dir = MV_LEFT; int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir); if (move_dir == MV_LEFT || move_dir == MV_RIGHT) last_horizontal_dir = move_dir; - if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */ + if (graphic == IMG_SP_MURPHY) // undefined => use special graphic { int direction = (player->is_snapping ? move_dir : last_horizontal_dir); @@ -3045,87 +3850,122 @@ static boolean equalGraphics(int graphic1, int graphic2) g1->anim_mode == g2->anim_mode); } -void DrawAllPlayers() +#define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1 + +enum { - int i; + DRAW_PLAYER_STAGE_INIT = 0, + DRAW_PLAYER_STAGE_LAST_FIELD, + DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER, +#if DRAW_PLAYER_OVER_PUSHED_ELEMENT + DRAW_PLAYER_STAGE_ELEMENT_PUSHED, + DRAW_PLAYER_STAGE_PLAYER, +#else + DRAW_PLAYER_STAGE_PLAYER, + DRAW_PLAYER_STAGE_ELEMENT_PUSHED, +#endif + DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER, + DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER, - for (i = 0; i < MAX_PLAYERS; i++) - if (stored_player[i].active) - DrawPlayer(&stored_player[i]); -} + NUM_DRAW_PLAYER_STAGES +}; -void DrawPlayerField(int x, int y) +static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage) { - if (!IS_PLAYER(x, y)) + static int static_last_player_graphic[MAX_PLAYERS]; + static int static_last_player_frame[MAX_PLAYERS]; + static boolean static_player_is_opaque[MAX_PLAYERS]; + static boolean draw_player[MAX_PLAYERS]; + int pnr = player->index_nr; + + if (drawing_stage == DRAW_PLAYER_STAGE_INIT) + { + static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir); + static_last_player_frame[pnr] = player->Frame; + static_player_is_opaque[pnr] = FALSE; + + draw_player[pnr] = TRUE; + } + + if (!draw_player[pnr]) return; - DrawPlayer(PLAYERINFO(x, y)); -} +#if DEBUG + if (!IN_LEV_FIELD(player->jx, player->jy)) + { + printf("DrawPlayerField(): x = %d, y = %d\n", player->jx, player->jy); + printf("DrawPlayerField(): This should never happen!\n"); -#define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1 + draw_player[pnr] = FALSE; + + return; + } +#endif + + int last_player_graphic = static_last_player_graphic[pnr]; + int last_player_frame = static_last_player_frame[pnr]; + boolean player_is_opaque = static_player_is_opaque[pnr]; -void DrawPlayer(struct PlayerInfo *player) -{ int jx = player->jx; int jy = player->jy; - int move_dir = player->MovDir; + int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir); int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0); int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0); int last_jx = (player->is_moving ? jx - dx : jx); int last_jy = (player->is_moving ? jy - dy : jy); int next_jx = jx + dx; int next_jy = jy + dy; - boolean player_is_moving = (player->MovPos ? TRUE : FALSE); - boolean player_is_opaque = FALSE; - int sx = SCREENX(jx), sy = SCREENY(jy); - int sxx = 0, syy = 0; - int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy]; - int graphic; - int action = ACTION_DEFAULT; - int last_player_graphic = getPlayerGraphic(player, move_dir); - int last_player_frame = player->Frame; - int frame = 0; + boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE); + int sx = SCREENX(jx); + int sy = SCREENY(jy); + int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0); + int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0); + int element = Feld[jx][jy]; + int last_element = Feld[last_jx][last_jy]; + int action = (player->is_pushing ? ACTION_PUSHING : + player->is_digging ? ACTION_DIGGING : + player->is_collecting ? ACTION_COLLECTING : + player->is_moving ? ACTION_MOVING : + player->is_snapping ? ACTION_SNAPPING : + player->is_dropping ? ACTION_DROPPING : + player->is_waiting ? player->action_waiting : + ACTION_DEFAULT); + + if (drawing_stage == DRAW_PLAYER_STAGE_INIT) + { + // ------------------------------------------------------------------------ + // initialize drawing the player + // ------------------------------------------------------------------------ - /* GfxElement[][] is set to the element the player is digging or collecting; - remove also for off-screen player if the player is not moving anymore */ - if (IN_LEV_FIELD(jx, jy) && !player_is_moving) - GfxElement[jx][jy] = EL_UNDEFINED; + draw_player[pnr] = FALSE; - if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy))) - return; + // GfxElement[][] is set to the element the player is digging or collecting; + // remove also for off-screen player if the player is not moving anymore + if (IN_LEV_FIELD(jx, jy) && !player_is_moving) + GfxElement[jx][jy] = EL_UNDEFINED; -#if DEBUG - if (!IN_LEV_FIELD(jx, jy)) - { - printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy); - printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy); - printf("DrawPlayerField(): This should never happen!\n"); - return; - } -#endif + if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy))) + return; - if (element == EL_EXPLOSION) - return; + if (element == EL_EXPLOSION) + return; - action = (player->is_pushing ? ACTION_PUSHING : - player->is_digging ? ACTION_DIGGING : - player->is_collecting ? ACTION_COLLECTING : - player->is_moving ? ACTION_MOVING : - player->is_snapping ? ACTION_SNAPPING : - player->is_dropping ? ACTION_DROPPING : - player->is_waiting ? player->action_waiting : ACTION_DEFAULT); + InitPlayerGfxAnimation(player, action, move_dir); - if (player->is_waiting) - move_dir = player->dir_waiting; + draw_player[pnr] = TRUE; + } + else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD) + { + // ------------------------------------------------------------------------ + // draw things in the field the player is leaving, if needed + // ------------------------------------------------------------------------ - InitPlayerGfxAnimation(player, action, move_dir); + if (!IN_SCR_FIELD(sx, sy)) + draw_player[pnr] = FALSE; - /* ----------------------------------------------------------------------- */ - /* draw things in the field the player is leaving, if needed */ - /* ----------------------------------------------------------------------- */ + if (!player->is_moving) + return; - if (player->is_moving) - { if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element)) { DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]); @@ -3141,35 +3981,32 @@ void DrawPlayer(struct PlayerInfo *player) last_element == EL_EM_DYNAMITE_ACTIVE || last_element == EL_SP_DISK_RED_ACTIVE) DrawDynamite(last_jx, last_jy); -#if 0 - /* !!! this is not enough to prevent flickering of players which are - moving next to each others without a free tile between them -- this - can only be solved by drawing all players layer by layer (first the - background, then the foreground etc.) !!! => TODO */ - else if (!IS_PLAYER(last_jx, last_jy)) - DrawLevelField(last_jx, last_jy); -#else else DrawLevelField(last_jx, last_jy); -#endif if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy))) DrawLevelElement(next_jx, next_jy, EL_EMPTY); } + else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER) + { + // ------------------------------------------------------------------------ + // draw things behind the player, if needed + // ------------------------------------------------------------------------ - if (!IN_SCR_FIELD(sx, sy)) - return; + if (Back[jx][jy]) + { + DrawLevelElement(jx, jy, Back[jx][jy]); - /* ----------------------------------------------------------------------- */ - /* draw things behind the player, if needed */ - /* ----------------------------------------------------------------------- */ + return; + } + + if (IS_ACTIVE_BOMB(element)) + { + DrawLevelElement(jx, jy, EL_EMPTY); + + return; + } - if (Back[jx][jy]) - DrawLevelElement(jx, jy, Back[jx][jy]); - else if (IS_ACTIVE_BOMB(element)) - DrawLevelElement(jx, jy, EL_EMPTY); - else - { if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED) { int old_element = GfxElement[jx][jy]; @@ -3182,14 +4019,14 @@ void DrawPlayer(struct PlayerInfo *player) DrawGraphic(sx, sy, old_graphic, frame); if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER) - player_is_opaque = TRUE; + static_player_is_opaque[pnr] = TRUE; } else { GfxElement[jx][jy] = EL_UNDEFINED; - /* make sure that pushed elements are drawn with correct frame rate */ - graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir); + // make sure that pushed elements are drawn with correct frame rate + int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir); if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic)) GfxFrame[jx][jy] = player->StepFrame; @@ -3197,84 +4034,31 @@ void DrawPlayer(struct PlayerInfo *player) DrawLevelField(jx, jy); } } - -#if !DRAW_PLAYER_OVER_PUSHED_ELEMENT - /* ----------------------------------------------------------------------- */ - /* draw player himself */ - /* ----------------------------------------------------------------------- */ - - graphic = getPlayerGraphic(player, move_dir); - - /* in the case of changed player action or direction, prevent the current - animation frame from being restarted for identical animations */ - if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic)) - player->Frame = last_player_frame; - - frame = getGraphicAnimationFrame(graphic, player->Frame); - - if (player->GfxPos) - { - if (move_dir == MV_LEFT || move_dir == MV_RIGHT) - sxx = player->GfxPos; - else - syy = player->GfxPos; - } - - if (player_is_opaque) - DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING); - else - DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING); - - if (SHIELD_ON(player)) - { - int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE : - IMG_SHIELD_NORMAL_ACTIVE); - int frame = getGraphicAnimationFrame(graphic, -1); - - DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING); - } -#endif - -#if DRAW_PLAYER_OVER_PUSHED_ELEMENT - if (player->GfxPos) + else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED) { - if (move_dir == MV_LEFT || move_dir == MV_RIGHT) - sxx = player->GfxPos; - else - syy = player->GfxPos; - } -#endif + // ------------------------------------------------------------------------ + // draw things the player is pushing, if needed + // ------------------------------------------------------------------------ - /* ----------------------------------------------------------------------- */ - /* draw things the player is pushing, if needed */ - /* ----------------------------------------------------------------------- */ + if (!player->is_pushing || !player->is_moving) + return; - if (player->is_pushing && player->is_moving) - { - int px = SCREENX(jx), py = SCREENY(jy); - int pxx = (TILEX - ABS(sxx)) * dx; - int pyy = (TILEY - ABS(syy)) * dy; int gfx_frame = GfxFrame[jx][jy]; - int graphic; - int sync_frame; - int frame; - - if (!IS_MOVING(jx, jy)) /* push movement already finished */ + if (!IS_MOVING(jx, jy)) // push movement already finished { element = Feld[next_jx][next_jy]; gfx_frame = GfxFrame[next_jx][next_jy]; } - graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir); - - sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame); - frame = getGraphicAnimationFrame(graphic, sync_frame); + int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir); + int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame); + int frame = getGraphicAnimationFrame(graphic, sync_frame); - /* draw background element under pushed element (like the Sokoban field) */ + // draw background element under pushed element (like the Sokoban field) if (game.use_masked_pushing && IS_MOVING(jx, jy)) { - /* this allows transparent pushing animation over non-black background */ + // this allows transparent pushing animation over non-black background if (Back[jx][jy]) DrawLevelElement(jx, jy, Back[jx][jy]); @@ -3289,118 +4073,147 @@ void DrawPlayer(struct PlayerInfo *player) else if (Back[next_jx][next_jy]) DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]); + int px = SCREENX(jx), py = SCREENY(jy); + int pxx = (TILEX - ABS(sxx)) * dx; + int pyy = (TILEY - ABS(syy)) * dy; + #if 1 - /* do not draw (EM style) pushing animation when pushing is finished */ - /* (two-tile animations usually do not contain start and end frame) */ + // do not draw (EM style) pushing animation when pushing is finished + // (two-tile animations usually do not contain start and end frame) if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy)) DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]); else DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING); #else - /* masked drawing is needed for EMC style (double) movement graphics */ - /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */ + // masked drawing is needed for EMC style (double) movement graphics + // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING); #endif } + else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER) + { + // ------------------------------------------------------------------------ + // draw player himself + // ------------------------------------------------------------------------ -#if DRAW_PLAYER_OVER_PUSHED_ELEMENT - /* ----------------------------------------------------------------------- */ - /* draw player himself */ - /* ----------------------------------------------------------------------- */ - - graphic = getPlayerGraphic(player, move_dir); + int graphic = getPlayerGraphic(player, move_dir); - /* in the case of changed player action or direction, prevent the current - animation frame from being restarted for identical animations */ - if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic)) - player->Frame = last_player_frame; + // in the case of changed player action or direction, prevent the current + // animation frame from being restarted for identical animations + if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic)) + player->Frame = last_player_frame; - frame = getGraphicAnimationFrame(graphic, player->Frame); + int frame = getGraphicAnimationFrame(graphic, player->Frame); - if (player->GfxPos) - { - if (move_dir == MV_LEFT || move_dir == MV_RIGHT) - sxx = player->GfxPos; + if (player_is_opaque) + DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING); else - syy = player->GfxPos; - } + DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING); - if (player_is_opaque) - DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING); - else - DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING); + if (SHIELD_ON(player)) + { + graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE : + IMG_SHIELD_NORMAL_ACTIVE); + frame = getGraphicAnimationFrame(graphic, -1); - if (SHIELD_ON(player)) + DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING); + } + } + else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER) { - int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE : - IMG_SHIELD_NORMAL_ACTIVE); - int frame = getGraphicAnimationFrame(graphic, -1); + // ------------------------------------------------------------------------ + // draw things in front of player (active dynamite or dynabombs) + // ------------------------------------------------------------------------ - DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING); - } -#endif + if (IS_ACTIVE_BOMB(element)) + { + int graphic = el2img(element); + int frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]); - /* ----------------------------------------------------------------------- */ - /* draw things in front of player (active dynamite or dynabombs) */ - /* ----------------------------------------------------------------------- */ + if (game.emulation == EMU_SUPAPLEX) + DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame); + else + DrawGraphicThruMask(sx, sy, graphic, frame); + } - if (IS_ACTIVE_BOMB(element)) + if (player_is_moving && last_element == EL_EXPLOSION) + { + int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ? + GfxElement[last_jx][last_jy] : EL_EMPTY); + int graphic = el_act2img(element, ACTION_EXPLODING); + int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2); + int phase = ExplodePhase[last_jx][last_jy] - 1; + int frame = getGraphicAnimationFrame(graphic, phase - delay); + + if (phase >= delay) + DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame); + } + } + else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER) { - graphic = el2img(element); - frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]); + // ------------------------------------------------------------------------ + // draw elements the player is just walking/passing through/under + // ------------------------------------------------------------------------ - if (game.emulation == EMU_SUPAPLEX) - DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame); - else - DrawGraphicThruMask(sx, sy, graphic, frame); - } + if (player_is_moving) + { + // handle the field the player is leaving ... + if (IS_ACCESSIBLE_INSIDE(last_element)) + DrawLevelField(last_jx, last_jy); + else if (IS_ACCESSIBLE_UNDER(last_element)) + DrawLevelFieldThruMask(last_jx, last_jy); + } - if (player_is_moving && last_element == EL_EXPLOSION) - { - int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ? - GfxElement[last_jx][last_jy] : EL_EMPTY); - int graphic = el_act2img(element, ACTION_EXPLODING); - int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2); - int phase = ExplodePhase[last_jx][last_jy] - 1; - int frame = getGraphicAnimationFrame(graphic, phase - delay); + // do not redraw accessible elements if the player is just pushing them + if (!player_is_moving || !player->is_pushing) + { + // ... and the field the player is entering + if (IS_ACCESSIBLE_INSIDE(element)) + DrawLevelField(jx, jy); + else if (IS_ACCESSIBLE_UNDER(element)) + DrawLevelFieldThruMask(jx, jy); + } - if (phase >= delay) - DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame); + MarkTileDirty(sx, sy); } +} + +void DrawPlayer(struct PlayerInfo *player) +{ + int i; + + for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++) + DrawPlayerExt(player, i); +} - /* ----------------------------------------------------------------------- */ - /* draw elements the player is just walking/passing through/under */ - /* ----------------------------------------------------------------------- */ +void DrawAllPlayers(void) +{ + int i, j; - if (player_is_moving) - { - /* handle the field the player is leaving ... */ - if (IS_ACCESSIBLE_INSIDE(last_element)) - DrawLevelField(last_jx, last_jy); - else if (IS_ACCESSIBLE_UNDER(last_element)) - DrawLevelFieldThruMask(last_jx, last_jy); - } + for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++) + for (j = 0; j < MAX_PLAYERS; j++) + if (stored_player[j].active) + DrawPlayerExt(&stored_player[j], i); +} - /* do not redraw accessible elements if the player is just pushing them */ - if (!player_is_moving || !player->is_pushing) - { - /* ... and the field the player is entering */ - if (IS_ACCESSIBLE_INSIDE(element)) - DrawLevelField(jx, jy); - else if (IS_ACCESSIBLE_UNDER(element)) - DrawLevelFieldThruMask(jx, jy); - } +void DrawPlayerField(int x, int y) +{ + if (!IS_PLAYER(x, y)) + return; - MarkTileDirty(sx, sy); + DrawPlayer(PLAYERINFO(x, y)); } -/* ------------------------------------------------------------------------- */ +// ---------------------------------------------------------------------------- -void WaitForEventToContinue() +void WaitForEventToContinue(void) { boolean still_wait = TRUE; - /* simulate releasing mouse button over last gadget, if still pressed */ + if (program.headless) + return; + + // simulate releasing mouse button over last gadget, if still pressed if (button_status) HandleGadgets(-1, -1, 0); @@ -3410,16 +4223,16 @@ void WaitForEventToContinue() while (still_wait) { - if (PendingEvent()) - { - Event event; - - NextEvent(&event); + Event event; + if (NextValidEvent(&event)) + { switch (event.type) { - case EVENT_BUTTONPRESS: + case EVENT_BUTTONRELEASE: case EVENT_KEYPRESS: + case SDL_CONTROLLERBUTTONDOWN: + case SDL_JOYBUTTONDOWN: still_wait = FALSE; break; @@ -3437,9 +4250,7 @@ void WaitForEventToContinue() still_wait = FALSE; } - DoAnimation(); - - WaitUntilDelayReached(&sync_frame_delay, sync_frame_delay_value); + BackToFront(); } } @@ -3449,13 +4260,19 @@ void WaitForEventToContinue() static int RequestHandleEvents(unsigned int req_state) { - boolean level_solved = (game_status == GAME_MODE_PLAYING && - local_player->LevelSolved_GameEnd); + boolean game_just_ended = (game_status == GAME_MODE_PLAYING && + checkGameEnded()); int width = request.width; int height = request.height; int sx, sy; int result; + // when showing request dialog after game ended, deactivate game panel + if (game_just_ended) + game.panel.active = FALSE; + + game.request_active = TRUE; + setRequestPosition(&sx, &sy, FALSE); button_status = MB_RELEASED; @@ -3465,18 +4282,20 @@ static int RequestHandleEvents(unsigned int req_state) while (result < 0) { - if (level_solved) + if (game_just_ended) { - SetDrawtoField(DRAW_FIELDBUFFER); + // the MM game engine does not use a special (scrollable) field buffer + if (level.game_engine_type != GAME_ENGINE_TYPE_MM) + SetDrawtoField(DRAW_TO_FIELDBUFFER); HandleGameActions(); - SetDrawtoField(DRAW_BACKBUFFER); + SetDrawtoField(DRAW_TO_BACKBUFFER); if (global.use_envelope_request) { - /* copy current state of request area to middle of playfield area */ - BlitBitmap(bitmap_db_cross, drawto, sx, sy, width, height, sx, sy); + // copy current state of request area to middle of playfield area + BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy); } } @@ -3514,7 +4333,7 @@ static int RequestHandleEvents(unsigned int req_state) button_status = MB_RELEASED; } - /* this sets 'request_gadget_id' */ + // this sets 'request_gadget_id' HandleGadgets(mx, my, button_status); switch (request_gadget_id) @@ -3549,8 +4368,22 @@ static int RequestHandleEvents(unsigned int req_state) break; } + case SDL_WINDOWEVENT: + HandleWindowEvent((WindowEvent *) &event); + break; + + case SDL_APP_WILLENTERBACKGROUND: + case SDL_APP_DIDENTERBACKGROUND: + case SDL_APP_WILLENTERFOREGROUND: + case SDL_APP_DIDENTERFOREGROUND: + HandlePauseResumeEvent((PauseResumeEvent *) &event); + break; + case EVENT_KEYPRESS: - switch (GetEventKey((KeyEvent *)&event, TRUE)) + { + Key key = GetEventKey((KeyEvent *)&event, TRUE); + + switch (key) { case KSYM_space: if (req_state & REQ_CONFIRM) @@ -3558,31 +4391,136 @@ static int RequestHandleEvents(unsigned int req_state) break; case KSYM_Return: -#if defined(TARGET_SDL2) + case KSYM_y: + case KSYM_Y: + case KSYM_Select: case KSYM_Menu: +#if defined(KSYM_Rewind) + case KSYM_Rewind: // for Amazon Fire TV remote #endif result = 1; break; case KSYM_Escape: -#if defined(TARGET_SDL2) + case KSYM_n: + case KSYM_N: case KSYM_Back: +#if defined(KSYM_FastForward) + case KSYM_FastForward: // for Amazon Fire TV remote #endif result = 0; break; default: + HandleKeysDebug(key, KEY_PRESSED); break; } if (req_state & REQ_PLAYER) - result = 0; + { + int old_player_nr = setup.network_player_nr; + + if (result != -1) + result = old_player_nr + 1; + + switch (key) + { + case KSYM_space: + result = old_player_nr + 1; + break; + + case KSYM_Up: + case KSYM_1: + result = 1; + break; + + case KSYM_Right: + case KSYM_2: + result = 2; + break; + + case KSYM_Down: + case KSYM_3: + result = 3; + break; + + case KSYM_Left: + case KSYM_4: + result = 4; + break; + + default: + break; + } + } + break; + } case EVENT_KEYRELEASE: ClearPlayerAction(); break; + case SDL_CONTROLLERBUTTONDOWN: + switch (event.cbutton.button) + { + case SDL_CONTROLLER_BUTTON_A: + case SDL_CONTROLLER_BUTTON_X: + case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: + case SDL_CONTROLLER_BUTTON_LEFTSTICK: + result = 1; + break; + + case SDL_CONTROLLER_BUTTON_B: + case SDL_CONTROLLER_BUTTON_Y: + case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: + case SDL_CONTROLLER_BUTTON_RIGHTSTICK: + case SDL_CONTROLLER_BUTTON_BACK: + result = 0; + break; + } + + if (req_state & REQ_PLAYER) + { + int old_player_nr = setup.network_player_nr; + + if (result != -1) + result = old_player_nr + 1; + + switch (event.cbutton.button) + { + case SDL_CONTROLLER_BUTTON_DPAD_UP: + case SDL_CONTROLLER_BUTTON_Y: + result = 1; + break; + + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: + case SDL_CONTROLLER_BUTTON_B: + result = 2; + break; + + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: + case SDL_CONTROLLER_BUTTON_A: + result = 3; + break; + + case SDL_CONTROLLER_BUTTON_DPAD_LEFT: + case SDL_CONTROLLER_BUTTON_X: + result = 4; + break; + + default: + break; + } + } + + break; + + case SDL_CONTROLLERBUTTONUP: + HandleJoystickEvent(&event); + ClearPlayerAction(); + break; + default: HandleOtherEvents(&event); break; @@ -3598,39 +4536,50 @@ static int RequestHandleEvents(unsigned int req_state) else if (joy & JOY_BUTTON_2) result = 0; } - - if (level_solved) + else if (AnyJoystick()) { - if (global.use_envelope_request) + int joy = AnyJoystick(); + + if (req_state & REQ_PLAYER) { - /* copy back current state of pressed buttons inside request area */ - BlitBitmap(drawto, bitmap_db_cross, sx, sy, width, height, sx, sy); + if (joy & JOY_UP) + result = 1; + else if (joy & JOY_RIGHT) + result = 2; + else if (joy & JOY_DOWN) + result = 3; + else if (joy & JOY_LEFT) + result = 4; } } - else + + if (game_just_ended) { - DoAnimation(); + if (global.use_envelope_request) + { + // copy back current state of pressed buttons inside request area + BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy); + } } BackToFront(); - - WaitUntilDelayReached(&sync_frame_delay, sync_frame_delay_value); } + game.request_active = FALSE; + return result; } static boolean RequestDoor(char *text, unsigned int req_state) { unsigned int old_door_state; - int last_game_status = game_status; /* save current game status */ int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN; int font_nr = FONT_TEXT_2; char *text_ptr; int result; int ty; - if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN) + if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN) { max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN; font_nr = FONT_TEXT_1; @@ -3639,36 +4588,35 @@ static boolean RequestDoor(char *text, unsigned int req_state) if (game_status == GAME_MODE_PLAYING) BlitScreenToBitmap(backbuffer); - /* disable deactivated drawing when quick-loading level tape recording */ + // disable deactivated drawing when quick-loading level tape recording if (tape.playing && tape.deactivate_display) TapeDeactivateDisplayOff(TRUE); SetMouseCursor(CURSOR_DEFAULT); -#if defined(NETWORK_AVALIABLE) - /* pause network game while waiting for request to answer */ - if (options.network && + // pause network game while waiting for request to answer + if (network.enabled && game_status == GAME_MODE_PLAYING && + !game.all_players_gone && req_state & REQUEST_WAIT_FOR_INPUT) SendToServer_PausePlaying(); -#endif old_door_state = GetDoorState(); - /* simulate releasing mouse button over last gadget, if still pressed */ + // simulate releasing mouse button over last gadget, if still pressed if (button_status) HandleGadgets(-1, -1, 0); UnmapAllGadgets(); - /* draw released gadget before proceeding */ + // draw released gadget before proceeding // BackToFront(); if (old_door_state & DOOR_OPEN_1) { CloseDoor(DOOR_CLOSE_1); - /* save old door content */ + // save old door content BlitBitmap(bitmap_db_door_1, bitmap_db_door_1, 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0); } @@ -3676,13 +4624,13 @@ static boolean RequestDoor(char *text, unsigned int req_state) SetDoorBackgroundImage(IMG_BACKGROUND_DOOR); SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1); - /* clear door drawing field */ + // clear door drawing field DrawBackground(DX, DY, DXSIZE, DYSIZE); - /* force DOOR font inside door area */ - game_status = GAME_MODE_PSEUDO_DOOR; + // force DOOR font inside door area + SetFontStatus(GAME_MODE_PSEUDO_DOOR); - /* write text for request */ + // write text for request for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++) { char text_line[max_request_line_len + 1]; @@ -3720,7 +4668,7 @@ static boolean RequestDoor(char *text, unsigned int req_state) // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0); } - game_status = last_game_status; /* restore current game status */ + ResetFontStatus(); if (req_state & REQ_ASK) { @@ -3739,7 +4687,7 @@ static boolean RequestDoor(char *text, unsigned int req_state) MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]); } - /* copy request gadgets to door backbuffer */ + // copy request gadgets to door backbuffer BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0); OpenDoor(DOOR_OPEN_1); @@ -3759,17 +4707,11 @@ static boolean RequestDoor(char *text, unsigned int req_state) return FALSE; } - if (game_status != GAME_MODE_MAIN) - InitAnimation(); - SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1); // ---------- handle request buttons ---------- result = RequestHandleEvents(req_state); - if (game_status != GAME_MODE_MAIN) - StopAnimation(); - UnmapToolButtons(); if (!(req_state & REQ_STAY_OPEN)) @@ -3793,15 +4735,14 @@ static boolean RequestDoor(char *text, unsigned int req_state) SetDrawBackgroundMask(REDRAW_FIELD); } -#if defined(NETWORK_AVALIABLE) - /* continue network game after request */ - if (options.network && + // continue network game after request + if (network.enabled && game_status == GAME_MODE_PLAYING && + !game.all_players_gone && req_state & REQUEST_WAIT_FOR_INPUT) SendToServer_ContinuePlaying(); -#endif - /* restore deactivated drawing when quick-loading level tape recording */ + // restore deactivated drawing when quick-loading level tape recording if (tape.playing && tape.deactivate_display) TapeDeactivateDisplayOn(); @@ -3815,21 +4756,20 @@ static boolean RequestEnvelope(char *text, unsigned int req_state) if (game_status == GAME_MODE_PLAYING) BlitScreenToBitmap(backbuffer); - /* disable deactivated drawing when quick-loading level tape recording */ + // disable deactivated drawing when quick-loading level tape recording if (tape.playing && tape.deactivate_display) TapeDeactivateDisplayOff(TRUE); SetMouseCursor(CURSOR_DEFAULT); -#if defined(NETWORK_AVALIABLE) - /* pause network game while waiting for request to answer */ - if (options.network && + // pause network game while waiting for request to answer + if (network.enabled && game_status == GAME_MODE_PLAYING && + !game.all_players_gone && req_state & REQUEST_WAIT_FOR_INPUT) SendToServer_PausePlaying(); -#endif - /* simulate releasing mouse button over last gadget, if still pressed */ + // simulate releasing mouse button over last gadget, if still pressed if (button_status) HandleGadgets(-1, -1, 0); @@ -3839,7 +4779,7 @@ static boolean RequestEnvelope(char *text, unsigned int req_state) // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR); // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1); - /* clear door drawing field */ + // clear door drawing field // DrawBackground(DX, DY, DXSIZE, DYSIZE); ShowEnvelopeRequest(text, req_state, ACTION_OPENING); @@ -3864,9 +4804,6 @@ static boolean RequestEnvelope(char *text, unsigned int req_state) // ---------- handle request buttons ---------- result = RequestHandleEvents(req_state); - if (game_status != GAME_MODE_MAIN) - StopAnimation(); - UnmapToolButtons(); ShowEnvelopeRequest(text, req_state, ACTION_CLOSING); @@ -3883,15 +4820,14 @@ static boolean RequestEnvelope(char *text, unsigned int req_state) SetDrawBackgroundMask(REDRAW_FIELD); } -#if defined(NETWORK_AVALIABLE) - /* continue network game after request */ - if (options.network && + // continue network game after request + if (network.enabled && game_status == GAME_MODE_PLAYING && + !game.all_players_gone && req_state & REQUEST_WAIT_FOR_INPUT) SendToServer_ContinuePlaying(); -#endif - /* restore deactivated drawing when quick-loading level tape recording */ + // restore deactivated drawing when quick-loading level tape recording if (tape.playing && tape.deactivate_display) TapeDeactivateDisplayOn(); @@ -3900,10 +4836,19 @@ static boolean RequestEnvelope(char *text, unsigned int req_state) boolean Request(char *text, unsigned int req_state) { + boolean overlay_enabled = GetOverlayEnabled(); + boolean result; + + SetOverlayEnabled(FALSE); + if (global.use_envelope_request) - return RequestEnvelope(text, req_state); + result = RequestEnvelope(text, req_state); else - return RequestDoor(text, req_state); + result = RequestDoor(text, req_state); + + SetOverlayEnabled(overlay_enabled); + + return result; } static int compareDoorPartOrderInfo(const void *object1, const void *object2) @@ -3920,7 +4865,7 @@ static int compareDoorPartOrderInfo(const void *object1, const void *object2) return compare_result; } -void InitGraphicCompatibilityInfo_Doors() +void InitGraphicCompatibilityInfo_Doors(void) { struct { @@ -3930,8 +4875,8 @@ void InitGraphicCompatibilityInfo_Doors() } doors[] = { - { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 }, - { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 }, + { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 }, + { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 }, { -1, -1, -1, NULL } }; @@ -3954,7 +4899,7 @@ void InitGraphicCompatibilityInfo_Doors() struct Rect *door_rect = &door_rect_list[door_index]; boolean door_gfx_redefined = FALSE; - /* check if any door part graphic definitions have been redefined */ + // check if any door part graphic definitions have been redefined for (j = 0; door_part_controls[j].door_token != -1; j++) { @@ -3965,7 +4910,7 @@ void InitGraphicCompatibilityInfo_Doors() door_gfx_redefined = TRUE; } - /* check for old-style door graphic/animation modifications */ + // check for old-style door graphic/animation modifications if (!door_gfx_redefined) { @@ -3981,7 +4926,7 @@ void InitGraphicCompatibilityInfo_Doors() struct GraphicInfo *g_part_2 = &graphic_info[part_2]; int num_door_steps, num_panel_steps; - /* remove door part graphics other than the two default wings */ + // remove door part graphics other than the two default wings for (j = 0; door_part_controls[j].door_token != -1; j++) { @@ -3993,7 +4938,7 @@ void InitGraphicCompatibilityInfo_Doors() g->bitmap = NULL; } - /* set graphics and screen positions of the default wings */ + // set graphics and screen positions of the default wings g_part_1->width = door_rect->width; g_part_1->height = door_rect->height; @@ -4025,18 +4970,18 @@ void InitGraphicCompatibilityInfo_Doors() door->part_2.y += door_rect->height - door->height; } - /* set animation delays for the default wings and panels */ + // set animation delays for the default wings and panels door->part_1.step_delay = door->step_delay; door->part_2.step_delay = door->step_delay; door->panel.step_delay = door->step_delay; - /* set animation draw order for the default wings */ + // set animation draw order for the default wings - door->part_1.sort_priority = 2; /* draw left wing over ... */ - door->part_2.sort_priority = 1; /* ... right wing */ + door->part_1.sort_priority = 2; // draw left wing over ... + door->part_2.sort_priority = 1; // ... right wing - /* set animation draw offset for the default wings */ + // set animation draw offset for the default wings if (door->anim_mode & ANIM_HORIZONTAL) { @@ -4057,7 +5002,7 @@ void InitGraphicCompatibilityInfo_Doors() num_door_steps = g_part_1->height / door->step_offset; } - /* set animation draw offset for the default panels */ + // set animation draw offset for the default panels if (door->step_offset > 1) { @@ -4077,7 +5022,7 @@ void InitGraphicCompatibilityInfo_Doors() } } -void InitDoors() +void InitDoors(void) { int i; @@ -4086,7 +5031,7 @@ void InitDoors() struct DoorPartControlInfo *dpc = &door_part_controls[i]; struct DoorPartOrderInfo *dpo = &door_part_order[i]; - /* initialize "start_step_opening" and "start_step_closing", if needed */ + // initialize "start_step_opening" and "start_step_closing", if needed if (dpc->pos->start_step_opening == 0 && dpc->pos->start_step_closing == 0) { @@ -4094,12 +5039,12 @@ void InitDoors() dpc->pos->start_step_closing = dpc->pos->start_step; } - /* fill structure for door part draw order (sorted below) */ + // fill structure for door part draw order (sorted below) dpo->nr = i; dpo->sort_priority = dpc->pos->sort_priority; } - /* sort door part controls according to sort_priority and graphic number */ + // sort door part controls according to sort_priority and graphic number qsort(door_part_order, MAX_DOOR_PARTS, sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo); } @@ -4142,7 +5087,7 @@ unsigned int CloseDoor(unsigned int door_state) return MoveDoor(door_state); } -unsigned int GetDoorState() +unsigned int GetDoorState(void) { return MoveDoor(DOOR_GET_STATE); } @@ -4152,7 +5097,7 @@ unsigned int SetDoorState(unsigned int door_state) return MoveDoor(door_state | DOOR_SET_STATE); } -int euclid(int a, int b) +static int euclid(int a, int b) { return (b ? euclid(b, a % b) : a); } @@ -4164,7 +5109,7 @@ unsigned int MoveDoor(unsigned int door_state) { DX, DY, DXSIZE, DYSIZE }, { VX, VY, VXSIZE, VYSIZE } }; - static int door1 = DOOR_OPEN_1; + static int door1 = DOOR_CLOSE_1; static int door2 = DOOR_CLOSE_2; unsigned int door_delay = 0; unsigned int door_delay_value; @@ -4201,7 +5146,7 @@ unsigned int MoveDoor(unsigned int door_state) door_state &= ~DOOR_CLOSE_ALL; } - if (game_status == GAME_MODE_EDITOR) + if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM)) door_state |= DOOR_NO_DELAY; if (door_state & DOOR_ACTION) @@ -4291,11 +5236,25 @@ unsigned int MoveDoor(unsigned int door_state) } else { - /* opening door sound has priority over simultaneously closing door */ + // opening door sound has priority over simultaneously closing door if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2)) + { PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE); + + if (door_state & DOOR_OPEN_1) + PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE); + if (door_state & DOOR_OPEN_2) + PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE); + } else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2)) + { PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE); + + if (door_state & DOOR_CLOSE_1) + PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE); + if (door_state & DOOR_CLOSE_2) + PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE); + } } for (k = start; k < num_move_steps; k++) @@ -4355,7 +5314,8 @@ unsigned int MoveDoor(unsigned int door_state) int sync_frame = kk_door * door_delay_value; int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame); - getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y); + getFixedGraphicSource(dpc->graphic, frame, &bitmap, + &g_src_x, &g_src_y); } // draw door panel @@ -4460,17 +5420,31 @@ unsigned int MoveDoor(unsigned int door_state) { BackToFront(); - if (game_status == GAME_MODE_MAIN) - DoAnimation(); - SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame); current_move_delay += max_step_delay; + + // prevent OS (Windows) from complaining about program not responding + CheckQuitEvent(); } if (door_part_done_all) break; } + + if (!(door_state & DOOR_NO_DELAY)) + { + // wait for specified door action post delay + if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2) + door_delay_value = MAX(door_1.post_delay, door_2.post_delay); + else if (door_state & DOOR_ACTION_1) + door_delay_value = door_1.post_delay; + else if (door_state & DOOR_ACTION_2) + door_delay_value = door_2.post_delay; + + while (!DelayReached(&door_delay, door_delay_value)) + BackToFront(); + } } if (door_state & DOOR_ACTION_1) @@ -4482,10 +5456,12 @@ unsigned int MoveDoor(unsigned int door_state) DrawMaskedBorder(REDRAW_DOOR_1); DrawMaskedBorder(REDRAW_DOOR_2); + ClearAutoRepeatKeyEvents(); + return (door1 | door2); } -static boolean useSpecialEditorDoor() +static boolean useSpecialEditorDoor(void) { int graphic = IMG_GLOBAL_BORDER_EDITOR; boolean redefined = getImageListEntryFromImageID(graphic)->redefined; @@ -4508,7 +5484,7 @@ static boolean useSpecialEditorDoor() return TRUE; } -void DrawSpecialEditorDoor() +void DrawSpecialEditorDoor(void) { struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION]; int top_border_width = gfx1->width; @@ -4522,7 +5498,7 @@ void DrawSpecialEditorDoor() if (!useSpecialEditorDoor()) return; - /* draw bigger level editor toolbox window */ + // draw bigger level editor toolbox window BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y, top_border_width, top_border_height, ex, ey - top_border_height); BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy, @@ -4531,7 +5507,7 @@ void DrawSpecialEditorDoor() redraw_mask |= REDRAW_ALL; } -void UndrawSpecialEditorDoor() +void UndrawSpecialEditorDoor(void) { struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION]; int top_border_width = gfx1->width; @@ -4546,7 +5522,7 @@ void UndrawSpecialEditorDoor() if (!useSpecialEditorDoor()) return; - /* draw normal tape recorder window */ + // draw normal tape recorder window if (graphic_info[IMG_GLOBAL_BORDER].bitmap) { BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, @@ -4566,7 +5542,7 @@ void UndrawSpecialEditorDoor() } -/* ---------- new tool button stuff ---------------------------------------- */ +// ---------- new tool button stuff ------------------------------------------- static struct { @@ -4577,42 +5553,43 @@ static struct } toolbutton_info[NUM_TOOL_BUTTONS] = { { - IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes, + IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes, TOOL_CTRL_ID_YES, "yes" }, { - IMG_REQUEST_BUTTON_GFX_NO, &request.button.no, + IMG_GFX_REQUEST_BUTTON_NO, &request.button.no, TOOL_CTRL_ID_NO, "no" }, { - IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm, + IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm, TOOL_CTRL_ID_CONFIRM, "confirm" }, { - IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1, + IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1, TOOL_CTRL_ID_PLAYER_1, "player 1" }, { - IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2, + IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2, TOOL_CTRL_ID_PLAYER_2, "player 2" }, { - IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3, + IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3, TOOL_CTRL_ID_PLAYER_3, "player 3" }, { - IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4, + IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4, TOOL_CTRL_ID_PLAYER_4, "player 4" } }; -void CreateToolButtons() +void CreateToolButtons(void) { int i; for (i = 0; i < NUM_TOOL_BUTTONS; i++) { - struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic]; + int graphic = toolbutton_info[i].graphic; + struct GraphicInfo *gfx = &graphic_info[graphic]; struct TextPosInfo *pos = toolbutton_info[i].pos; struct GadgetInfo *gi; Bitmap *deco_bitmap = None; @@ -4624,11 +5601,46 @@ void CreateToolButtons() int gd_y = gfx->src_y; int gd_xp = gfx->src_x + gfx->pressed_xoffset; int gd_yp = gfx->src_y + gfx->pressed_yoffset; + int x = pos->x; + int y = pos->y; int id = i; if (global.use_envelope_request) + { setRequestPosition(&dx, &dy, TRUE); + // check if request buttons are outside of envelope and fix, if needed + if (x < 0 || x + gfx->width > request.width || + y < 0 || y + gfx->height > request.height) + { + if (id == TOOL_CTRL_ID_YES) + { + x = 0; + y = request.height - 2 * request.border_size - gfx->height; + } + else if (id == TOOL_CTRL_ID_NO) + { + x = request.width - 2 * request.border_size - gfx->width; + y = request.height - 2 * request.border_size - gfx->height; + } + else if (id == TOOL_CTRL_ID_CONFIRM) + { + x = (request.width - 2 * request.border_size - gfx->width) / 2; + y = request.height - 2 * request.border_size - gfx->height; + } + else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4) + { + int player_nr = id - TOOL_CTRL_ID_PLAYER_1; + + x = (request.width - 2 * request.border_size - gfx->width) / 2; + y = request.height - 2 * request.border_size - gfx->height * 2; + + x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width; + y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height; + } + } + } + if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4) { int player_nr = id - TOOL_CTRL_ID_PLAYER_1; @@ -4640,9 +5652,10 @@ void CreateToolButtons() } gi = CreateGadget(GDI_CUSTOM_ID, id, + GDI_IMAGE_ID, graphic, GDI_INFO_TEXT, toolbutton_info[i].infotext, - GDI_X, dx + GDI_ACTIVE_POS(pos->x), - GDI_Y, dy + GDI_ACTIVE_POS(pos->y), + GDI_X, dx + x, + GDI_Y, dy + y, GDI_WIDTH, gfx->width, GDI_HEIGHT, gfx->height, GDI_TYPE, GD_TYPE_NORMAL_BUTTON, @@ -4665,7 +5678,7 @@ void CreateToolButtons() } } -void FreeToolButtons() +void FreeToolButtons(void) { int i; @@ -4673,7 +5686,7 @@ void FreeToolButtons() FreeGadget(tool_gadget[i]); } -static void UnmapToolButtons() +static void UnmapToolButtons(void) { int i; @@ -4689,8 +5702,8 @@ static void HandleToolButtons(struct GadgetInfo *gi) static struct Mapping_EM_to_RND_object { int element_em; - boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */ - boolean is_backside; /* backside of moving element */ + boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND + boolean is_backside; // backside of moving element int element_rnd; int action; @@ -4760,7 +5773,7 @@ em_object_mapping_list[] = Xbomb_force_w, FALSE, FALSE, EL_BOMB, -1, MV_BIT_LEFT }, -#endif /* EM_ENGINE_BAD_ROLL */ +#endif // EM_ENGINE_BAD_ROLL { Xstone, TRUE, FALSE, @@ -6613,7 +7626,7 @@ int map_element_RND_to_EM(int element_rnd) { int i; - /* return "Xalpha_quest" for all undefined elements in mapping array */ + // return "Xalpha_quest" for all undefined elements in mapping array for (i = 0; i < NUM_FILE_ELEMENTS; i++) mapping_RND_to_EM[i] = Xalpha_quest; @@ -6642,7 +7655,7 @@ int map_element_EM_to_RND(int element_em) { int i; - /* return "EL_UNKNOWN" for all undefined elements in mapping array */ + // return "EL_UNKNOWN" for all undefined elements in mapping array for (i = 0; i < TILE_MAX; i++) mapping_EM_to_RND[i] = EL_UNKNOWN; @@ -6741,7 +7754,7 @@ int map_direction_EM_to_RND(int direction) int map_element_RND_to_SP(int element_rnd) { - int element_sp = 0x20; /* map unknown elements to yellow "hardware" */ + int element_sp = 0x20; // map unknown elements to yellow "hardware" if (element_rnd >= EL_SP_START && element_rnd <= EL_SP_END) @@ -6785,6 +7798,125 @@ int map_action_SP_to_RND(int action_sp) } } +int map_element_RND_to_MM(int element_rnd) +{ + return (element_rnd >= EL_MM_START_1 && + element_rnd <= EL_MM_END_1 ? + EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 : + + element_rnd >= EL_MM_START_2 && + element_rnd <= EL_MM_END_2 ? + EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 : + + element_rnd >= EL_CHAR_START && + element_rnd <= EL_CHAR_END ? + EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START : + + element_rnd >= EL_MM_RUNTIME_START && + element_rnd <= EL_MM_RUNTIME_END ? + EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START : + + element_rnd >= EL_MM_DUMMY_START && + element_rnd <= EL_MM_DUMMY_END ? + EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START : + + EL_MM_EMPTY_NATIVE); +} + +int map_element_MM_to_RND(int element_mm) +{ + return (element_mm == EL_MM_EMPTY_NATIVE || + element_mm == EL_DF_EMPTY_NATIVE ? + EL_EMPTY : + + element_mm >= EL_MM_START_1_NATIVE && + element_mm <= EL_MM_END_1_NATIVE ? + EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE : + + element_mm >= EL_MM_START_2_NATIVE && + element_mm <= EL_MM_END_2_NATIVE ? + EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE : + + element_mm >= EL_MM_CHAR_START_NATIVE && + element_mm <= EL_MM_CHAR_END_NATIVE ? + EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE : + + element_mm >= EL_MM_RUNTIME_START_NATIVE && + element_mm <= EL_MM_RUNTIME_END_NATIVE ? + EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE : + + element_mm >= EL_MM_DUMMY_START_NATIVE && + element_mm <= EL_MM_DUMMY_END_NATIVE ? + EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE : + + EL_EMPTY); +} + +int map_action_MM_to_RND(int action_mm) +{ + // all MM actions are defined to exactly match their RND counterparts + return action_mm; +} + +int map_sound_MM_to_RND(int sound_mm) +{ + switch (sound_mm) + { + case SND_MM_GAME_LEVELTIME_CHARGING: + return SND_GAME_LEVELTIME_CHARGING; + + case SND_MM_GAME_HEALTH_CHARGING: + return SND_GAME_HEALTH_CHARGING; + + default: + return SND_UNDEFINED; + } +} + +int map_mm_wall_element(int element) +{ + return (element >= EL_MM_STEEL_WALL_START && + element <= EL_MM_STEEL_WALL_END ? + EL_MM_STEEL_WALL : + + element >= EL_MM_WOODEN_WALL_START && + element <= EL_MM_WOODEN_WALL_END ? + EL_MM_WOODEN_WALL : + + element >= EL_MM_ICE_WALL_START && + element <= EL_MM_ICE_WALL_END ? + EL_MM_ICE_WALL : + + element >= EL_MM_AMOEBA_WALL_START && + element <= EL_MM_AMOEBA_WALL_END ? + EL_MM_AMOEBA_WALL : + + element >= EL_DF_STEEL_WALL_START && + element <= EL_DF_STEEL_WALL_END ? + EL_DF_STEEL_WALL : + + element >= EL_DF_WOODEN_WALL_START && + element <= EL_DF_WOODEN_WALL_END ? + EL_DF_WOODEN_WALL : + + element); +} + +int map_mm_wall_element_editor(int element) +{ + switch (element) + { + case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START; + case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START; + case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START; + case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START; + case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START; + case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START; + + default: return element; + } +} + int get_next_element(int element) { switch (element) @@ -6805,21 +7937,26 @@ int get_next_element(int element) } } +int el2img_mm(int element_mm) +{ + return el2img(map_element_MM_to_RND(element_mm)); +} + 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 = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN - /* direction_graphic[][] == graphic[] for undefined direction graphics */ + // direction_graphic[][] == graphic[] for undefined direction graphics return element_info[element].direction_graphic[action][direction]; } 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 = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN - /* direction_graphic[][] == graphic[] for undefined direction graphics */ + // direction_graphic[][] == graphic[] for undefined direction graphics return element_info[element].direction_crumbled[action][direction]; } @@ -7003,9 +8140,9 @@ int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir) return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr); } -boolean getTeamMode_EM() +boolean getTeamMode_EM(void) { - return game.team_mode; + return game.team_mode || network_playing; } int getGameFrameDelay_EM(int native_em_game_frame_delay) @@ -7029,6 +8166,8 @@ unsigned int InitRND(int seed) return InitEngineRandom_EM(seed); else if (level.game_engine_type == GAME_ENGINE_TYPE_SP) return InitEngineRandom_SP(seed); + else if (level.game_engine_type == GAME_ENGINE_TYPE_MM) + return InitEngineRandom_MM(seed); else return InitEngineRandom_RND(seed); } @@ -7036,7 +8175,7 @@ unsigned int InitRND(int seed) static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX]; static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX]; -inline static int get_effective_element_EM(int tile, int frame_em) +static int get_effective_element_EM(int tile, int frame_em) { int element = object_mapping[tile].element_rnd; int action = object_mapping[tile].action; @@ -7057,7 +8196,7 @@ inline static int get_effective_element_EM(int tile, int frame_em) return element; } } - else /* frame_em == 7 */ + else // frame_em == 7 { switch (tile) { @@ -7103,7 +8242,7 @@ inline static int get_effective_element_EM(int tile, int frame_em) } } -inline static boolean check_linear_animation_EM(int tile) +static boolean check_linear_animation_EM(int tile) { switch (tile) { @@ -7139,13 +8278,13 @@ inline static boolean check_linear_animation_EM(int tile) return FALSE; } -inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em, - boolean has_crumbled_graphics, - int crumbled, int sync_frame) +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 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; @@ -7162,6 +8301,7 @@ inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em, &g_em->crumbled_src_x, &g_em->crumbled_src_y); g_em->crumbled_border_size = graphic_info[crumbled].border_size; + g_em->crumbled_tile_size = graphic_info[crumbled].tile_size; g_em->has_crumbled_graphics = TRUE; } @@ -7171,15 +8311,18 @@ inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em, g_em->crumbled_src_x = 0; g_em->crumbled_src_y = 0; g_em->crumbled_border_size = 0; + g_em->crumbled_tile_size = 0; g_em->has_crumbled_graphics = FALSE; } } +#if 0 void ResetGfxAnimation_EM(int x, int y, int tile) { GfxFrame[x][y] = 0; } +#endif void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em, int tile, int frame_em, int x, int y) @@ -7205,12 +8348,12 @@ void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em, action == ACTION_FILLING || action == ACTION_EMPTYING); - /* special case: graphic uses "2nd movement tile" and has defined - 7 frames for movement animation (or less) => use default graphic - for last (8th) frame which ends the movement animation */ + // special case: graphic uses "2nd movement tile" and has defined + // 7 frames for movement animation (or less) => use default graphic + // for last (8th) frame which ends the movement animation if (g->double_movement && g->anim_frames < 8 && frame_em == 7) { - action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */ + action = ACTION_DEFAULT; // (keep action_* unchanged for now) graphic = (direction == MV_NONE ? el_act2img(effective_element, action) : el_act_dir2img(effective_element, action, direction)); @@ -7234,7 +8377,7 @@ void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em, GfxFrame[x][y]++; #if 1 - /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */ + // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! if (g->double_movement && frame_em == 0) GfxFrame[x][y] = 0; #endif @@ -7253,7 +8396,7 @@ void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em, { GfxFrame[x][y]++; - /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */ + // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast if (tile == Xsand_stonesand_quickout_1 || tile == Xsand_stonesand_quickout_2) GfxFrame[x][y]++; @@ -7264,7 +8407,7 @@ void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em, else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY)) sync_frame = GfxFrame[x][y]; else - sync_frame = 0; /* playfield border (pseudo steel) */ + sync_frame = 0; // playfield border (pseudo steel) SetRandomAnimationValue(x, y); @@ -7300,9 +8443,9 @@ void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em, struct GraphicInfo *g = &graphic_info[graphic]; int sync_frame; - /* special case: graphic uses "2nd movement tile" and has defined - 7 frames for movement animation (or less) => use default graphic - for last (8th) frame which ends the movement animation */ + // special case: graphic uses "2nd movement tile" and has defined + // 7 frames for movement animation (or less) => use default graphic + // for last (8th) frame which ends the movement animation if (g->double_movement && g->anim_frames < 8 && frame_em == 7) { effective_action = ACTION_DEFAULT; @@ -7323,7 +8466,7 @@ void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em, else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY)) sync_frame = GfxFrame[x][y]; else - sync_frame = 0; /* playfield border (pseudo steel) */ + sync_frame = 0; // playfield border (pseudo steel) SetRandomAnimationValue(x, y); @@ -7336,8 +8479,8 @@ void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em, getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y, g->double_movement && is_backside); - /* (updating the "crumbled" graphic definitions is probably not really needed, - as animations for crumbled graphics can't be longer than one EMC cycle) */ + // (updating the "crumbled" graphic definitions is probably not really needed, + // as animations for crumbled graphics can't be longer than one EMC cycle) set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled, sync_frame); } @@ -7379,7 +8522,7 @@ void InitGraphicInfo_EM(void) if (graphic_info_em_object[0][0].bitmap == NULL) { - /* EM graphics not yet initialized in em_open_all() */ + // EM graphics not yet initialized in em_open_all() return; } @@ -7387,7 +8530,7 @@ void InitGraphicInfo_EM(void) printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n"); #endif - /* always start with reliable default values */ + // always start with reliable default values for (i = 0; i < TILE_MAX; i++) { object_mapping[i].element_rnd = EL_UNKNOWN; @@ -7396,7 +8539,7 @@ void InitGraphicInfo_EM(void) object_mapping[i].direction = MV_NONE; } - /* always start with reliable default values */ + // always start with reliable default values for (p = 0; p < MAX_PLAYERS; p++) { for (i = 0; i < SPR_MAX; i++) @@ -7497,7 +8640,7 @@ void InitGraphicInfo_EM(void) struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j]; Bitmap *src_bitmap; int src_x, src_y; - /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */ + // ensure to get symmetric 3-frame, 2-delay animations as used in EM boolean special_animation = (action != ACTION_DEFAULT && g->anim_frames == 3 && g->anim_delay == 2 && @@ -7668,7 +8811,7 @@ void InitGraphicInfo_EM(void) i == Xsand_stoneout_2 ? j + 8 : j) + 1; int step = (is_backside ? step_frame : num_steps - step_frame); - if (is_backside) /* tile where movement starts */ + if (is_backside) // tile where movement starts { if (dx < 0 || dy < 0) { @@ -7681,7 +8824,7 @@ void InitGraphicInfo_EM(void) g_em->dst_offset_y = cy * step; } } - else /* tile where movement ends */ + else // tile where movement ends { if (dx < 0 || dy < 0) { @@ -7699,7 +8842,7 @@ void InitGraphicInfo_EM(void) g_em->height = TILEY - cy * step; } - /* create unique graphic identifier to decide if tile must be redrawn */ + // create unique graphic identifier to decide if tile must be redrawn /* bit 31 - 16 (16 bit): EM style graphic bit 15 - 12 ( 4 bit): EM style frame bit 11 - 6 ( 6 bit): graphic width @@ -7709,7 +8852,7 @@ void InitGraphicInfo_EM(void) #if DEBUG_EM_GFX - /* skip check for EMC elements not contained in original EMC artwork */ + // skip check for EMC elements not contained in original EMC artwork if (element == EL_EMC_FAKE_ACID) continue; @@ -7799,7 +8942,7 @@ void InitGraphicInfo_EM(void) direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) : Xspring); - /* no separate animation for "smashed by rock" -- use rock instead */ + // no separate animation for "smashed by rock" -- use rock instead struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j]; struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j]; @@ -7868,7 +9011,7 @@ void InitGraphicInfo_EM(void) #if DEBUG_EM_GFX - /* skip check for EMC elements not contained in original EMC artwork */ + // skip check for EMC elements not contained in original EMC artwork if (element == EL_PLAYER_3 || element == EL_PLAYER_4) continue; @@ -7925,47 +9068,60 @@ void InitGraphicInfo_EM(void) #endif } -void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame, - boolean any_player_moving, - boolean any_player_snapping, - boolean any_player_dropping) +static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame, + boolean any_player_moving, + boolean any_player_snapping, + boolean any_player_dropping) { - static boolean player_was_waiting = TRUE; - if (frame == 0 && !any_player_dropping) { - if (!player_was_waiting) + if (!local_player->was_waiting) { - if (!SaveEngineSnapshotToList()) + if (!CheckSaveEngineSnapshotToList()) return; - player_was_waiting = TRUE; + local_player->was_waiting = TRUE; } } else if (any_player_moving || any_player_snapping || any_player_dropping) { - player_was_waiting = FALSE; + local_player->was_waiting = FALSE; } } -void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting, - boolean murphy_is_dropping) +static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting, + boolean murphy_is_dropping) { - static boolean player_was_waiting = TRUE; - if (murphy_is_waiting) { - if (!player_was_waiting) + if (!local_player->was_waiting) { - if (!SaveEngineSnapshotToList()) + if (!CheckSaveEngineSnapshotToList()) return; - player_was_waiting = TRUE; + local_player->was_waiting = TRUE; } } else { - player_was_waiting = FALSE; + local_player->was_waiting = FALSE; + } +} + +static void CheckSaveEngineSnapshot_MM(boolean element_clicked, + boolean button_released) +{ + if (button_released) + { + if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE) + CheckSaveEngineSnapshotToList(); + } + else if (element_clicked) + { + if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE) + CheckSaveEngineSnapshotToList(); + + game.snapshot.changed_action = TRUE; } } @@ -7985,13 +9141,30 @@ void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame, void CheckSingleStepMode_SP(boolean murphy_is_waiting, boolean murphy_is_dropping) { + boolean murphy_starts_dropping = FALSE; + int i; + + for (i = 0; i < MAX_PLAYERS; i++) + if (stored_player[i].force_dropping) + murphy_starts_dropping = TRUE; + if (tape.single_step && tape.recording && !tape.pausing) - if (murphy_is_waiting) + if (murphy_is_waiting && !murphy_starts_dropping) TapeTogglePause(TAPE_TOGGLE_AUTOMATIC); CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping); } +void CheckSingleStepMode_MM(boolean element_clicked, + boolean button_released) +{ + if (tape.single_step && tape.recording && !tape.pausing) + if (button_released) + TapeTogglePause(TAPE_TOGGLE_AUTOMATIC); + + CheckSaveEngineSnapshot_MM(element_clicked, button_released); +} + void getGraphicSource_SP(struct GraphicInfo_SP *g_sp, int graphic, int sync_frame, int x, int y) { @@ -8025,7 +9198,7 @@ void PlayMenuSoundExt(int sound) PlaySound(sound); } -void PlayMenuSound() +void PlayMenuSound(void) { PlayMenuSoundExt(menu.sound[game_status]); } @@ -8058,7 +9231,7 @@ void PlayMenuSoundIfLoopExt(int sound) PlaySoundLoop(sound); } -void PlayMenuSoundIfLoop() +void PlayMenuSoundIfLoop(void) { PlayMenuSoundIfLoopExt(menu.sound[game_status]); } @@ -8071,35 +9244,65 @@ void PlayMenuMusicExt(int music) if (!setup.sound_music) return; - PlayMusic(music); + if (IS_LOOP_MUSIC(music)) + PlayMusicLoop(music); + else + PlayMusic(music); +} + +void PlayMenuMusic(void) +{ + char *curr_music = getCurrentlyPlayingMusicFilename(); + char *next_music = getMusicInfoEntryFilename(menu.music[game_status]); + + if (!strEqual(curr_music, next_music)) + PlayMenuMusicExt(menu.music[game_status]); +} + +void PlayMenuSoundsAndMusic(void) +{ + PlayMenuSound(); + PlayMenuMusic(); +} + +static void FadeMenuSounds(void) +{ + FadeSounds(); +} + +static void FadeMenuMusic(void) +{ + char *curr_music = getCurrentlyPlayingMusicFilename(); + char *next_music = getMusicInfoEntryFilename(menu.music[game_status]); + + if (!strEqual(curr_music, next_music)) + FadeMusic(); } -void PlayMenuMusic() +void FadeMenuSoundsAndMusic(void) { - PlayMenuMusicExt(menu.music[game_status]); + FadeMenuSounds(); + FadeMenuMusic(); } -void PlaySoundActivating() +void PlaySoundActivating(void) { #if 0 PlaySound(SND_MENU_ITEM_ACTIVATING); #endif } -void PlaySoundSelecting() +void PlaySoundSelecting(void) { #if 0 PlaySound(SND_MENU_ITEM_SELECTING); #endif } -void ToggleFullscreenOrChangeWindowScalingIfNeeded() +void ToggleFullscreenOrChangeWindowScalingIfNeeded(void) { boolean change_fullscreen = (setup.fullscreen != video.fullscreen_enabled); - boolean change_fullscreen_mode = (video.fullscreen_enabled && - !strEqual(setup.fullscreen_mode, - video.fullscreen_mode_current)); boolean change_window_scaling_percent = (!video.fullscreen_enabled && setup.window_scaling_percent != video.window_scaling_percent); @@ -8110,7 +9313,6 @@ void ToggleFullscreenOrChangeWindowScalingIfNeeded() if (!change_window_scaling_percent && !video.fullscreen_available) return; -#if defined(TARGET_SDL2) if (change_window_scaling_percent) { SDLSetWindowScaling(setup.window_scaling_percent); @@ -8121,52 +9323,44 @@ void ToggleFullscreenOrChangeWindowScalingIfNeeded() { SDLSetWindowFullscreen(setup.fullscreen); - /* set setup value according to successfully changed fullscreen mode */ + // set setup value according to successfully changed fullscreen mode setup.fullscreen = video.fullscreen_enabled; return; } -#endif if (change_fullscreen || - change_fullscreen_mode || change_window_scaling_percent) { Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH); - /* save backbuffer content which gets lost when toggling fullscreen mode */ + // save backbuffer content which gets lost when toggling fullscreen mode BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0); - if (change_fullscreen_mode) - { - /* keep fullscreen, but change fullscreen mode (screen resolution) */ - video.fullscreen_enabled = FALSE; /* force new fullscreen mode */ - } - if (change_window_scaling_percent) { - /* keep window mode, but change window scaling */ - video.fullscreen_enabled = TRUE; /* force new window scaling */ + // keep window mode, but change window scaling + video.fullscreen_enabled = TRUE; // force new window scaling } - /* toggle fullscreen */ + // toggle fullscreen ChangeVideoModeIfNeeded(setup.fullscreen); - /* set setup value according to successfully changed fullscreen mode */ + // set setup value according to successfully changed fullscreen mode setup.fullscreen = video.fullscreen_enabled; - /* restore backbuffer content from temporary backbuffer backup bitmap */ + // restore backbuffer content from temporary backbuffer backup bitmap BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0); FreeBitmap(tmp_backbuffer); - /* update visible window/screen */ + // update visible window/screen BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0); } } -void JoinRectangles(int *x, int *y, int *width, int *height, - int x2, int y2, int width2, int height2) +static void JoinRectangles(int *x, int *y, int *width, int *height, + int x2, int y2, int width2, int height2) { // do not join with "off-screen" rectangle if (x2 == -1 || y2 == -1) @@ -8178,8 +9372,120 @@ void JoinRectangles(int *x, int *y, int *width, int *height, *height = MAX(*height, height2); } -void ChangeViewportPropertiesIfNeeded() +void SetAnimStatus(int anim_status_new) +{ + if (anim_status_new == GAME_MODE_MAIN) + anim_status_new = GAME_MODE_PSEUDO_MAINONLY; + else if (anim_status_new == GAME_MODE_SCORES) + anim_status_new = GAME_MODE_PSEUDO_SCORESOLD; + + global.anim_status_next = anim_status_new; + + // directly set screen modes that are entered without fading + if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY && + global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) || + (global.anim_status == GAME_MODE_PSEUDO_TYPENAME && + global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY)) + global.anim_status = global.anim_status_next; +} + +void SetGameStatus(int game_status_new) +{ + if (game_status_new != game_status) + game_status_last_screen = game_status; + + game_status = game_status_new; + + SetAnimStatus(game_status_new); +} + +void SetFontStatus(int game_status_new) +{ + static int last_game_status = -1; + + if (game_status_new != -1) + { + // set game status for font use after storing last game status + last_game_status = game_status; + game_status = game_status_new; + } + else + { + // reset game status after font use from last stored game status + game_status = last_game_status; + } +} + +void ResetFontStatus(void) +{ + SetFontStatus(-1); +} + +void SetLevelSetInfo(char *identifier, int level_nr) { + setString(&levelset.identifier, identifier); + + levelset.level_nr = level_nr; +} + +boolean CheckIfAllViewportsHaveChanged(void) +{ + // if game status has not changed, viewports have not changed either + if (game_status == game_status_last) + return FALSE; + + // check if all viewports have changed with current game status + + struct RectWithBorder *vp_playfield = &viewport.playfield[game_status]; + struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status]; + struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status]; + int new_real_sx = vp_playfield->x; + int new_real_sy = vp_playfield->y; + int new_full_sxsize = vp_playfield->width; + int new_full_sysize = vp_playfield->height; + int new_dx = vp_door_1->x; + int new_dy = vp_door_1->y; + int new_dxsize = vp_door_1->width; + int new_dysize = vp_door_1->height; + int new_vx = vp_door_2->x; + int new_vy = vp_door_2->y; + int new_vxsize = vp_door_2->width; + int new_vysize = vp_door_2->height; + + boolean playfield_viewport_has_changed = + (new_real_sx != REAL_SX || + new_real_sy != REAL_SY || + new_full_sxsize != FULL_SXSIZE || + new_full_sysize != FULL_SYSIZE); + + boolean door_1_viewport_has_changed = + (new_dx != DX || + new_dy != DY || + new_dxsize != DXSIZE || + new_dysize != DYSIZE); + + boolean door_2_viewport_has_changed = + (new_vx != VX || + new_vy != VY || + new_vxsize != VXSIZE || + new_vysize != VYSIZE || + game_status_last == GAME_MODE_EDITOR); + + return (playfield_viewport_has_changed && + door_1_viewport_has_changed && + door_2_viewport_has_changed); +} + +boolean CheckFadeAll(void) +{ + return (CheckIfGlobalBorderHasChanged() || + CheckIfAllViewportsHaveChanged()); +} + +void ChangeViewportPropertiesIfNeeded(void) +{ + boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ? + FALSE : setup.small_game_graphics); int gfx_game_mode = game_status; int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT : game_status); @@ -8190,11 +9496,14 @@ void ChangeViewportPropertiesIfNeeded() struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR]; int new_win_xsize = vp_window->width; int new_win_ysize = vp_window->height; - int border_size = vp_playfield->border_size; - int new_sx = vp_playfield->x + border_size; - int new_sy = vp_playfield->y + border_size; - int new_sxsize = vp_playfield->width - 2 * border_size; - int new_sysize = vp_playfield->height - 2 * border_size; + int border_left = vp_playfield->border_left; + int border_right = vp_playfield->border_right; + int border_top = vp_playfield->border_top; + int border_bottom = vp_playfield->border_bottom; + int new_sx = vp_playfield->x + border_left; + int new_sy = vp_playfield->y + border_top; + int new_sxsize = vp_playfield->width - border_left - border_right; + int new_sysize = vp_playfield->height - border_top - border_bottom; int new_real_sx = vp_playfield->x; int new_real_sy = vp_playfield->y; int new_full_sxsize = vp_playfield->width; @@ -8211,9 +9520,7 @@ void ChangeViewportPropertiesIfNeeded() int new_ey = vp_door_3->y; int new_exsize = vp_door_3->width; int new_eysize = vp_door_3->height; - int new_tilesize_var = - (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size); - + int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size); int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var : gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE); int new_scr_fieldx = new_sxsize / tilesize; @@ -8222,7 +9529,7 @@ void ChangeViewportPropertiesIfNeeded() int new_scr_fieldy_buffers = new_sysize / new_tilesize_var; boolean init_gfx_buffers = FALSE; boolean init_video_buffer = FALSE; - boolean init_gadgets_and_toons = FALSE; + boolean init_gadgets_and_anims = FALSE; boolean init_em_graphics = FALSE; if (new_win_xsize != WIN_XSIZE || @@ -8233,6 +9540,7 @@ void ChangeViewportPropertiesIfNeeded() init_video_buffer = TRUE; init_gfx_buffers = TRUE; + init_gadgets_and_anims = TRUE; // printf("::: video: init_video_buffer, init_gfx_buffers\n"); } @@ -8240,7 +9548,7 @@ void ChangeViewportPropertiesIfNeeded() if (new_scr_fieldx != SCR_FIELDX || new_scr_fieldy != SCR_FIELDY) { - /* this always toggles between MAIN and GAME when using small tile size */ + // this always toggles between MAIN and GAME when using small tile size SCR_FIELDX = new_scr_fieldx; SCR_FIELDY = new_scr_fieldy; @@ -8300,8 +9608,8 @@ void ChangeViewportPropertiesIfNeeded() } // add current and new door 2 area if position or size has changed - if (new_dx != VX || new_dy != VY || - new_dxsize != VXSIZE || new_dysize != VYSIZE) + if (new_vx != VX || new_vy != VY || + new_vxsize != VXSIZE || new_vysize != VYSIZE) { JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE, VX, VY, VXSIZE, VYSIZE); @@ -8347,10 +9655,10 @@ void ChangeViewportPropertiesIfNeeded() TILESIZE_VAR = new_tilesize_var; init_gfx_buffers = TRUE; - init_gadgets_and_toons = TRUE; + init_gadgets_and_anims = TRUE; // printf("::: viewports: init_gfx_buffers\n"); - // printf("::: viewports: init_gadgets_and_toons\n"); + // printf("::: viewports: init_gadgets_and_anims\n"); } if (init_gfx_buffers) @@ -8373,16 +9681,18 @@ void ChangeViewportPropertiesIfNeeded() { // printf("::: init_video_buffer\n"); + FreeAllImageTextures(); // needs old renderer to free the textures + InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen); InitImageTextures(); } - if (init_gadgets_and_toons) + if (init_gadgets_and_anims) { - // printf("::: init_gadgets_and_toons\n"); + // printf("::: init_gadgets_and_anims\n"); InitGadgets(); - InitToons(); + InitGlobalAnimations(); } if (init_em_graphics)