X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Ftools.c;h=b92cf01b747cd3dbd61a90c4d85790f88f2b7001;hb=459e3ba4589c247d389e315d1f501ee099a8a125;hp=f0fcae2929884c3eb2fc751a08b6a82313584c3d;hpb=51309ee0f66e213ee621bbc5389a33124f64519a;p=rocksndiamonds.git diff --git a/src/tools.c b/src/tools.c index f0fcae29..b92cf01b 100644 --- a/src/tools.c +++ b/src/tools.c @@ -165,8 +165,17 @@ static struct DoorPartControlInfo door_part_controls[] = } }; +static struct XY xy_topdown[] = +{ + { 0, -1 }, + { -1, 0 }, + { +1, 0 }, + { 0, +1 } +}; + // forward declaration for internal use +static void MapToolButtons(unsigned int); static void UnmapToolButtons(void); static void HandleToolButtons(struct GadgetInfo *); static int el_act_dir2crm(int, int, int); @@ -391,6 +400,16 @@ int getLevelFromScreenY(int y) return getLevelFromScreenY_RND(y); } +int getScreenFieldSizeX(void) +{ + return (tape.playing ? tape.scr_fieldx : SCR_FIELDX); +} + +int getScreenFieldSizeY(void) +{ + return (tape.playing ? tape.scr_fieldy : SCR_FIELDY); +} + void DumpTile(int x, int y) { int sx = SCREENX(x); @@ -481,7 +500,9 @@ void RedrawPlayfield(void) if (game_status != GAME_MODE_PLAYING) return; - if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + if (level.game_engine_type == GAME_ENGINE_TYPE_BD) + RedrawPlayfield_BD(TRUE); + else if (level.game_engine_type == GAME_ENGINE_TYPE_EM) RedrawPlayfield_EM(TRUE); else if (level.game_engine_type == GAME_ENGINE_TYPE_SP) RedrawPlayfield_SP(TRUE); @@ -502,6 +523,10 @@ static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height, Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status); Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr; + // may happen for "border.draw_masked.*" with undefined "global.border.*" + if (src_bitmap == NULL) + return; + if (x == -1 && y == -1) return; @@ -612,6 +637,10 @@ void DrawMaskedBorderToTarget(int draw_target) gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target; } + // always use global border for PLAYING when restarting the game + if (global.border_status == GAME_MODE_PSEUDO_RESTARTING) + global.border_status = GAME_MODE_PLAYING; + DrawMaskedBorderExt(REDRAW_ALL, draw_target); global.border_status = last_border_status; @@ -619,9 +648,11 @@ void DrawMaskedBorderToTarget(int draw_target) } } -void DrawTileCursor(int draw_target) +void DrawTileCursor(int draw_target, int drawing_stage) { - DrawTileCursor_MM(draw_target, game_status == GAME_MODE_PLAYING); + int tile_cursor_active = (game_status == GAME_MODE_PLAYING); + + DrawTileCursor_MM(draw_target, drawing_stage, tile_cursor_active); } void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy) @@ -639,7 +670,9 @@ void BlitScreenToBitmap_RND(Bitmap *target_bitmap) void BlitScreenToBitmap(Bitmap *target_bitmap) { - if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + if (level.game_engine_type == GAME_ENGINE_TYPE_BD) + BlitScreenToBitmap_BD(target_bitmap); + else 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); @@ -753,7 +786,7 @@ void BackToFront(void) DrawFramesPerSecond(); // remove playfield redraw before potentially merging with doors redraw - if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE)) + if (DrawingDeactivated(REAL_SX, REAL_SY)) redraw_mask &= ~REDRAW_FIELD; // redraw complete window if both playfield and (some) doors need redraw @@ -954,6 +987,10 @@ static void SetScreenStates_BeforeFadingOut(void) static void SetScreenStates_AfterFadingOut(void) { global.border_status = game_status; + + // always use global border for PLAYING when restarting the game + if (global.border_status == GAME_MODE_PSEUDO_RESTARTING) + global.border_status = GAME_MODE_PLAYING; } void FadeIn(int fade_mask) @@ -993,6 +1030,10 @@ void FadeOut(int fade_mask) fade_type_skip != FADE_MODE_SKIP_FADE_OUT) BackToFront(); + // when using BD game engine, cover playfield before fading out after a game + if (game_bd.cover_screen) + CoverScreen_BD(); + SetScreenStates_BeforeFadingOut(); SetTileCursorActive(FALSE); @@ -1081,82 +1122,108 @@ void FadeSkipNextFadeOut(void) FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP); } -static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic) +static int getGlobalGameStatus(int status) +{ + return (status == GAME_MODE_PSEUDO_TYPENAME ? GAME_MODE_MAIN : + status == GAME_MODE_SCOREINFO ? GAME_MODE_SCORES : + status); +} + +int getImageFromGraphicOrDefault(int graphic, int default_graphic) { if (graphic == IMG_UNDEFINED) - return NULL; + return IMG_UNDEFINED; boolean redefined = getImageListEntryFromImageID(graphic)->redefined; return (graphic_info[graphic].bitmap != NULL || redefined ? - graphic_info[graphic].bitmap : - graphic_info[default_graphic].bitmap); + graphic : default_graphic); } -static Bitmap *getBackgroundBitmap(int graphic) +static int getBackgroundImage(int graphic) { - return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND); + return getImageFromGraphicOrDefault(graphic, IMG_BACKGROUND); } -static Bitmap *getGlobalBorderBitmap(int graphic) +static int getGlobalBorderImage(int graphic) { - return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER); + return getImageFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER); } -Bitmap *getGlobalBorderBitmapFromStatus(int status) +Bitmap *getGlobalBorderBitmapFromStatus(int status_raw) { + int status = getGlobalGameStatus(status_raw); int graphic = - (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 : + (status == GAME_MODE_MAIN ? 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); + int graphic_final = getGlobalBorderImage(graphic); + + return graphic_info[graphic_final].bitmap; +} + +void SetBackgroundImage(int graphic, int redraw_mask) +{ + struct GraphicInfo *g = &graphic_info[graphic]; + struct GraphicInfo g_undefined = { 0 }; + + if (graphic == IMG_UNDEFINED) + g = &g_undefined; + + // always use original size bitmap for backgrounds, if existing + Bitmap *bitmap = (g->bitmaps != NULL && + g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] != NULL ? + g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] : g->bitmap); - return getGlobalBorderBitmap(graphic); + // remove every mask before setting mask for window, and + // remove window area mask before setting mask for main or door area + int remove_mask = (redraw_mask == REDRAW_ALL ? 0xffff : REDRAW_ALL); + + // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) + SetBackgroundBitmap(NULL, remove_mask, 0, 0, 0, 0); // !!! FIX THIS !!! + SetBackgroundBitmap(bitmap, redraw_mask, + g->src_x, g->src_y, + g->width, g->height); } void SetWindowBackgroundImageIfDefined(int graphic) { if (graphic_info[graphic].bitmap) - SetWindowBackgroundBitmap(graphic_info[graphic].bitmap); + SetBackgroundImage(graphic, REDRAW_ALL); } void SetMainBackgroundImageIfDefined(int graphic) { if (graphic_info[graphic].bitmap) - SetMainBackgroundBitmap(graphic_info[graphic].bitmap); + SetBackgroundImage(graphic, REDRAW_FIELD); } void SetDoorBackgroundImageIfDefined(int graphic) { if (graphic_info[graphic].bitmap) - SetDoorBackgroundBitmap(graphic_info[graphic].bitmap); + SetBackgroundImage(graphic, REDRAW_DOOR_1); } void SetWindowBackgroundImage(int graphic) { - SetWindowBackgroundBitmap(getBackgroundBitmap(graphic)); + SetBackgroundImage(getBackgroundImage(graphic), REDRAW_ALL); } void SetMainBackgroundImage(int graphic) { - SetMainBackgroundBitmap(getBackgroundBitmap(graphic)); + SetBackgroundImage(getBackgroundImage(graphic), REDRAW_FIELD); } void SetDoorBackgroundImage(int graphic) { - SetDoorBackgroundBitmap(getBackgroundBitmap(graphic)); + SetBackgroundImage(getBackgroundImage(graphic), REDRAW_DOOR_1); } void SetPanelBackground(void) { - struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL]; - - BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y, - gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE); - - SetDoorBackgroundBitmap(bitmap_db_panel); + SetDoorBackgroundImage(IMG_BACKGROUND_PANEL); } void DrawBackground(int x, int y, int width, int height) @@ -1406,39 +1473,42 @@ void SetBorderElement(void) } } -void FloodFillLevelExt(int from_x, int from_y, int fill_element, +void FloodFillLevelExt(int start_x, int start_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; + static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY]; + struct XY *check = xy_topdown; + int old_element = field[start_x][start_y]; + int stack_pos = 0; - // check if starting field still has the desired content - if (field[from_x][from_y] == fill_element) + // do nothing if start field already has the desired content + if (old_element == fill_element) return; - safety++; + stack_buffer[stack_pos++] = (struct XY){ start_x, start_y }; + + while (stack_pos > 0) + { + struct XY current = stack_buffer[--stack_pos]; + int i; - if (safety > max_fieldx * max_fieldy) - Fail("Something went wrong in 'FloodFill()'. Please debug."); + field[current.x][current.y] = fill_element; - old_element = field[from_x][from_y]; - field[from_x][from_y] = fill_element; + for (i = 0; i < 4; i++) + { + int x = current.x + check[i].x; + int y = current.y + check[i].y; - for (i = 0; i < 4; i++) - { - x = from_x + check[i][0]; - y = from_y + check[i][1]; + // check for stack buffer overflow (should not happen) + if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY) + Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug."); - if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element) - FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy, - field, max_fieldx, max_fieldy); + if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element) + stack_buffer[stack_pos++] = (struct XY){ x, y }; + } } - - safety--; } void FloodFillLevel(int from_x, int from_y, int fill_element, @@ -1455,11 +1525,18 @@ void SetRandomAnimationValue(int x, int y) gfx.anim_random_frame = GfxRandom[x][y]; } +void SetAnimationFirstLevel(int first_level) +{ + gfx.anim_first_level = first_level; +} + int getGraphicAnimationFrame(int graphic, int sync_frame) { // animation synchronized with global frame counter, not move position if (graphic_info[graphic].anim_global_sync || sync_frame < 0) sync_frame = FrameCounter; + else if (graphic_info[graphic].anim_global_anim_sync) + sync_frame = getGlobalAnimSyncFrame(); return getAnimationFrame(graphic_info[graphic].anim_frames, graphic_info[graphic].anim_delay, @@ -1468,6 +1545,40 @@ int getGraphicAnimationFrame(int graphic, int sync_frame) sync_frame); } +int getGraphicAnimationFrameXY(int graphic, int lx, int ly) +{ + if (graphic_info[graphic].anim_mode & ANIM_TILED) + { + struct GraphicInfo *g = &graphic_info[graphic]; + int xsize = MAX(1, g->anim_frames_per_line); + int ysize = MAX(1, g->anim_frames / xsize); + int xoffset = g->anim_start_frame % xsize; + int yoffset = g->anim_start_frame % ysize; + // may be needed if screen field is significantly larger than playfield + int x = (lx + xoffset + SCR_FIELDX * xsize) % xsize; + int y = (ly + yoffset + SCR_FIELDY * ysize) % ysize; + int sync_frame = y * xsize + x; + + return sync_frame % g->anim_frames; + } + else if (graphic_info[graphic].anim_mode & ANIM_RANDOM_STATIC) + { + struct GraphicInfo *g = &graphic_info[graphic]; + // may be needed if screen field is significantly larger than playfield + int x = (lx + SCR_FIELDX * lev_fieldx) % lev_fieldx; + int y = (ly + SCR_FIELDY * lev_fieldy) % lev_fieldy; + int sync_frame = GfxRandomStatic[x][y]; + + return sync_frame % g->anim_frames; + } + else + { + int sync_frame = (IN_LEV_FIELD(lx, ly) ? GfxFrame[lx][ly] : -1); + + return getGraphicAnimationFrame(graphic, sync_frame); + } +} + void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap) { struct GraphicInfo *g = &graphic_info[graphic]; @@ -1476,7 +1587,7 @@ void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap) if (tilesize == gfx.standard_tile_size) *bitmap = g->bitmaps[IMG_BITMAP_STANDARD]; else if (tilesize == game.tile_size) - *bitmap = g->bitmaps[IMG_BITMAP_GAME]; + *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME]; else *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)]; } @@ -1522,7 +1633,7 @@ void getSizedGraphicSourceExt(int graphic, int frame, int tilesize, *g = graphic_info[IMG_CHAR_EXCLAM]; // if no in-game graphics defined, always use standard graphic size - if (g->bitmaps[IMG_BITMAP_GAME] == NULL) + if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL) tilesize = TILESIZE; getGraphicSourceBitmap(graphic, tilesize, bitmap); @@ -1549,6 +1660,24 @@ void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y) getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y); } +void getGlobalAnimGraphicSource(int graphic, int frame, + Bitmap **bitmap, int *x, int *y) +{ + 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]; + + // use original size graphics, if existing, else use standard size graphics + if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL]) + *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL]; + else + *bitmap = g->bitmaps[IMG_BITMAP_STANDARD]; + + getGraphicSourceXY(graphic, frame, x, y, FALSE); +} + static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap, int *x, int *y, boolean get_backside) { @@ -1623,7 +1752,7 @@ void DrawGraphicThruMask(int x, int y, int graphic, int frame) #if DEBUG if (!IN_SCR_FIELD(x, y)) { - Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d", + Debug("draw:DrawGraphicThruMask", "x = %d, y = %d, graphic = %d", x, y, graphic); Debug("draw:DrawGraphicThruMask", "This should never happen!"); @@ -1642,7 +1771,7 @@ void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame) #if DEBUG if (!IN_SCR_FIELD(x, y)) { - Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d", + Debug("draw:DrawFixedGraphicThruMask", "x = %d, y = %d, graphic = %d", x, y, graphic); Debug("draw:DrawFixedGraphicThruMask", "This should never happen!"); @@ -1716,7 +1845,7 @@ void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic, void DrawMiniGraphic(int x, int y, int graphic) { - DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic); + DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX, SY + y * MINI_TILEY, graphic); MarkTileDirty(x / 2, y / 2); } @@ -1919,9 +2048,9 @@ static void DrawGraphicShifted(int x, int y, int dx, int dy, } if (graphic_info[graphic].double_movement) // EM style movement images - DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode); + DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode, mask_mode); else - DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode); + DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode, mask_mode); } static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, @@ -1939,22 +2068,28 @@ void DrawScreenElementExt(int x, int y, int dx, int dy, int element, if (IN_LEV_FIELD(lx, ly)) { + if (element == EL_EMPTY) + element = GfxElementEmpty[lx][ly]; + SetRandomAnimationValue(lx, ly); graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]); - frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]); + frame = getGraphicAnimationFrameXY(graphic, lx, ly); // 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]); + frame = getGraphicAnimationFrameXY(graphic, lx, ly); } + + if (game.use_masked_elements && (dx || dy)) + mask_mode = USE_MASKING; } else // border element { graphic = el2img(element); - frame = getGraphicAnimationFrame(graphic, -1); + frame = getGraphicAnimationFrameXY(graphic, lx, ly); } if (element == EL_EXPANDABLE_WALL) @@ -2068,8 +2203,27 @@ static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy, cx = (dx > 0 ? TILESIZE_VAR - width : 0); cy = (dy > 0 ? TILESIZE_VAR - height : 0); - BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, - width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy); + if (game.use_masked_elements) + { + int graphic0 = el2img(EL_EMPTY); + int frame0 = getGraphicAnimationFrameXY(graphic0, x, y); + Bitmap *src_bitmap0; + int src_x0, src_y0; + + getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0); + + BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy, + width, height, + FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy); + + BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy, + width, height, + FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy); + } + else + BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, + width, height, + FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy); } static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame, @@ -2088,6 +2242,15 @@ static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame, getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y); + // only needed when using masked elements + int graphic0 = el2img(EL_EMPTY); + int frame0 = getGraphicAnimationFrameXY(graphic0, x, y); + Bitmap *src_bitmap0; + int src_x0, src_y0; + + if (game.use_masked_elements) + getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0); + // draw simple, sloppy, non-corner-accurate crumbled border width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR); @@ -2095,9 +2258,23 @@ static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame, cx = (dir == 2 ? crumbled_border_pos_var : 0); cy = (dir == 3 ? crumbled_border_pos_var : 0); - BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height, - FX + sx * TILEX_VAR + cx, - FY + sy * TILEY_VAR + cy); + if (game.use_masked_elements) + { + BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy, + width, height, + FX + sx * TILEX_VAR + cx, + FY + sy * TILEY_VAR + cy); + + BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy, + width, height, + FX + sx * TILEX_VAR + cx, + FY + sy * TILEY_VAR + cy); + } + else + BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, + width, height, + FX + sx * TILEX_VAR + cx, + FY + sy * TILEY_VAR + cy); // (remaining middle border part must be at least as big as corner part) if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) || @@ -2143,10 +2320,23 @@ static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame, by = cy; } - BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by, - width, height, - FX + sx * TILEX_VAR + cx, - FY + sy * TILEY_VAR + cy); + if (game.use_masked_elements) + { + BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by, + width, height, + FX + sx * TILEX_VAR + cx, + FY + sy * TILEY_VAR + cy); + + BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by, + width, height, + FX + sx * TILEX_VAR + cx, + FY + sy * TILEY_VAR + cy); + } + else + BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by, + width, height, + FX + sx * TILEX_VAR + cx, + FY + sy * TILEY_VAR + cy); } } } @@ -2156,13 +2346,7 @@ static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame) int sx = SCREENX(x), sy = SCREENY(y); int element; int i; - static int xy[4][2] = - { - { 0, -1 }, - { -1, 0 }, - { +1, 0 }, - { 0, +1 } - }; + struct XY *xy = xy_topdown; if (!IN_LEV_FIELD(x, y)) return; @@ -2177,8 +2361,8 @@ static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame) // crumble field borders towards direct neighbour fields for (i = 0; i < 4; i++) { - int xx = x + xy[i][0]; - int yy = y + xy[i][1]; + int xx = x + xy[i].x; + int yy = y + xy[i].y; element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) : BorderElement); @@ -2212,10 +2396,10 @@ static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame) // crumble field borders of direct neighbour fields for (i = 0; i < 4; i++) { - int xx = x + xy[i][0]; - int yy = y + xy[i][1]; - int sxx = sx + xy[i][0]; - int syy = sy + xy[i][1]; + int xx = x + xy[i].x; + int yy = y + xy[i].y; + int sxx = sx + xy[i].x; + int syy = sy + xy[i].y; if (!IN_LEV_FIELD(xx, yy) || !IN_SCR_FIELD(sxx, syy)) @@ -2301,29 +2485,23 @@ void DrawLevelFieldCrumbledDigging(int x, int y, int direction, int frame2 = getGraphicAnimationFrame(graphic2, step_frame); int sx = SCREENX(x), sy = SCREENY(y); - DrawGraphic(sx, sy, graphic1, frame1); + DrawScreenGraphic(sx, sy, graphic1, frame1); DrawLevelFieldCrumbledExt(x, y, graphic2, frame2); } void DrawLevelFieldCrumbledNeighbours(int x, int y) { int sx = SCREENX(x), sy = SCREENY(y); - static int xy[4][2] = - { - { 0, -1 }, - { -1, 0 }, - { +1, 0 }, - { 0, +1 } - }; + struct XY *xy = xy_topdown; int i; // crumble direct neighbour fields (required for field borders) for (i = 0; i < 4; i++) { - int xx = x + xy[i][0]; - int yy = y + xy[i][1]; - int sxx = sx + xy[i][0]; - int syy = sy + xy[i][1]; + int xx = x + xy[i].x; + int yy = y + xy[i].y; + int sxx = sx + xy[i].x; + int syy = sy + xy[i].y; if (!IN_LEV_FIELD(xx, yy) || !IN_SCR_FIELD(sxx, syy) || @@ -2382,9 +2560,43 @@ static int getBorderElement(int x, int y) return border[steel_position][steel_type]; } +void DrawScreenGraphic(int x, int y, int graphic, int frame) +{ + if (game.use_masked_elements) + { + if (graphic != el2img(EL_EMPTY)) + DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING); + + DrawGraphicThruMask(x, y, graphic, frame); + } + else + { + DrawGraphic(x, y, graphic, frame); + } +} + +void DrawLevelGraphic(int x, int y, int graphic, int frame) +{ + DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame); +} + void DrawScreenElement(int x, int y, int element) { - DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING); + int mask_mode = NO_MASKING; + + if (game.use_masked_elements) + { + int lx = LEVELX(x), ly = LEVELY(y); + + if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY) + { + DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING); + + mask_mode = USE_MASKING; + } + } + + DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode); DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y)); } @@ -2438,6 +2650,16 @@ void DrawScreenField(int x, int y) else DrawScreenElement(x, y, EL_EMPTY); + if (cut_mode != CUT_BELOW && game.use_masked_elements) + { + int dir = MovDir[lx][ly]; + int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0); + int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0); + + if (IN_SCR_FIELD(newx, newy)) + DrawScreenElement(newx, newy, EL_EMPTY); + } + if (horiz_move) DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING); else if (cut_mode == NO_CUTTING) @@ -2513,7 +2735,7 @@ void DrawLevelField(int x, int y) DrawScreenField(SCREENX(x), SCREENY(y)); else if (IS_MOVING(x, y)) { - int newx,newy; + int newx, newy; Moving2Blocked(x, y, &newx, &newy); if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy))) @@ -2705,9 +2927,9 @@ static void AnimateEnvelope(int envelope_nr, int anim_mode, int action) int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND); boolean ffwd_delay = (tape.playing && tape.fast_forward); boolean no_delay = (tape.warp_forward); - unsigned int anim_delay = 0; int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay); int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2); + DelayCounter anim_delay = { anim_delay_value }; int font_nr = FONT_ENVELOPE_1 + envelope_nr; int font_width = getFontWidth(font_nr); int font_height = getFontHeight(font_nr); @@ -2744,16 +2966,16 @@ static void AnimateEnvelope(int envelope_nr, int anim_mode, int action) for (xx = 0; xx < xsize; xx++) DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr); - DrawTextBuffer(sx + font_width, sy + font_height, - level.envelope[envelope_nr].text, font_nr, max_xsize, - xsize - 2, ysize - 2, 0, mask_mode, - level.envelope[envelope_nr].autowrap, - level.envelope[envelope_nr].centered, FALSE); + DrawTextArea(sx + font_width, sy + font_height, + level.envelope[envelope_nr].text, font_nr, max_xsize, + xsize - 2, ysize - 2, 0, mask_mode, + level.envelope[envelope_nr].autowrap, + level.envelope[envelope_nr].centered, FALSE); redraw_mask |= REDRAW_FIELD; BackToFront(); - SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame); + SkipUntilDelayReached(&anim_delay, &i, last_frame); } ClearAutoRepeatKeyEvents(); @@ -2813,8 +3035,7 @@ void ShowEnvelope(int envelope_nr) static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy, int xsize, int ysize) { - if (!global.use_envelope_request || - request.sort_priority <= 0) + if (!global.use_envelope_request) return; if (request.bitmap == NULL || @@ -2834,9 +3055,22 @@ static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy, BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0); + // create masked surface for request bitmap, if needed + if (graphic_info[IMG_BACKGROUND_REQUEST].draw_masked) + { + SDL_Surface *surface = request.bitmap->surface; + SDL_Surface *surface_masked = request.bitmap->surface_masked; + + SDLBlitSurface(surface, surface_masked, 0, 0, xsize, ysize, 0, 0); + SDL_SetColorKey(surface_masked, SET_TRANSPARENT_PIXEL, + SDL_MapRGB(surface_masked->format, 0x00, 0x00, 0x00)); + } + SDLFreeBitmapTextures(request.bitmap); SDLCreateBitmapTextures(request.bitmap); + ResetBitmapAlpha(request.bitmap); + // set envelope request run-time values request.sx = sx; request.sy = sy; @@ -2844,16 +3078,22 @@ static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy, request.ysize = ysize; } -void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage) +void DrawEnvelopeRequestToScreen(int drawing_target) { if (global.use_envelope_request && - game.request_active_or_moving && - request.sort_priority > 0 && - drawing_target == DRAW_TO_SCREEN && - drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2) + game.request_active && + drawing_target == DRAW_TO_SCREEN) { - BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize, - request.sx, request.sy); + struct GraphicInfo *g = &graphic_info[IMG_BACKGROUND_REQUEST]; + + SetBitmapAlphaNextBlit(request.bitmap, g->alpha); + + if (g->draw_masked) + BlitToScreenMasked(request.bitmap, 0, 0, request.xsize, request.ysize, + request.sx, request.sy); + else + BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize, + request.sx, request.sy); } } @@ -2924,7 +3164,7 @@ static void setRequestPosition(int *x, int *y, boolean add_border_size) setRequestPositionExt(x, y, request.width, request.height, add_border_size); } -static void DrawEnvelopeRequest(char *text) +static void DrawEnvelopeRequestText(int sx, int sy, char *text) { char *text_final = text; char *text_door_style = NULL; @@ -2942,15 +3182,11 @@ static void DrawEnvelopeRequest(char *text) int line_length = max_text_width / font_width; int max_lines = max_text_height / line_height; int text_width = line_length * font_width; - int width = request.width; - int height = request.height; - int tile_size = MAX(request.step_offset, 1); - int x_steps = width / tile_size; - int y_steps = height / tile_size; int sx_offset = border_size; int sy_offset = border_size; - int sx, sy; - int i, x, y; + + // force DOOR font inside door area + SetFontStatus(GAME_MODE_PSEUDO_DOOR); if (request.centered) sx_offset = (request.width - text_width) / 2; @@ -2959,6 +3195,13 @@ static void DrawEnvelopeRequest(char *text) { char *src_text_ptr, *dst_text_ptr; + if (maxWordLengthInRequestString(text) > line_length) + { + font_nr = FONT_REQUEST_NARROW; + font_width = getFontWidth(font_nr); + line_length = max_text_width / font_width; + } + text_door_style = checked_malloc(2 * strlen(text) + 1); src_text_ptr = text; @@ -2982,9 +3225,34 @@ static void DrawEnvelopeRequest(char *text) text_final = text_door_style; } + 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); + + if (text_door_style) + free(text_door_style); + + ResetFontStatus(); +} + +static void DrawEnvelopeRequest(char *text, unsigned int req_state) +{ + DrawBuffer *drawto_last = drawto; + int graphic = IMG_BACKGROUND_REQUEST; + int width = request.width; + int height = request.height; + int tile_size = MAX(request.step_offset, 1); + int x_steps = width / tile_size; + int y_steps = height / tile_size; + int sx, sy; + int x, y; + setRequestPosition(&sx, &sy, FALSE); - ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE); + // draw complete envelope request to temporary bitmap + drawto = bitmap_db_store_1; + + ClearRectangle(drawto, sx, sy, width, height); for (y = 0; y < y_steps; y++) for (x = 0; x < x_steps; x++) @@ -2992,38 +3260,28 @@ static void DrawEnvelopeRequest(char *text) x, y, x_steps, y_steps, tile_size, tile_size); - // 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); - - ResetFontStatus(); - - for (i = 0; i < NUM_TOOL_BUTTONS; i++) - RedrawGadget(tool_gadget[i]); + // write text for request + DrawEnvelopeRequestText(sx, sy, text); - // store readily prepared envelope request for later use when animating - BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0); + MapToolButtons(req_state); - PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height); + // restore pointer to drawing buffer + drawto = drawto_last; - if (text_door_style) - free(text_door_style); + // prepare complete envelope request from temporary bitmap + PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy, width, height); } static void AnimateEnvelopeRequest(int anim_mode, int action) { - int graphic = IMG_BACKGROUND_REQUEST; - boolean draw_masked = graphic_info[graphic].draw_masked; + boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded()); int delay_value_normal = request.step_delay; int delay_value_fast = delay_value_normal / 2; 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 = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2); - unsigned int anim_delay = 0; + int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value) / 2); + DelayCounter anim_delay = { anim_delay_value }; int tile_size = MAX(request.step_offset, 1); int max_xsize = request.width / tile_size; @@ -3065,11 +3323,12 @@ static void AnimateEnvelopeRequest(int anim_mode, int action) int dst_x, dst_y; int xx, yy; + if (game_ended) + HandleGameActions(); + setRequestPosition(&src_x, &src_y, FALSE); setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE); - BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0); - for (yy = 0; yy < 2; yy++) { for (xx = 0; xx < 2; xx++) @@ -3081,22 +3340,21 @@ static void AnimateEnvelopeRequest(int anim_mode, int action) int xx_size = (xx ? tile_size : xsize_size_left); int yy_size = (yy ? tile_size : ysize_size_top); - if (draw_masked) - BlitBitmapMasked(bitmap_db_store_2, backbuffer, - src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy); - else - BlitBitmap(bitmap_db_store_2, backbuffer, - src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy); + // draw partial (animated) envelope request to temporary bitmap + BlitBitmap(bitmap_db_store_1, bitmap_db_store_2, + src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy); } } - PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height); + // prepare partial (animated) envelope request from temporary bitmap + PrepareEnvelopeRequestToScreen(bitmap_db_store_2, dst_x, dst_y, + width, height); redraw_mask |= REDRAW_FIELD; BackToFront(); - SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame); + SkipUntilDelayReached(&anim_delay, &i, last_frame); } ClearAutoRepeatKeyEvents(); @@ -3113,40 +3371,6 @@ static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action) int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL: anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode); - if (game_status == GAME_MODE_PLAYING) - BlitScreenToBitmap(backbuffer); - - SetDrawtoField(DRAW_TO_BACKBUFFER); - - // SetDrawBackgroundMask(REDRAW_NONE); - - if (action == ACTION_OPENING) - { - BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0); - - if (req_state & REQ_ASK) - { - MapGadget(tool_gadget[TOOL_CTRL_ID_YES]); - MapGadget(tool_gadget[TOOL_CTRL_ID_NO]); - MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]); - MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]); - } - else if (req_state & REQ_CONFIRM) - { - MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]); - MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]); - } - else if (req_state & REQ_PLAYER) - { - MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]); - MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]); - MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]); - MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]); - } - - DrawEnvelopeRequest(text); - } - game.envelope_active = TRUE; // needed for RedrawPlayfield() events if (action == ACTION_OPENING) @@ -3170,20 +3394,14 @@ static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action) } game.envelope_active = FALSE; +} - if (action == ACTION_CLOSING) - BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0); - - // SetDrawBackgroundMask(last_draw_background_mask); - - redraw_mask |= REDRAW_FIELD; - - BackToFront(); +static Bitmap *GetPreviewTileBitmap(Bitmap *bitmap) +{ + if (level.game_engine_type == GAME_ENGINE_TYPE_BD) + return GetPreviewTileBitmap_BD(bitmap); - if (action == ACTION_CLOSING && - game_status == GAME_MODE_PLAYING && - level.game_engine_type == GAME_ENGINE_TYPE_RND) - SetDrawtoField(DRAW_TO_FIELDBUFFER); + return bitmap; } static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize) @@ -3199,6 +3417,10 @@ static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize) int graphic = el2preimg(element); getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y); + + // for BD style levels, maybe use bitmap with level-specific colors + src_bitmap = GetPreviewTileBitmap(src_bitmap); + BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x, dst_y); } @@ -3206,7 +3428,7 @@ static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize) void DrawLevel(int draw_background_mask) { - int x,y; + int x, y; SetMainBackgroundImage(IMG_BACKGROUND_PLAYING); SetDrawBackgroundMask(draw_background_mask); @@ -3223,7 +3445,7 @@ void DrawLevel(int draw_background_mask) void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y, int tilesize) { - int x,y; + int x, y; for (x = 0; x < size_x; x++) for (y = 0; y < size_y; y++) @@ -3234,7 +3456,7 @@ void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y, void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y) { - int x,y; + int x, y; for (x = 0; x < size_x; x++) for (y = 0; y < size_y; y++) @@ -3367,15 +3589,17 @@ static void DrawPreviewLevelInfo(int mode) static void DrawPreviewLevelExt(boolean restart) { - static unsigned int scroll_delay = 0; - static unsigned int label_delay = 0; + static DelayCounter scroll_delay = { 0 }; + static DelayCounter label_delay = { 0 }; static int from_x, from_y, scroll_direction; static int label_state, label_counter; - unsigned int scroll_delay_value = preview.step_delay; 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); + scroll_delay.value = preview.step_delay; + label_delay.value = MICROLEVEL_LABEL_DELAY; + if (restart) { from_x = 0; @@ -3403,8 +3627,8 @@ static void DrawPreviewLevelExt(boolean restart) DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR); // initialize delay counters - DelayReached(&scroll_delay, 0); - DelayReached(&label_delay, 0); + ResetDelayCounter(&scroll_delay); + ResetDelayCounter(&label_delay); if (leveldir_current->name) { @@ -3429,7 +3653,7 @@ static void DrawPreviewLevelExt(boolean restart) // 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)) + DelayReached(&scroll_delay)) { switch (scroll_direction) { @@ -3487,7 +3711,7 @@ static void DrawPreviewLevelExt(boolean restart) if (!strEqual(level.name, NAMELESS_LEVEL_NAME) && !strEqual(level.author, ANONYMOUS_NAME) && !strEqual(level.author, leveldir_current->name) && - DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY)) + DelayReached(&label_delay)) { int max_label_counter = 23; @@ -3547,7 +3771,7 @@ void DrawPreviewPlayers(void) { int element = level.field[x][y]; - if (ELEM_IS_PLAYER(element)) + if (IS_PLAYER_ELEMENT(element)) { int player_nr = GET_PLAYER_NR(element); @@ -3603,8 +3827,35 @@ void DrawPreviewPlayers(void) } } +static void PreparePreviewTileBitmap(void) +{ + // check if special preview bitmap with level-specific colors should be created + if (level.game_engine_type != GAME_ENGINE_TYPE_BD) + return; + + // use original sized bitmap (else reduced color palette is lost by downscaling) + int original_tilesize = MAX(MINI_TILESIZE, preview.tile_size); + int scale_down_factor = original_tilesize / preview.tile_size; + Bitmap *src_bitmap; + int src_x, src_y; + int element_template = EL_BD_GAME_GRAPHICS_COLOR_TEMPLATE; + int graphic_template = el2preimg(element_template); + int element_default = EL_BD_ROCK; + int graphic_default = el2preimg(element_default); + + // create special preview bitmap and scale it down to preview tile size + getSizedGraphicSource(graphic_template, 0, original_tilesize, &src_bitmap, &src_x, &src_y); + PreparePreviewTileBitmap_BD(src_bitmap, scale_down_factor); + + // force using special preview bitmap to replace original preview bitmap + getSizedGraphicSource(graphic_default, 0, preview.tile_size, &src_bitmap, &src_x, &src_y); + SetPreviewTileBitmapReference_BD(src_bitmap); +} + void DrawPreviewLevelInitial(void) { + PreparePreviewTileBitmap(); // only needed for native BD style levels + DrawPreviewLevelExt(TRUE); DrawPreviewPlayers(); } @@ -3712,10 +3963,10 @@ void ClearNetworkPlayers(void) } static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y, - int graphic, int sync_frame, + int graphic, int lx, int ly, int mask_mode) { - int frame = getGraphicAnimationFrame(graphic, sync_frame); + int frame = getGraphicAnimationFrameXY(graphic, lx, ly); if (mask_mode == USE_MASKING) DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame); @@ -3734,15 +3985,38 @@ void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y, DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame); } +void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y, + int graphic, int sync_frame, int tilesize, + int mask_mode) +{ + int frame = getGraphicAnimationFrame(graphic, sync_frame); + + if (mask_mode == USE_MASKING) + DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize); + else + DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize); +} + static void DrawGraphicAnimation(int x, int y, int graphic) { int lx = LEVELX(x), ly = LEVELY(y); + int mask_mode = NO_MASKING; if (!IN_SCR_FIELD(x, y)) return; + if (game.use_masked_elements) + { + if (Tile[lx][ly] != EL_EMPTY) + { + DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING); + + mask_mode = USE_MASKING; + } + } + DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, - graphic, GfxFrame[lx][ly], NO_MASKING); + graphic, lx, ly, mask_mode); MarkTileDirty(x, y); } @@ -3750,12 +4024,24 @@ static void DrawGraphicAnimation(int x, int y, int graphic) void DrawFixedGraphicAnimation(int x, int y, int graphic) { int lx = LEVELX(x), ly = LEVELY(y); + int mask_mode = NO_MASKING; if (!IN_SCR_FIELD(x, y)) return; + if (game.use_masked_elements) + { + if (Tile[lx][ly] != EL_EMPTY) + { + DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING); + + mask_mode = USE_MASKING; + } + } + DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY, - graphic, GfxFrame[lx][ly], NO_MASKING); + graphic, lx, ly, mask_mode); + MarkTileDirty(x, y); } @@ -3778,9 +4064,15 @@ void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic) if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy)) return; + if (Tile[x][y] == EL_EMPTY) + graphic = el2img(GfxElementEmpty[x][y]); + if (!IS_NEW_FRAME(GfxFrame[x][y], graphic)) return; + if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC)) + return; + DrawGraphicAnimation(sx, sy, graphic); #if 1 @@ -3832,7 +4124,7 @@ static int getPlayerGraphic(struct PlayerInfo *player, int move_dir) return graphic; } else - return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir); + return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir); } static boolean equalGraphics(int graphic1, int graphic2) @@ -3981,9 +4273,6 @@ static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage) DrawDynamite(last_jx, last_jy); else DrawLevelField(last_jx, last_jy); - - 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) { @@ -4014,7 +4303,7 @@ static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage) if (GFX_CRUMBLED(old_element)) DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame); else - DrawGraphic(sx, sy, old_graphic, frame); + DrawScreenGraphic(sx, sy, old_graphic, frame); if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER) static_player_is_opaque[pnr] = TRUE; @@ -4041,6 +4330,9 @@ static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage) if (!player->is_pushing || !player->is_moving) return; + if (Tile[next_jx][next_jy] == EL_EXPLOSION) + return; + int gfx_frame = GfxFrame[jx][jy]; if (!IS_MOVING(jx, jy)) // push movement already finished @@ -4062,14 +4354,12 @@ static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage) DrawLevelElement(jx, jy, Back[jx][jy]); else DrawLevelElement(jx, jy, EL_EMPTY); - - if (Back[next_jx][next_jy]) - DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]); - else - DrawLevelElement(next_jx, next_jy, EL_EMPTY); } - else if (Back[next_jx][next_jy]) + + if (Back[next_jx][next_jy]) DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]); + else + DrawLevelElement(next_jx, next_jy, EL_EMPTY); int px = SCREENX(jx), py = SCREENY(jy); int pxx = (TILEX - ABS(sxx)) * dx; @@ -4126,7 +4416,7 @@ static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage) if (IS_ACTIVE_BOMB(element)) { int graphic = el2img(element); - int frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]); + int frame = getGraphicAnimationFrameXY(graphic, jx, jy); if (game.emulation == EMU_SUPAPLEX) DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame); @@ -4260,26 +4550,15 @@ void WaitForEventToContinue(void) } } -#define MAX_REQUEST_LINES 13 -#define MAX_REQUEST_LINE_FONT1_LEN 7 -#define MAX_REQUEST_LINE_FONT2_LEN 10 - -static int RequestHandleEvents(unsigned int req_state) +static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game) { - boolean game_just_ended = (game_status == GAME_MODE_PLAYING && - checkGameEnded()); + boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded()); int draw_buffer_last = GetDrawtoField(); 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; @@ -4289,21 +4568,13 @@ static int RequestHandleEvents(unsigned int req_state) while (result < 0) { - boolean event_handled = FALSE; - - if (game_just_ended) + if (game_ended) { - SetDrawtoField(draw_buffer_last); + SetDrawtoField(draw_buffer_game); HandleGameActions(); SetDrawtoField(DRAW_TO_BACKBUFFER); - - if (global.use_envelope_request) - { - // copy current state of request area to middle of playfield area - BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy); - } } if (PendingEvent()) @@ -4312,14 +4583,13 @@ static int RequestHandleEvents(unsigned int req_state) while (NextValidEvent(&event)) { - event_handled = TRUE; - switch (event.type) { case EVENT_BUTTONPRESS: case EVENT_BUTTONRELEASE: case EVENT_MOTIONNOTIFY: { + DrawBuffer *drawto_last = drawto; int mx, my; if (event.type == EVENT_MOTIONNOTIFY) @@ -4342,9 +4612,25 @@ static int RequestHandleEvents(unsigned int req_state) button_status = MB_RELEASED; } + if (global.use_envelope_request) + { + // draw changed button states to temporary bitmap + drawto = bitmap_db_store_1; + } + // this sets 'request_gadget_id' HandleGadgets(mx, my, button_status); + if (global.use_envelope_request) + { + // restore pointer to drawing buffer + drawto = drawto_last; + + // prepare complete envelope request from temporary bitmap + PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy, + width, height); + } + switch (request_gadget_id) { case TOOL_CTRL_ID_YES: @@ -4374,11 +4660,12 @@ static int RequestHandleEvents(unsigned int req_state) break; default: - // only check clickable animations if no request gadget clicked - HandleGlobalAnimClicks(mx, my, button_status, FALSE); break; } + // only needed to handle clickable pointer animations here + HandleGlobalAnimClicks(mx, my, button_status, FALSE); + break; } @@ -4395,7 +4682,7 @@ static int RequestHandleEvents(unsigned int req_state) case EVENT_KEYPRESS: { - Key key = GetEventKey((KeyEvent *)&event, TRUE); + Key key = GetEventKey((KeyEvent *)&event); switch (key) { @@ -4568,44 +4855,21 @@ static int RequestHandleEvents(unsigned int req_state) } } - if (event_handled) - { - if (game_just_ended) - { - 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); - } - } - - PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height); - } - BackToFront(); } SetDrawtoField(draw_buffer_last); - game.request_active = FALSE; - return result; } -static boolean RequestDoor(char *text, unsigned int req_state) +static void DoRequestBefore(void) { - unsigned int old_door_state; - int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN; - int font_nr = FONT_TEXT_2; - char *text_ptr; - int result; - int ty; + boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded()); - if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN) - { - max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN; - font_nr = FONT_TEXT_1; - } + // when showing request dialog after game ended, deactivate game panel + if (game_ended) + game.panel.active = FALSE; if (game_status == GAME_MODE_PLAYING) BlitScreenToBitmap(backbuffer); @@ -4619,41 +4883,89 @@ static boolean RequestDoor(char *text, unsigned int req_state) // 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) + !game.all_players_gone) SendToServer_PausePlaying(); - old_door_state = GetDoorState(); - // simulate releasing mouse button over last gadget, if still pressed if (button_status) HandleGadgets(-1, -1, 0); UnmapAllGadgets(); +} - // draw released gadget before proceeding - // BackToFront(); +static void DoRequestAfter(void) +{ + RemapAllGadgets(); - if (old_door_state & DOOR_OPEN_1) + if (game_status == GAME_MODE_PLAYING) { - CloseDoor(DOOR_CLOSE_1); + SetPanelBackground(); + SetDrawBackgroundMask(REDRAW_DOOR_1); + } + else + { + SetDrawBackgroundMask(REDRAW_FIELD); + } - // save old door content - BlitBitmap(bitmap_db_door_1, bitmap_db_door_1, - 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0); + // continue network game after request + if (network.enabled && + game_status == GAME_MODE_PLAYING && + !game.all_players_gone) + SendToServer_ContinuePlaying(); + + // restore deactivated drawing when quick-loading level tape recording + if (tape.playing && tape.deactivate_display) + TapeDeactivateDisplayOn(); +} + +static void setRequestDoorTextProperties(char *text, + int text_spacing, + int line_spacing, + int *set_font_nr, + int *set_max_lines, + int *set_max_line_length) +{ + struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status]; + struct TextPosInfo *pos = &request.button.confirm; + int button_ypos = pos->y; + int font_nr = FONT_TEXT_2; + int font_width = getFontWidth(font_nr); + int font_height = getFontHeight(font_nr); + int line_height = font_height + line_spacing; + int max_text_width = vp_door_1->width; + int max_text_height = button_ypos - 2 * text_spacing; + int max_line_length = max_text_width / font_width; + int max_lines = max_text_height / line_height; + + if (maxWordLengthInRequestString(text) > max_line_length) + { + font_nr = FONT_TEXT_1; + font_width = getFontWidth(font_nr); + max_line_length = max_text_width / font_width; } - SetDoorBackgroundImage(IMG_BACKGROUND_DOOR); - SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1); + *set_font_nr = font_nr; + *set_max_lines = max_lines; + *set_max_line_length = max_line_length; +} - // clear door drawing field - DrawBackground(DX, DY, DXSIZE, DYSIZE); +static void DrawRequestDoorText(char *text) +{ + char *text_ptr = text; + int text_spacing = 8; + int line_spacing = 2; + int max_request_lines; + int max_request_line_len; + int font_nr; + int ty; // force DOOR font inside door area SetFontStatus(GAME_MODE_PSEUDO_DOOR); - // write text for request - for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++) + setRequestDoorTextProperties(text, text_spacing, line_spacing, &font_nr, + &max_request_lines, &max_request_line_len); + + for (text_ptr = text, ty = 0; ty < max_request_lines; ty++) { char text_line[max_request_line_len + 1]; int tx, tl, tc = 0; @@ -4664,7 +4976,6 @@ static boolean RequestDoor(char *text, unsigned int req_state) for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++) { tc = *(text_ptr + tx); - // if (!tc || tc == ' ') if (!tc || tc == ' ' || tc == '?' || tc == '!') break; } @@ -4683,59 +4994,48 @@ static boolean RequestDoor(char *text, unsigned int req_state) text_line[tl] = 0; DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2, - DY + 8 + ty * (getFontHeight(font_nr) + 2), + DY + text_spacing + ty * (getFontHeight(font_nr) + line_spacing), text_line, font_nr); text_ptr += tl + (tc == ' ' ? 1 : 0); - // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0); } ResetFontStatus(); +} - if (req_state & REQ_ASK) - { - MapGadget(tool_gadget[TOOL_CTRL_ID_YES]); - MapGadget(tool_gadget[TOOL_CTRL_ID_NO]); - MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]); - MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]); - } - else if (req_state & REQ_CONFIRM) - { - MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]); - MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]); - } - else if (req_state & REQ_PLAYER) +static int RequestDoor(char *text, unsigned int req_state) +{ + unsigned int old_door_state = GetDoorState(); + int draw_buffer_last = GetDrawtoField(); + int result; + + if (old_door_state & DOOR_OPEN_1) { - MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]); - MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]); - MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]); - MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]); + CloseDoor(DOOR_CLOSE_1); + + // save old door content + BlitBitmap(bitmap_db_door_1, bitmap_db_door_1, + 0, 0, DXSIZE, DYSIZE, DXSIZE, 0); } - // copy request gadgets to door backbuffer - BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0); + SetDoorBackgroundImage(IMG_BACKGROUND_DOOR); + SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1); - OpenDoor(DOOR_OPEN_1); + // clear door drawing field + DrawBackground(DX, DY, DXSIZE, DYSIZE); - if (!(req_state & REQUEST_WAIT_FOR_INPUT)) - { - if (game_status == GAME_MODE_PLAYING) - { - SetPanelBackground(); - SetDrawBackgroundMask(REDRAW_DOOR_1); - } - else - { - SetDrawBackgroundMask(REDRAW_FIELD); - } + // write text for request + DrawRequestDoorText(text); - return FALSE; - } + MapToolButtons(req_state); - SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1); + // copy request gadgets to door backbuffer + BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0); + + OpenDoor(DOOR_OPEN_1); // ---------- handle request buttons ---------- - result = RequestHandleEvents(req_state); + result = RequestHandleEvents(req_state, draw_buffer_last); UnmapToolButtons(); @@ -4748,134 +5048,48 @@ static boolean RequestDoor(char *text, unsigned int req_state) OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK); } - RemapAllGadgets(); - - if (game_status == GAME_MODE_PLAYING) - { - SetPanelBackground(); - SetDrawBackgroundMask(REDRAW_DOOR_1); - } - else - { - SetDrawBackgroundMask(REDRAW_FIELD); - } - - // 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(); - - // restore deactivated drawing when quick-loading level tape recording - if (tape.playing && tape.deactivate_display) - TapeDeactivateDisplayOn(); - return result; } -static boolean RequestEnvelope(char *text, unsigned int req_state) +static int RequestEnvelope(char *text, unsigned int req_state) { + int draw_buffer_last = GetDrawtoField(); int result; - if (game_status == GAME_MODE_PLAYING) - BlitScreenToBitmap(backbuffer); - - // disable deactivated drawing when quick-loading level tape recording - if (tape.playing && tape.deactivate_display) - TapeDeactivateDisplayOff(TRUE); - - SetMouseCursor(CURSOR_DEFAULT); - - // 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(); - - // simulate releasing mouse button over last gadget, if still pressed - if (button_status) - HandleGadgets(-1, -1, 0); - - UnmapAllGadgets(); - - // (replace with setting corresponding request background) - // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR); - // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1); - - // clear door drawing field - // DrawBackground(DX, DY, DXSIZE, DYSIZE); - + DrawEnvelopeRequest(text, req_state); ShowEnvelopeRequest(text, req_state, ACTION_OPENING); - if (!(req_state & REQUEST_WAIT_FOR_INPUT)) - { - if (game_status == GAME_MODE_PLAYING) - { - SetPanelBackground(); - SetDrawBackgroundMask(REDRAW_DOOR_1); - } - else - { - SetDrawBackgroundMask(REDRAW_FIELD); - } - - return FALSE; - } - - SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1); - // ---------- handle request buttons ---------- - result = RequestHandleEvents(req_state); + result = RequestHandleEvents(req_state, draw_buffer_last); UnmapToolButtons(); ShowEnvelopeRequest(text, req_state, ACTION_CLOSING); - RemapAllGadgets(); - - if (game_status == GAME_MODE_PLAYING) - { - SetPanelBackground(); - SetDrawBackgroundMask(REDRAW_DOOR_1); - } - else - { - SetDrawBackgroundMask(REDRAW_FIELD); - } - - // 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(); - - // restore deactivated drawing when quick-loading level tape recording - if (tape.playing && tape.deactivate_display) - TapeDeactivateDisplayOn(); - return result; } -boolean Request(char *text, unsigned int req_state) +int Request(char *text, unsigned int req_state) { boolean overlay_enabled = GetOverlayEnabled(); - boolean result; + int result; - game.request_active_or_moving = TRUE; + game.request_active = TRUE; SetOverlayEnabled(FALSE); + DoRequestBefore(); + if (global.use_envelope_request) result = RequestEnvelope(text, req_state); else result = RequestDoor(text, req_state); + DoRequestAfter(); + SetOverlayEnabled(overlay_enabled); - game.request_active_or_moving = FALSE; + game.request_active = FALSE; return result; } @@ -5140,8 +5354,7 @@ unsigned int MoveDoor(unsigned int door_state) }; static int door1 = DOOR_CLOSE_1; static int door2 = DOOR_CLOSE_2; - unsigned int door_delay = 0; - unsigned int door_delay_value; + DelayCounter door_delay = { 0 }; int i; if (door_state == DOOR_GET_STATE) @@ -5180,6 +5393,7 @@ unsigned int MoveDoor(unsigned int door_state) if (door_state & DOOR_ACTION) { + boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded()); boolean door_panel_drawn[NUM_DOORS]; boolean panel_has_doors[NUM_DOORS]; boolean door_part_skip[MAX_DOOR_PARTS]; @@ -5191,7 +5405,6 @@ unsigned int MoveDoor(unsigned int door_state) int num_move_steps = 0; // number of animation steps for all doors int max_move_delay_doors_only = 0; // delay for doors only (no panel) int num_move_steps_doors_only = 0; // steps for doors only (no panel) - int current_move_delay = 0; int start = 0; int k; @@ -5257,7 +5470,7 @@ unsigned int MoveDoor(unsigned int door_state) num_move_steps = max_move_delay / max_step_delay; num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay; - door_delay_value = max_step_delay; + door_delay.value = max_step_delay; if ((door_state & DOOR_NO_DELAY) || setup.quick_doors) { @@ -5340,7 +5553,7 @@ unsigned int MoveDoor(unsigned int door_state) { int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1); int kk_door = MAX(0, k2_door); - int sync_frame = kk_door * door_delay_value; + int sync_frame = kk_door * door_delay.value; int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame); getFixedGraphicSource(dpc->graphic, frame, &bitmap, @@ -5447,11 +5660,12 @@ unsigned int MoveDoor(unsigned int door_state) if (!(door_state & DOOR_NO_DELAY)) { - BackToFront(); + if (game_ended) + HandleGameActions(); - SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame); + BackToFront(); - current_move_delay += max_step_delay; + SkipUntilDelayReached(&door_delay, &k, last_frame); // prevent OS (Windows) from complaining about program not responding CheckQuitEvent(); @@ -5465,14 +5679,19 @@ unsigned int MoveDoor(unsigned int door_state) { // 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); + 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; + door_delay.value = door_1.post_delay; else if (door_state & DOOR_ACTION_2) - door_delay_value = door_2.post_delay; + door_delay.value = door_2.post_delay; + + while (!DelayReached(&door_delay)) + { + if (game_ended) + HandleGameActions(); - while (!DelayReached(&door_delay, door_delay_value)) BackToFront(); + } } } @@ -5648,99 +5867,1266 @@ void CreateToolButtons(void) int y = pos->y; int id = i; + // do not use touch buttons if overlay touch buttons are disabled + if (is_touch_button && !setup.touch.overlay_buttons) + continue; + if (global.use_envelope_request && !is_touch_button) { setRequestPosition(&base_x, &base_y, 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; + // 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 && + pos->draw_player) + { + int player_nr = id - TOOL_CTRL_ID_PLAYER_1; + + getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0, + pos->size, &deco_bitmap, &deco_x, &deco_y); + deco_xpos = (gfx->width - pos->size) / 2; + deco_ypos = (gfx->height - pos->size) / 2; + } + + gi = CreateGadget(GDI_CUSTOM_ID, id, + GDI_IMAGE_ID, graphic, + GDI_INFO_TEXT, toolbutton_info[i].infotext, + GDI_X, base_x + x, + GDI_Y, base_y + y, + GDI_WIDTH, gfx->width, + GDI_HEIGHT, gfx->height, + GDI_TYPE, GD_TYPE_NORMAL_BUTTON, + GDI_STATE, GD_BUTTON_UNPRESSED, + GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y, + GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp, + GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y, + GDI_DECORATION_POSITION, deco_xpos, deco_ypos, + GDI_DECORATION_SIZE, pos->size, pos->size, + GDI_DECORATION_SHIFTING, 1, 1, + GDI_DIRECT_DRAW, FALSE, + GDI_OVERLAY_TOUCH_BUTTON, is_touch_button, + GDI_EVENT_MASK, event_mask, + GDI_CALLBACK_ACTION, HandleToolButtons, + GDI_END); + + if (gi == NULL) + Fail("cannot create gadget"); + + tool_gadget[id] = gi; + } +} + +void FreeToolButtons(void) +{ + int i; + + for (i = 0; i < NUM_TOOL_BUTTONS; i++) + FreeGadget(tool_gadget[i]); +} + +static void MapToolButtons(unsigned int req_state) +{ + if (req_state & REQ_ASK) + { + MapGadget(tool_gadget[TOOL_CTRL_ID_YES]); + MapGadget(tool_gadget[TOOL_CTRL_ID_NO]); + MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]); + MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]); + } + else if (req_state & REQ_CONFIRM) + { + MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]); + MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]); + } + else if (req_state & REQ_PLAYER) + { + MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]); + MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]); + MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]); + MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]); + } +} + +static void UnmapToolButtons(void) +{ + int i; + + for (i = 0; i < NUM_TOOL_BUTTONS; i++) + UnmapGadget(tool_gadget[i]); +} + +static void HandleToolButtons(struct GadgetInfo *gi) +{ + request_gadget_id = gi->custom_id; +} + +static struct Mapping_BD_to_RND_object +{ + int element_bd; + boolean is_rnd_to_bd_mapping; // unique mapping BD <-> RND + + int element_rnd; + int action; + int direction; +} +bd_object_mapping_list[] = +{ + // additional RND style elements mapped to BD style elements must be listed first + + { + O_DIRT, TRUE, + EL_SAND, -1, -1 + }, + { + O_STONE, TRUE, + EL_BD_ROCK, -1, -1 + }, + { + O_BRICK, TRUE, + EL_BD_WALL, -1, -1 + }, + { + O_STEEL, TRUE, + EL_STEELWALL, -1, -1 + }, + { + O_DIAMOND, TRUE, + EL_BD_DIAMOND, -1, -1 + }, + { + O_INBOX, TRUE, + EL_PLAYER_1, -1, -1 + }, + { + O_INBOX, TRUE, + EL_PLAYER_2, -1, -1 + }, + { + O_INBOX, TRUE, + EL_PLAYER_3, -1, -1 + }, + { + O_INBOX, TRUE, + EL_PLAYER_4, -1, -1 + }, + { + O_PRE_OUTBOX, TRUE, + EL_EXIT_CLOSED, -1, -1 + }, + + // BD style elements with their corresponding RND style elements + + { + O_SPACE, TRUE, + EL_EMPTY, -1, -1 + }, + { + O_DIRT, TRUE, + EL_BD_SAND, -1, -1 + }, + { + O_DIRT_SLOPED_UP_RIGHT, TRUE, + EL_BD_SAND_SLOPED_UP_RIGHT, -1, -1 + }, + { + O_DIRT_SLOPED_UP_LEFT, TRUE, + EL_BD_SAND_SLOPED_UP_LEFT, -1, -1 + }, + { + O_DIRT_SLOPED_DOWN_LEFT, TRUE, + EL_BD_SAND_SLOPED_DOWN_LEFT, -1, -1 + }, + { + O_DIRT_SLOPED_DOWN_RIGHT, TRUE, + EL_BD_SAND_SLOPED_DOWN_RIGHT, -1, -1 + }, + { + O_DIRT_BALL, TRUE, + EL_BD_SAND_BALL, -1, -1 + }, + { + O_DIRT_BALL_F, FALSE, + EL_BD_SAND_BALL, ACTION_FALLING, -1 + }, + { + O_DIRT_LOOSE, TRUE, + EL_BD_SAND_LOOSE, -1, -1 + }, + { + O_DIRT_LOOSE_F, FALSE, + EL_BD_SAND_LOOSE, ACTION_FALLING, -1 + }, + { + O_DIRT2, TRUE, + EL_BD_SAND_2, -1, -1 + }, + { + O_BRICK, TRUE, + EL_BD_WALL, -1, -1 + }, + { + O_BRICK_SLOPED_UP_RIGHT, TRUE, + EL_BD_WALL_SLOPED_UP_RIGHT, -1, -1 + }, + { + O_BRICK_SLOPED_UP_LEFT, TRUE, + EL_BD_WALL_SLOPED_UP_LEFT, -1, -1 + }, + { + O_BRICK_SLOPED_DOWN_LEFT, TRUE, + EL_BD_WALL_SLOPED_DOWN_LEFT, -1, -1 + }, + { + O_BRICK_SLOPED_DOWN_RIGHT, TRUE, + EL_BD_WALL_SLOPED_DOWN_RIGHT, -1, -1 + }, + { + O_BRICK_NON_SLOPED, TRUE, + EL_BD_WALL_NON_SLOPED, -1, -1 + }, + { + O_MAGIC_WALL, TRUE, + EL_BD_MAGIC_WALL, ACTION_ACTIVE, -1 + }, + { + O_PRE_OUTBOX, TRUE, + EL_BD_EXIT_CLOSED, -1, -1 + }, + { + O_OUTBOX, TRUE, + EL_BD_EXIT_OPEN, -1, -1 + }, + { + O_PRE_INVIS_OUTBOX, TRUE, + EL_BD_INVISIBLE_EXIT_CLOSED, -1, -1 + }, + { + O_INVIS_OUTBOX, TRUE, + EL_BD_INVISIBLE_EXIT_OPEN, -1, -1 + }, + { + O_STEEL, TRUE, + EL_BD_STEELWALL, -1, -1 + }, + { + O_STEEL_SLOPED_UP_RIGHT, TRUE, + EL_BD_STEELWALL_SLOPED_UP_RIGHT, -1, -1 + }, + { + O_STEEL_SLOPED_UP_LEFT, TRUE, + EL_BD_STEELWALL_SLOPED_UP_LEFT, -1, -1 + }, + { + O_STEEL_SLOPED_DOWN_LEFT, TRUE, + EL_BD_STEELWALL_SLOPED_DOWN_LEFT, -1, -1 + }, + { + O_STEEL_SLOPED_DOWN_RIGHT, TRUE, + EL_BD_STEELWALL_SLOPED_DOWN_RIGHT, -1, -1 + }, + { + O_STEEL_EXPLODABLE, TRUE, + EL_BD_STEELWALL_EXPLODABLE, -1, -1 + }, + { + O_STEEL_EATABLE, TRUE, + EL_BD_STEELWALL_DIGGABLE, -1, -1 + }, + { + O_BRICK_EATABLE, TRUE, + EL_BD_WALL_DIGGABLE, -1, -1 + }, + { + O_STONE, TRUE, + EL_BD_ROCK, -1, -1 + }, + { + O_STONE_F, FALSE, + EL_BD_ROCK, ACTION_FALLING, -1 + }, + { + O_FLYING_STONE, TRUE, + EL_BD_FLYING_ROCK, -1, -1 + }, + { + O_FLYING_STONE_F, FALSE, + EL_BD_FLYING_ROCK, ACTION_FALLING, -1 + }, + { + O_MEGA_STONE, TRUE, + EL_BD_MEGA_ROCK, -1, -1 + }, + { + O_MEGA_STONE_F, FALSE, + EL_BD_MEGA_ROCK, ACTION_FALLING, -1 + }, + { + O_DIAMOND, TRUE, + EL_BD_DIAMOND, -1, -1 + }, + { + O_DIAMOND_F, FALSE, + EL_BD_DIAMOND, ACTION_FALLING, -1 + }, + { + O_FLYING_DIAMOND, TRUE, + EL_BD_FLYING_DIAMOND, -1, -1 + }, + { + O_FLYING_DIAMOND_F, FALSE, + EL_BD_FLYING_DIAMOND, ACTION_FALLING, -1 + }, + { + O_NUT, TRUE, + EL_BD_NUT, -1, -1 + }, + { + O_NUT_F, FALSE, + EL_BD_NUT, ACTION_FALLING, -1 + }, + { + O_BLADDER_SPENDER, TRUE, + EL_BD_BLADDER_SPENDER, -1, -1 + }, + { + O_INBOX, TRUE, + EL_BD_INBOX, -1, -1 + }, + { + O_H_EXPANDING_WALL, TRUE, + EL_BD_EXPANDABLE_WALL_HORIZONTAL, -1, -1 + }, + { + O_V_EXPANDING_WALL, TRUE, + EL_BD_EXPANDABLE_WALL_VERTICAL, -1, -1 + }, + { + O_EXPANDING_WALL, TRUE, + EL_BD_EXPANDABLE_WALL_ANY, -1, -1 + }, + { + O_H_EXPANDING_STEEL_WALL, TRUE, + EL_BD_EXPANDABLE_STEELWALL_HORIZONTAL, -1, -1 + }, + { + O_V_EXPANDING_STEEL_WALL, TRUE, + EL_BD_EXPANDABLE_STEELWALL_VERTICAL, -1, -1 + }, + { + O_EXPANDING_STEEL_WALL, TRUE, + EL_BD_EXPANDABLE_STEELWALL_ANY, -1, -1 + }, + { + O_EXPANDING_WALL_SWITCH, TRUE, + EL_BD_EXPANDABLE_WALL_SWITCH_HORIZONTAL, -1, -1 + }, + { + O_CREATURE_SWITCH, TRUE, + EL_BD_CREATURE_SWITCH, -1, -1 + }, + { + O_BITER_SWITCH, TRUE, + EL_BD_BITER_SWITCH_1, -1, -1 + }, + { + O_REPLICATOR_SWITCH, TRUE, + EL_BD_REPLICATOR_SWITCH, -1, -1 + }, + { + O_CONVEYOR_SWITCH, TRUE, + EL_BD_CONVEYOR_SWITCH, -1, -1 + }, + { + O_CONVEYOR_DIR_SWITCH, TRUE, + EL_BD_CONVEYOR_DIR_SWITCH_RIGHT, -1, -1 + }, + { + O_ACID, TRUE, + EL_BD_ACID, -1, -1 + }, + { + O_FALLING_WALL, TRUE, + EL_BD_FALLING_WALL, -1, -1 + }, + { + O_FALLING_WALL_F, FALSE, + EL_BD_FALLING_WALL, ACTION_FALLING, -1 + }, + { + O_BOX, TRUE, + EL_BD_BOX, -1, -1 + }, + { + O_TIME_PENALTY, TRUE, + EL_BD_TIME_PENALTY, -1, -1 + }, + { + O_GRAVESTONE, TRUE, + EL_BD_GRAVESTONE, -1, -1 + }, + { + O_STONE_GLUED, TRUE, + EL_BD_ROCK_GLUED, -1, -1 + }, + { + O_DIAMOND_GLUED, TRUE, + EL_BD_DIAMOND_GLUED, -1, -1 + }, + { + O_DIAMOND_KEY, TRUE, + EL_BD_DIAMOND_KEY, -1, -1 + }, + { + O_TRAPPED_DIAMOND, TRUE, + EL_BD_TRAPPED_DIAMOND, -1, -1 + }, + { + O_CLOCK, TRUE, + EL_BD_CLOCK, -1, -1 + }, + { + O_DIRT_GLUED, TRUE, + EL_BD_SAND_GLUED, -1, -1 + }, + { + O_KEY_1, TRUE, + EL_BD_KEY_1, -1, -1 + }, + { + O_KEY_2, TRUE, + EL_BD_KEY_2, -1, -1 + }, + { + O_KEY_3, TRUE, + EL_BD_KEY_3, -1, -1 + }, + { + O_DOOR_1, TRUE, + EL_BD_GATE_1, -1, -1 + }, + { + O_DOOR_2, TRUE, + EL_BD_GATE_2, -1, -1 + }, + { + O_DOOR_3, TRUE, + EL_BD_GATE_3, -1, -1 + }, + { + O_POT, TRUE, + EL_BD_POT, -1, -1 + }, + { + O_GRAVITY_SWITCH, TRUE, + EL_BD_GRAVITY_SWITCH, -1, -1 + }, + { + O_PNEUMATIC_HAMMER, TRUE, + EL_BD_PNEUMATIC_HAMMER, -1, -1 + }, + { + O_TELEPORTER, TRUE, + EL_BD_TELEPORTER, -1, -1 + }, + { + O_SKELETON, TRUE, + EL_BD_SKELETON, -1, -1 + }, + { + O_WATER, TRUE, + EL_BD_WATER, -1, -1 + }, + { + O_WATER_1, FALSE, + EL_BD_WATER, -1, -1 + }, + { + O_WATER_2, FALSE, + EL_BD_WATER, -1, -1 + }, + { + O_WATER_3, FALSE, + EL_BD_WATER, -1, -1 + }, + { + O_WATER_4, FALSE, + EL_BD_WATER, -1, -1 + }, + { + O_WATER_5, FALSE, + EL_BD_WATER, -1, -1 + }, + { + O_WATER_6, FALSE, + EL_BD_WATER, -1, -1 + }, + { + O_WATER_7, FALSE, + EL_BD_WATER, -1, -1 + }, + { + O_WATER_8, FALSE, + EL_BD_WATER, -1, -1 + }, + { + O_WATER_9, FALSE, + EL_BD_WATER, -1, -1 + }, + { + O_WATER_10, FALSE, + EL_BD_WATER, -1, -1 + }, + { + O_WATER_11, FALSE, + EL_BD_WATER, -1, -1 + }, + { + O_WATER_12, FALSE, + EL_BD_WATER, -1, -1 + }, + { + O_WATER_13, FALSE, + EL_BD_WATER, -1, -1 + }, + { + O_WATER_14, FALSE, + EL_BD_WATER, -1, -1 + }, + { + O_WATER_15, FALSE, + EL_BD_WATER, -1, -1 + }, + { + O_WATER_16, FALSE, + EL_BD_WATER, -1, -1 + }, + { + O_COW_1, TRUE, + EL_BD_COW_LEFT, -1, -1 + }, + { + O_COW_2, TRUE, + EL_BD_COW_UP, -1, -1 + }, + { + O_COW_3, TRUE, + EL_BD_COW_RIGHT, -1, -1 + }, + { + O_COW_4, TRUE, + EL_BD_COW_DOWN, -1, -1 + }, + { + O_COW_ENCLOSED_1, FALSE, + EL_BD_COW_DOWN, -1, -1 + }, + { + O_COW_ENCLOSED_2, FALSE, + EL_BD_COW_DOWN, -1, -1 + }, + { + O_COW_ENCLOSED_3, FALSE, + EL_BD_COW_DOWN, -1, -1 + }, + { + O_COW_ENCLOSED_4, FALSE, + EL_BD_COW_DOWN, -1, -1 + }, + { + O_COW_ENCLOSED_5, FALSE, + EL_BD_COW_DOWN, -1, -1 + }, + { + O_COW_ENCLOSED_6, FALSE, + EL_BD_COW_DOWN, -1, -1 + }, + { + O_COW_ENCLOSED_7, FALSE, + EL_BD_COW_DOWN, -1, -1 + }, + { + O_WALLED_DIAMOND, TRUE, + EL_BD_WALL_DIAMOND, -1, -1 + }, + { + O_WALLED_KEY_1, TRUE, + EL_BD_WALL_KEY_1, -1, -1 + }, + { + O_WALLED_KEY_2, TRUE, + EL_BD_WALL_KEY_2, -1, -1 + }, + { + O_WALLED_KEY_3, TRUE, + EL_BD_WALL_KEY_3, -1, -1 + }, + { + O_AMOEBA, TRUE, + EL_BD_AMOEBA, -1, -1 + }, + { + O_AMOEBA_2, TRUE, + EL_BD_AMOEBA_2, -1, -1 + }, + { + O_REPLICATOR, TRUE, + EL_BD_REPLICATOR, -1, -1 + }, + { + O_CONVEYOR_LEFT, TRUE, + EL_BD_CONVEYOR_LEFT, -1, -1 + }, + { + O_CONVEYOR_RIGHT, TRUE, + EL_BD_CONVEYOR_RIGHT, -1, -1 + }, + { + O_LAVA, TRUE, + EL_BD_LAVA, -1, -1 + }, + { + O_SWEET, TRUE, + EL_BD_SWEET, -1, -1 + }, + { + O_VOODOO, TRUE, + EL_BD_VOODOO_DOLL, -1, -1 + }, + { + O_SLIME, TRUE, + EL_BD_SLIME, -1, -1 + }, + { + O_BLADDER, TRUE, + EL_BD_BLADDER, -1, -1 + }, + { + O_BLADDER_1, FALSE, + EL_BD_BLADDER, -1, -1 + }, + { + O_BLADDER_2, FALSE, + EL_BD_BLADDER, -1, -1 + }, + { + O_BLADDER_3, FALSE, + EL_BD_BLADDER, -1, -1 + }, + { + O_BLADDER_4, FALSE, + EL_BD_BLADDER, -1, -1 + }, + { + O_BLADDER_5, FALSE, + EL_BD_BLADDER, -1, -1 + }, + { + O_BLADDER_6, FALSE, + EL_BD_BLADDER, -1, -1 + }, + { + O_BLADDER_7, FALSE, + EL_BD_BLADDER, -1, -1 + }, + { + O_BLADDER_8, FALSE, + EL_BD_BLADDER, -1, -1 + }, + { + O_WAITING_STONE, TRUE, + EL_BD_WAITING_ROCK, -1, -1 + }, + { + O_CHASING_STONE, TRUE, + EL_BD_CHASING_ROCK, -1, -1 + }, + { + O_GHOST, TRUE, + EL_BD_GHOST, -1, -1 + }, + { + O_FIREFLY_1, TRUE, + EL_BD_FIREFLY_LEFT, -1, -1 + }, + { + O_FIREFLY_2, TRUE, + EL_BD_FIREFLY_UP, -1, -1 + }, + { + O_FIREFLY_3, TRUE, + EL_BD_FIREFLY_RIGHT, -1, -1 + }, + { + O_FIREFLY_4, TRUE, + EL_BD_FIREFLY_DOWN, -1, -1 + }, + { + O_ALT_FIREFLY_1, TRUE, + EL_BD_FIREFLY_2_LEFT, -1, -1 + }, + { + O_ALT_FIREFLY_2, TRUE, + EL_BD_FIREFLY_2_UP, -1, -1 + }, + { + O_ALT_FIREFLY_3, TRUE, + EL_BD_FIREFLY_2_RIGHT, -1, -1 + }, + { + O_ALT_FIREFLY_4, TRUE, + EL_BD_FIREFLY_2_DOWN, -1, -1 + }, + { + O_BUTTER_1, TRUE, + EL_BD_BUTTERFLY_LEFT, -1, -1 + }, + { + O_BUTTER_2, TRUE, + EL_BD_BUTTERFLY_UP, -1, -1 + }, + { + O_BUTTER_3, TRUE, + EL_BD_BUTTERFLY_RIGHT, -1, -1 + }, + { + O_BUTTER_4, TRUE, + EL_BD_BUTTERFLY_DOWN, -1, -1 + }, + { + O_ALT_BUTTER_1, TRUE, + EL_BD_BUTTERFLY_2_LEFT, -1, -1 + }, + { + O_ALT_BUTTER_2, TRUE, + EL_BD_BUTTERFLY_2_UP, -1, -1 + }, + { + O_ALT_BUTTER_3, TRUE, + EL_BD_BUTTERFLY_2_RIGHT, -1, -1 + }, + { + O_ALT_BUTTER_4, TRUE, + EL_BD_BUTTERFLY_2_DOWN, -1, -1 + }, + { + O_STONEFLY_1, TRUE, + EL_BD_STONEFLY_LEFT, -1, -1 + }, + { + O_STONEFLY_2, TRUE, + EL_BD_STONEFLY_UP, -1, -1 + }, + { + O_STONEFLY_3, TRUE, + EL_BD_STONEFLY_RIGHT, -1, -1 + }, + { + O_STONEFLY_4, TRUE, + EL_BD_STONEFLY_DOWN, -1, -1 + }, + { + O_BITER_1, TRUE, + EL_BD_BITER_UP, -1, -1 + }, + { + O_BITER_2, TRUE, + EL_BD_BITER_RIGHT, -1, -1 + }, + { + O_BITER_3, TRUE, + EL_BD_BITER_DOWN, -1, -1 + }, + { + O_BITER_4, TRUE, + EL_BD_BITER_LEFT, -1, -1 + }, + { + O_DRAGONFLY_1, TRUE, + EL_BD_DRAGONFLY_LEFT, -1, -1 + }, + { + O_DRAGONFLY_2, TRUE, + EL_BD_DRAGONFLY_UP, -1, -1 + }, + { + O_DRAGONFLY_3, TRUE, + EL_BD_DRAGONFLY_RIGHT, -1, -1 + }, + { + O_DRAGONFLY_4, TRUE, + EL_BD_DRAGONFLY_DOWN, -1, -1 + }, + { + O_PRE_PL_1, FALSE, + EL_BD_PLAYER, ACTION_GROWING, -1 + }, + { + O_PRE_PL_2, FALSE, + EL_BD_PLAYER, ACTION_GROWING, -1 + }, + { + O_PRE_PL_3, FALSE, + EL_BD_PLAYER, ACTION_GROWING, -1 + }, + { + O_PLAYER, TRUE, + EL_BD_PLAYER, -1, -1 + }, + { + O_PLAYER_BOMB, TRUE, + EL_BD_PLAYER_WITH_BOMB, -1, -1 + }, + { + O_PLAYER_GLUED, TRUE, + EL_BD_PLAYER_GLUED, -1, -1 + }, + { + O_PLAYER_STIRRING, TRUE, + EL_BD_PLAYER_STIRRING, -1, -1 + }, + { + O_BOMB, TRUE, + EL_BD_BOMB, -1, -1 + }, + { + O_BOMB_TICK_1, FALSE, + EL_BD_BOMB, ACTION_ACTIVE, -1 + }, + { + O_BOMB_TICK_2, FALSE, + EL_BD_BOMB, ACTION_ACTIVE, -1 + }, + { + O_BOMB_TICK_3, FALSE, + EL_BD_BOMB, ACTION_ACTIVE, -1 + }, + { + O_BOMB_TICK_4, FALSE, + EL_BD_BOMB, ACTION_ACTIVE, -1 + }, + { + O_BOMB_TICK_5, FALSE, + EL_BD_BOMB, ACTION_ACTIVE, -1 + }, + { + O_BOMB_TICK_6, FALSE, + EL_BD_BOMB, ACTION_ACTIVE, -1 + }, + { + O_BOMB_TICK_7, FALSE, + EL_BD_BOMB, ACTION_ACTIVE, -1 + }, + { + O_NITRO_PACK, TRUE, + EL_BD_NITRO_PACK, -1, -1 + }, + { + O_NITRO_PACK_F, FALSE, + EL_BD_NITRO_PACK, ACTION_FALLING, -1 + }, + { + O_NITRO_PACK_EXPLODE, FALSE, + EL_BD_NITRO_PACK, ACTION_EXPLODING, -1 + }, + { + O_PRE_CLOCK_1, FALSE, + EL_BD_CLOCK, ACTION_GROWING, -1 + }, + { + O_PRE_CLOCK_2, FALSE, + EL_BD_CLOCK, ACTION_GROWING, -1 + }, + { + O_PRE_CLOCK_3, FALSE, + EL_BD_CLOCK, ACTION_GROWING, -1 + }, + { + O_PRE_CLOCK_4, FALSE, + EL_BD_CLOCK, ACTION_GROWING, -1 + }, + { + O_PRE_DIA_1, FALSE, + EL_BD_DIAMOND, ACTION_GROWING, -1 + }, + { + O_PRE_DIA_2, FALSE, + EL_BD_DIAMOND, ACTION_GROWING, -1 + }, + { + O_PRE_DIA_3, FALSE, + EL_BD_DIAMOND, ACTION_GROWING, -1 + }, + { + O_PRE_DIA_4, FALSE, + EL_BD_DIAMOND, ACTION_GROWING, -1 + }, + { + O_PRE_DIA_5, FALSE, + EL_BD_DIAMOND, ACTION_GROWING, -1 + }, + { + O_EXPLODE_1, FALSE, + EL_DEFAULT, ACTION_EXPLODING, -1 + }, + { + O_EXPLODE_2, FALSE, + EL_DEFAULT, ACTION_EXPLODING, -1 + }, + { + O_EXPLODE_3, FALSE, + EL_DEFAULT, ACTION_EXPLODING, -1 + }, + { + O_EXPLODE_4, FALSE, + EL_DEFAULT, ACTION_EXPLODING, -1 + }, + { + O_EXPLODE_5, FALSE, + EL_DEFAULT, ACTION_EXPLODING, -1 + }, + { + O_PRE_STONE_1, FALSE, + EL_BD_ROCK, ACTION_GROWING, -1 + }, + { + O_PRE_STONE_2, FALSE, + EL_BD_ROCK, ACTION_GROWING, -1 + }, + { + O_PRE_STONE_3, FALSE, + EL_BD_ROCK, ACTION_GROWING, -1 + }, + { + O_PRE_STONE_4, FALSE, + EL_BD_ROCK, ACTION_GROWING, -1 + }, + { + O_PRE_STEEL_1, FALSE, + EL_BD_STEELWALL, ACTION_GROWING, -1 + }, + { + O_PRE_STEEL_2, FALSE, + EL_BD_STEELWALL, ACTION_GROWING, -1 + }, + { + O_PRE_STEEL_3, FALSE, + EL_BD_STEELWALL, ACTION_GROWING, -1 + }, + { + O_PRE_STEEL_4, FALSE, + EL_BD_STEELWALL, ACTION_GROWING, -1 + }, + { + O_GHOST_EXPL_1, FALSE, + EL_BD_GHOST, ACTION_EXPLODING, -1 + }, + { + O_GHOST_EXPL_2, FALSE, + EL_BD_GHOST, ACTION_EXPLODING, -1 + }, + { + O_GHOST_EXPL_3, FALSE, + EL_BD_GHOST, ACTION_EXPLODING, -1 + }, + { + O_GHOST_EXPL_4, FALSE, + EL_BD_GHOST, ACTION_EXPLODING, -1 + }, + { + O_BOMB_EXPL_1, FALSE, + EL_BD_BOMB, ACTION_EXPLODING, -1 + }, + { + O_BOMB_EXPL_2, FALSE, + EL_BD_BOMB, ACTION_EXPLODING, -1 + }, + { + O_BOMB_EXPL_3, FALSE, + EL_BD_BOMB, ACTION_EXPLODING, -1 + }, + { + O_BOMB_EXPL_4, FALSE, + EL_BD_BOMB, ACTION_EXPLODING, -1 + }, + { + O_NITRO_EXPL_1, FALSE, + EL_BD_NITRO_PACK, ACTION_EXPLODING, -1 + }, + { + O_NITRO_EXPL_2, FALSE, + EL_BD_NITRO_PACK, ACTION_EXPLODING, -1 + }, + { + O_NITRO_EXPL_3, FALSE, + EL_BD_NITRO_PACK, ACTION_EXPLODING, -1 + }, + { + O_NITRO_EXPL_4, FALSE, + EL_BD_NITRO_PACK, ACTION_EXPLODING, -1 + }, + { + O_AMOEBA_2_EXPL_1, FALSE, + EL_BD_AMOEBA_2, ACTION_EXPLODING, -1 + }, + { + O_AMOEBA_2_EXPL_2, FALSE, + EL_BD_AMOEBA_2, ACTION_EXPLODING, -1 + }, + { + O_AMOEBA_2_EXPL_3, FALSE, + EL_BD_AMOEBA_2, ACTION_EXPLODING, -1 + }, + { + O_AMOEBA_2_EXPL_4, FALSE, + EL_BD_AMOEBA_2, ACTION_EXPLODING, -1 + }, + { + O_NUT_EXPL_1, FALSE, + EL_BD_NUT, ACTION_BREAKING, -1 + }, + { + O_NUT_EXPL_2, FALSE, + EL_BD_NUT, ACTION_BREAKING, -1 + }, + { + O_NUT_EXPL_3, FALSE, + EL_BD_NUT, ACTION_BREAKING, -1 + }, + { + O_NUT_EXPL_4, FALSE, + EL_BD_NUT, ACTION_BREAKING, -1 + }, + { + O_PLAYER_PNEUMATIC_LEFT, FALSE, + EL_BD_PLAYER, ACTION_HITTING, MV_BIT_LEFT + }, + { + O_PLAYER_PNEUMATIC_RIGHT, FALSE, + EL_BD_PLAYER, ACTION_HITTING, MV_BIT_RIGHT + }, + { + O_PNEUMATIC_ACTIVE_LEFT, TRUE, + EL_BD_PNEUMATIC_HAMMER, ACTION_HITTING, MV_BIT_LEFT + }, + { + O_PNEUMATIC_ACTIVE_RIGHT, TRUE, + EL_BD_PNEUMATIC_HAMMER, ACTION_HITTING, MV_BIT_RIGHT + }, + + // helper (runtime) elements + + { + O_FAKE_BONUS, FALSE, + EL_BD_FAKE_BONUS, -1, -1 + }, + { + O_INBOX_CLOSED, FALSE, + EL_BD_INBOX, -1, -1 + }, + { + O_INBOX_OPEN, FALSE, + EL_BD_INBOX, ACTION_OPENING, -1 + }, + { + O_OUTBOX_CLOSED, FALSE, + EL_BD_EXIT_CLOSED, -1, -1 + }, + { + O_OUTBOX_OPEN, FALSE, + EL_BD_EXIT_OPEN, -1, -1 + }, + { + O_COVERED, FALSE, + EL_BD_COVERED, -1, -1 + }, + { + O_PLAYER_LEFT, FALSE, + EL_BD_PLAYER, ACTION_MOVING, MV_BIT_LEFT + }, + { + O_PLAYER_RIGHT, FALSE, + EL_BD_PLAYER, ACTION_MOVING, MV_BIT_RIGHT + }, + { + O_PLAYER_BLINK, FALSE, + EL_BD_PLAYER, ACTION_BORING_1, -1 + }, + { + O_PLAYER_TAP, FALSE, + EL_BD_PLAYER, ACTION_BORING_2, -1 + }, + { + O_PLAYER_TAP_BLINK, FALSE, + EL_BD_PLAYER, ACTION_BORING_3, -1 + }, + { + O_CREATURE_SWITCH_ON, FALSE, + EL_BD_CREATURE_SWITCH_ACTIVE, -1, -1 + }, + { + O_EXPANDING_WALL_SWITCH_HORIZ, FALSE, + EL_BD_EXPANDABLE_WALL_SWITCH_HORIZONTAL, -1, -1 + }, + { + O_EXPANDING_WALL_SWITCH_VERT, FALSE, + EL_BD_EXPANDABLE_WALL_SWITCH_VERTICAL, -1, -1 + }, + { + O_GRAVITY_SWITCH_ACTIVE, FALSE, + EL_BD_GRAVITY_SWITCH_ACTIVE, -1, -1 + }, + { + O_REPLICATOR_SWITCH_OFF, FALSE, + EL_BD_REPLICATOR_SWITCH, -1, -1 + }, + { + O_REPLICATOR_SWITCH_ON, FALSE, + EL_BD_REPLICATOR_SWITCH_ACTIVE, -1, -1 + }, + { + O_CONVEYOR_DIR_NORMAL, FALSE, + EL_BD_CONVEYOR_DIR_SWITCH_RIGHT, -1, -1 + }, + { + O_CONVEYOR_DIR_CHANGED, FALSE, + EL_BD_CONVEYOR_DIR_SWITCH_LEFT, -1, -1 + }, + { + O_CONVEYOR_SWITCH_OFF, FALSE, + EL_BD_CONVEYOR_SWITCH, -1, -1 + }, + { + O_CONVEYOR_SWITCH_ON, FALSE, + EL_BD_CONVEYOR_SWITCH_ACTIVE, -1, -1 + }, + { + O_MAGIC_WALL_ACTIVE, FALSE, + EL_BD_MAGIC_WALL_ACTIVE, -1, -1 + }, + { + O_REPLICATOR_ACTIVE, FALSE, + EL_BD_REPLICATOR_ACTIVE, -1, -1 + }, + { + O_CONVEYOR_LEFT_ACTIVE, FALSE, + EL_BD_CONVEYOR_LEFT_ACTIVE, -1, -1 + }, + { + O_CONVEYOR_RIGHT_ACTIVE, FALSE, + EL_BD_CONVEYOR_RIGHT_ACTIVE, -1, -1 + }, + { + O_BITER_SWITCH_1, FALSE, + EL_BD_BITER_SWITCH_1, -1, -1 + }, + { + O_BITER_SWITCH_2, FALSE, + EL_BD_BITER_SWITCH_2, -1, -1 + }, + { + O_BITER_SWITCH_3, FALSE, + EL_BD_BITER_SWITCH_3, -1, -1 + }, + { + O_BITER_SWITCH_4, FALSE, + EL_BD_BITER_SWITCH_4, -1, -1 + }, + + { + -1, FALSE, + -1, -1, -1 + } +}; - x = (request.width - 2 * request.border_size - gfx->width) / 2; - y = request.height - 2 * request.border_size - gfx->height * 2; +int map_element_RND_to_BD(int element_rnd) +{ + static unsigned short mapping_RND_to_BD[NUM_FILE_ELEMENTS]; + static boolean mapping_initialized = FALSE; - 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 (!mapping_initialized) + { + int i; - if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4) - { - int player_nr = id - TOOL_CTRL_ID_PLAYER_1; + // return "O_UNKNOWN" for all undefined elements in mapping array + for (i = 0; i < NUM_FILE_ELEMENTS; i++) + mapping_RND_to_BD[i] = O_UNKNOWN; - getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0, - pos->size, &deco_bitmap, &deco_x, &deco_y); - deco_xpos = (gfx->width - pos->size) / 2; - deco_ypos = (gfx->height - pos->size) / 2; - } + for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++) + if (bd_object_mapping_list[i].is_rnd_to_bd_mapping) + mapping_RND_to_BD[bd_object_mapping_list[i].element_rnd] = + bd_object_mapping_list[i].element_bd; - gi = CreateGadget(GDI_CUSTOM_ID, id, - GDI_IMAGE_ID, graphic, - GDI_INFO_TEXT, toolbutton_info[i].infotext, - GDI_X, base_x + x, - GDI_Y, base_y + y, - GDI_WIDTH, gfx->width, - GDI_HEIGHT, gfx->height, - GDI_TYPE, GD_TYPE_NORMAL_BUTTON, - GDI_STATE, GD_BUTTON_UNPRESSED, - GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y, - GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp, - GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y, - GDI_DECORATION_POSITION, deco_xpos, deco_ypos, - GDI_DECORATION_SIZE, pos->size, pos->size, - GDI_DECORATION_SHIFTING, 1, 1, - GDI_DIRECT_DRAW, FALSE, - GDI_OVERLAY_TOUCH_BUTTON, is_touch_button, - GDI_EVENT_MASK, event_mask, - GDI_CALLBACK_ACTION, HandleToolButtons, - GDI_END); + mapping_initialized = TRUE; + } - if (gi == NULL) - Fail("cannot create gadget"); + if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS) + { + Warn("invalid RND element %d", element_rnd); - tool_gadget[id] = gi; + return O_UNKNOWN; } + + return mapping_RND_to_BD[element_rnd]; } -void FreeToolButtons(void) +int map_element_BD_to_RND(int element_bd) { - int i; + static unsigned short mapping_BD_to_RND[O_MAX_ALL]; + static boolean mapping_initialized = FALSE; - for (i = 0; i < NUM_TOOL_BUTTONS; i++) - FreeGadget(tool_gadget[i]); -} + if (!mapping_initialized) + { + int i; -static void UnmapToolButtons(void) -{ - int i; + // return "EL_UNKNOWN" for all undefined elements in mapping array + for (i = 0; i < O_MAX_ALL; i++) + mapping_BD_to_RND[i] = EL_UNKNOWN; - for (i = 0; i < NUM_TOOL_BUTTONS; i++) - UnmapGadget(tool_gadget[i]); -} + for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++) + mapping_BD_to_RND[bd_object_mapping_list[i].element_bd] = + bd_object_mapping_list[i].element_rnd; -static void HandleToolButtons(struct GadgetInfo *gi) -{ - request_gadget_id = gi->custom_id; + mapping_initialized = TRUE; + } + + if (element_bd < 0 || element_bd >= O_MAX_ALL) + { + Warn("invalid BD element %d", element_bd); + + return EL_UNKNOWN; + } + + return mapping_BD_to_RND[element_bd]; } static struct Mapping_EM_to_RND_object @@ -7985,6 +9371,10 @@ int map_element_RND_to_MM(int element_rnd) element_rnd <= EL_MM_END_2 ? EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 : + element_rnd >= EL_MM_START_3 && + element_rnd <= EL_MM_END_3 ? + EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 : + element_rnd >= EL_CHAR_START && element_rnd <= EL_CHAR_END ? EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START : @@ -7993,10 +9383,6 @@ int map_element_RND_to_MM(int element_rnd) 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); } @@ -8014,6 +9400,10 @@ int map_element_MM_to_RND(int element_mm) element_mm <= EL_MM_END_2_NATIVE ? EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE : + element_mm >= EL_MM_START_3_NATIVE && + element_mm <= EL_MM_END_3_NATIVE ? + EL_MM_START_3 + element_mm - EL_MM_START_3_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 : @@ -8022,10 +9412,6 @@ int map_element_MM_to_RND(int element_mm) 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); } @@ -8119,6 +9505,11 @@ int el2img_mm(int element_mm) return el2img(map_element_MM_to_RND(element_mm)); } +int el_act2img_mm(int element_mm, int action) +{ + return el_act2img(map_element_MM_to_RND(element_mm), action); +} + int el_act_dir2img(int element, int action, int direction) { element = GFX_ELEMENT(element); @@ -8334,7 +9725,9 @@ boolean isActivePlayer_EM(int player_nr) unsigned int InitRND(int seed) { - if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + if (level.game_engine_type == GAME_ENGINE_TYPE_BD) + return InitEngineRandom_BD(seed); + else if (level.game_engine_type == GAME_ENGINE_TYPE_EM) return InitEngineRandom_EM(seed); else if (level.game_engine_type == GAME_ENGINE_TYPE_SP) return InitEngineRandom_SP(seed); @@ -8344,14 +9737,15 @@ unsigned int InitRND(int seed) return InitEngineRandom_RND(seed); } -static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX]; -static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX]; +static struct Mapping_BD_to_RND_object bd_object_mapping[O_MAX_ALL]; +static struct Mapping_EM_to_RND_object em_object_mapping[GAME_TILE_MAX]; +static struct Mapping_EM_to_RND_player em_player_mapping[MAX_PLAYERS][PLY_MAX]; static int get_effective_element_EM(int tile, int frame_em) { - int element = object_mapping[tile].element_rnd; - int action = object_mapping[tile].action; - boolean is_backside = object_mapping[tile].is_backside; + int element = em_object_mapping[tile].element_rnd; + int action = em_object_mapping[tile].action; + boolean is_backside = em_object_mapping[tile].is_backside; boolean action_removing = (action == ACTION_DIGGING || action == ACTION_SNAPPING || action == ACTION_COLLECTING); @@ -8499,8 +9893,8 @@ void ResetGfxAnimation_EM(int x, int y, int tile) void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em, int tile, int frame_em, int x, int y) { - int action = object_mapping[tile].action; - int direction = object_mapping[tile].direction; + int action = em_object_mapping[tile].action; + int direction = em_object_mapping[tile].direction; int effective_element = get_effective_element_EM(tile, frame_em); int graphic = (direction == MV_NONE ? el_act2img(effective_element, action) : @@ -8539,11 +9933,11 @@ void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em, } else if (action_moving) { - boolean is_backside = object_mapping[tile].is_backside; + boolean is_backside = em_object_mapping[tile].is_backside; if (is_backside) { - int direction = object_mapping[tile].direction; + int direction = em_object_mapping[tile].direction; int move_dir = (action_falling ? MV_DOWN : direction); GfxFrame[x][y]++; @@ -8576,6 +9970,8 @@ void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em, if (graphic_info[graphic].anim_global_sync) sync_frame = FrameCounter; + else if (graphic_info[graphic].anim_global_anim_sync) + sync_frame = getGlobalAnimSyncFrame(); else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY)) sync_frame = GfxFrame[x][y]; else @@ -8596,9 +9992,9 @@ void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em, void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em, int tile, int frame_em, int x, int y) { - int action = object_mapping[tile].action; - int direction = object_mapping[tile].direction; - boolean is_backside = object_mapping[tile].is_backside; + int action = em_object_mapping[tile].action; + int direction = em_object_mapping[tile].direction; + boolean is_backside = em_object_mapping[tile].is_backside; int effective_element = get_effective_element_EM(tile, frame_em); int effective_action = action; int graphic = (direction == MV_NONE ? @@ -8635,6 +10031,8 @@ void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em, if (graphic_info[graphic].anim_global_sync) sync_frame = FrameCounter; + else if (graphic_info[graphic].anim_global_anim_sync) + sync_frame = getGlobalAnimSyncFrame(); else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY)) sync_frame = GfxFrame[x][y]; else @@ -8660,9 +10058,9 @@ void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em, void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em, int player_nr, int anim, int frame_em) { - int element = player_mapping[player_nr][anim].element_rnd; - int action = player_mapping[player_nr][anim].action; - int direction = player_mapping[player_nr][anim].direction; + int element = em_player_mapping[player_nr][anim].element_rnd; + int action = em_player_mapping[player_nr][anim].action; + int direction = em_player_mapping[player_nr][anim].direction; int graphic = (direction == MV_NONE ? el_act2img(element, action) : el_act_dir2img(element, action, direction)); @@ -8685,6 +10083,96 @@ void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em, &g_em->src_x, &g_em->src_y, FALSE); } +#define BD_GFX_RANGE(a, n, i) ((i) >= (a) && (i) < (a) + (n)) +#define BD_GFX_FRAME(b, i) (((i) - (b)) * 8) + +void InitGraphicInfo_BD(void) +{ + int i, j; + + // always start with reliable default values + for (i = 0; i < O_MAX_ALL; i++) + { + bd_object_mapping[i].element_rnd = EL_UNKNOWN; + bd_object_mapping[i].action = ACTION_DEFAULT; + bd_object_mapping[i].direction = MV_NONE; + } + + for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++) + { + int e = bd_object_mapping_list[i].element_bd; + + bd_object_mapping[e].element_rnd = bd_object_mapping_list[i].element_rnd; + + if (bd_object_mapping_list[i].action != -1) + bd_object_mapping[e].action = bd_object_mapping_list[i].action; + + if (bd_object_mapping_list[i].direction != -1) + bd_object_mapping[e].direction = + MV_DIR_FROM_BIT(bd_object_mapping_list[i].direction); + } + + for (i = 0; i < O_MAX_ALL; i++) + { + int element = bd_object_mapping[i].element_rnd; + int action = bd_object_mapping[i].action; + int direction = bd_object_mapping[i].direction; + + for (j = 0; j < 8; j++) + { + int effective_element = element; + int effective_action = action; + int graphic = (el_act_dir2img(effective_element, effective_action, + direction)); + struct GraphicInfo *g = &graphic_info[graphic]; + struct GraphicInfo_BD *g_bd = &graphic_info_bd_object[i][j]; + Bitmap *src_bitmap; + int src_x, src_y; + int sync_frame = (BD_GFX_RANGE(O_PRE_PL_1, 3, i) ? BD_GFX_FRAME(O_PRE_PL_1, i) : + BD_GFX_RANGE(O_PRE_DIA_1, 5, i) ? BD_GFX_FRAME(O_PRE_DIA_1, i) : + BD_GFX_RANGE(O_PRE_STONE_1, 4, i) ? BD_GFX_FRAME(O_PRE_STONE_1, i) : + BD_GFX_RANGE(O_PRE_STEEL_1, 4, i) ? BD_GFX_FRAME(O_PRE_STEEL_1, i) : + BD_GFX_RANGE(O_BOMB_TICK_1, 7, i) ? BD_GFX_FRAME(O_BOMB_TICK_1, i) : + BD_GFX_RANGE(O_BOMB_EXPL_1, 4, i) ? BD_GFX_FRAME(O_BOMB_EXPL_1, i) : + BD_GFX_RANGE(O_NUT_EXPL_1, 4, i) ? BD_GFX_FRAME(O_NUT_EXPL_1, i) : + BD_GFX_RANGE(O_GHOST_EXPL_1, 4, i) ? BD_GFX_FRAME(O_GHOST_EXPL_1, i) : + BD_GFX_RANGE(O_EXPLODE_1, 5, i) ? BD_GFX_FRAME(O_EXPLODE_1, i) : + BD_GFX_RANGE(O_PRE_CLOCK_1, 4, i) ? BD_GFX_FRAME(O_PRE_CLOCK_1, i) : + BD_GFX_RANGE(O_NITRO_EXPL_1, 4, i) ? BD_GFX_FRAME(O_NITRO_EXPL_1, i) : + BD_GFX_RANGE(O_AMOEBA_2_EXPL_1, 4, i) ? BD_GFX_FRAME(O_AMOEBA_2_EXPL_1, i): + i == O_INBOX_OPEN || i == O_OUTBOX_OPEN ? j : + j * 2); + int frame = getAnimationFrame(g->anim_frames, + g->anim_delay, + g->anim_mode, + g->anim_start_frame, + sync_frame); + + getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE); + + g_bd->bitmap = src_bitmap; + g_bd->src_x = src_x; + g_bd->src_y = src_y; + g_bd->width = TILEX; + g_bd->height = TILEY; + } + } + + // game graphics template for level-specific colors for native BD levels + int graphic = IMG_BD_GAME_GRAPHICS_COLOR_TEMPLATE; + struct GraphicInfo_BD *g_bd = &graphic_info_bd_color_template; + Bitmap *src_bitmap; + int src_x, src_y; + + getGraphicSourceExt(graphic, 0, &src_bitmap, &src_x, &src_y, FALSE); + + g_bd->bitmap = src_bitmap; + g_bd->src_x = src_x; + g_bd->src_y = src_y; + g_bd->width = TILEX; + g_bd->height = TILEY; +} + void InitGraphicInfo_EM(void) { int i, j, p; @@ -8692,10 +10180,10 @@ void InitGraphicInfo_EM(void) // always start with reliable default values for (i = 0; i < GAME_TILE_MAX; i++) { - object_mapping[i].element_rnd = EL_UNKNOWN; - object_mapping[i].is_backside = FALSE; - object_mapping[i].action = ACTION_DEFAULT; - object_mapping[i].direction = MV_NONE; + em_object_mapping[i].element_rnd = EL_UNKNOWN; + em_object_mapping[i].is_backside = FALSE; + em_object_mapping[i].action = ACTION_DEFAULT; + em_object_mapping[i].direction = MV_NONE; } // always start with reliable default values @@ -8703,9 +10191,9 @@ void InitGraphicInfo_EM(void) { for (i = 0; i < PLY_MAX; i++) { - player_mapping[p][i].element_rnd = EL_UNKNOWN; - player_mapping[p][i].action = ACTION_DEFAULT; - player_mapping[p][i].direction = MV_NONE; + em_player_mapping[p][i].element_rnd = EL_UNKNOWN; + em_player_mapping[p][i].action = ACTION_DEFAULT; + em_player_mapping[p][i].direction = MV_NONE; } } @@ -8713,14 +10201,14 @@ void InitGraphicInfo_EM(void) { int e = em_object_mapping_list[i].element_em; - object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd; - object_mapping[e].is_backside = em_object_mapping_list[i].is_backside; + em_object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd; + em_object_mapping[e].is_backside = em_object_mapping_list[i].is_backside; if (em_object_mapping_list[i].action != -1) - object_mapping[e].action = em_object_mapping_list[i].action; + em_object_mapping[e].action = em_object_mapping_list[i].action; if (em_object_mapping_list[i].direction != -1) - object_mapping[e].direction = + em_object_mapping[e].direction = MV_DIR_FROM_BIT(em_object_mapping_list[i].direction); } @@ -8729,22 +10217,22 @@ void InitGraphicInfo_EM(void) int a = em_player_mapping_list[i].action_em; int p = em_player_mapping_list[i].player_nr; - player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd; + em_player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd; if (em_player_mapping_list[i].action != -1) - player_mapping[p][a].action = em_player_mapping_list[i].action; + em_player_mapping[p][a].action = em_player_mapping_list[i].action; if (em_player_mapping_list[i].direction != -1) - player_mapping[p][a].direction = + em_player_mapping[p][a].direction = MV_DIR_FROM_BIT(em_player_mapping_list[i].direction); } for (i = 0; i < GAME_TILE_MAX; i++) { - int element = object_mapping[i].element_rnd; - int action = object_mapping[i].action; - int direction = object_mapping[i].direction; - boolean is_backside = object_mapping[i].is_backside; + int element = em_object_mapping[i].element_rnd; + int action = em_object_mapping[i].action; + int direction = em_object_mapping[i].direction; + boolean is_backside = em_object_mapping[i].is_backside; boolean action_exploding = ((action == ACTION_EXPLODING || action == ACTION_SMASHED_BY_ROCK || action == ACTION_SMASHED_BY_SPRING) && @@ -9016,10 +10504,10 @@ void InitGraphicInfo_EM(void) { for (j = 0; j < 8; j++) { - int element = object_mapping[i].element_rnd; - int action = object_mapping[i].action; - int direction = object_mapping[i].direction; - boolean is_backside = object_mapping[i].is_backside; + int element = em_object_mapping[i].element_rnd; + int action = em_object_mapping[i].action; + int direction = em_object_mapping[i].direction; + boolean is_backside = em_object_mapping[i].is_backside; int graphic_action = el_act_dir2img(element, action, direction); int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction); @@ -9059,9 +10547,9 @@ void InitGraphicInfo_EM(void) { for (i = 0; i < PLY_MAX; i++) { - int element = player_mapping[p][i].element_rnd; - int action = player_mapping[p][i].action; - int direction = player_mapping[p][i].direction; + int element = em_player_mapping[p][i].element_rnd; + int action = em_player_mapping[p][i].action; + int direction = em_player_mapping[p][i].direction; for (j = 0; j < 8; j++) { @@ -9098,7 +10586,7 @@ void InitGraphicInfo_EM(void) } } -static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame, +static void CheckSaveEngineSnapshot_EM(int frame, boolean any_player_moving, boolean any_player_snapping, boolean any_player_dropping) @@ -9155,7 +10643,7 @@ static void CheckSaveEngineSnapshot_MM(boolean element_clicked, } } -boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame, +boolean CheckSingleStepMode_EM(int frame, boolean any_player_moving, boolean any_player_snapping, boolean any_player_dropping) @@ -9164,7 +10652,7 @@ boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame, if (frame == 7 && !any_player_dropping && FrameCounter > 6) TapeTogglePause(TAPE_TOGGLE_AUTOMATIC); - CheckSaveEngineSnapshot_EM(action, frame, any_player_moving, + CheckSaveEngineSnapshot_EM(frame, any_player_moving, any_player_snapping, any_player_dropping); return tape.pausing; @@ -9198,7 +10686,7 @@ void CheckSingleStepMode_MM(boolean element_clicked, } void getGraphicSource_SP(struct GraphicInfo_SP *g_sp, - int graphic, int sync_frame, int x, int y) + int graphic, int sync_frame) { int frame = getGraphicAnimationFrame(graphic, sync_frame); @@ -9215,6 +10703,17 @@ int getGraphicInfo_Delay(int graphic) return graphic_info[graphic].anim_delay; } +boolean getGraphicInfo_NewFrame(int x, int y, int graphic) +{ + if (!IS_NEW_FRAME(GfxFrame[x][y], graphic)) + return FALSE; + + if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC)) + return FALSE; + + return TRUE; +} + void PlayMenuSoundExt(int sound) { if (sound == SND_UNDEFINED) @@ -9537,9 +11036,9 @@ 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); + int gfx_game_mode = getGlobalGameStatus(game_status); + int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT : + gfx_game_mode); struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode]; struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode]; struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode]; @@ -9581,6 +11080,7 @@ void ChangeViewportPropertiesIfNeeded(void) boolean init_gfx_buffers = FALSE; boolean init_video_buffer = FALSE; boolean init_gadgets_and_anims = FALSE; + boolean init_bd_graphics = FALSE; boolean init_em_graphics = FALSE; if (new_win_xsize != WIN_XSIZE || @@ -9679,7 +11179,8 @@ void ChangeViewportPropertiesIfNeeded(void) // changing tile size invalidates scroll values of engine snapshots FreeEngineSnapshotSingle(); - // changing tile size requires update of graphic mapping for EM engine + // changing tile size requires update of graphic mapping for BD/EM engine + init_bd_graphics = TRUE; init_em_graphics = TRUE; } @@ -9746,8 +11247,227 @@ void ChangeViewportPropertiesIfNeeded(void) InitGlobalAnimations(); } + if (init_bd_graphics) + { + InitGraphicInfo_BD(); + } + if (init_em_graphics) { InitGraphicInfo_EM(); } } + +void OpenURL(char *url) +{ +#if SDL_VERSION_ATLEAST(2,0,14) + SDL_OpenURL(url); +#else + Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!", + url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL); + Warn("Please upgrade to at least SDL 2.0.14 for URL support!"); +#endif +} + +void OpenURLFromHash(SetupFileHash *hash, int hash_key) +{ + OpenURL(getHashEntry(hash, int2str(hash_key, 0))); +} + +char *getCurrentLevelsetName(void) +{ + return leveldir_current->name; +} + + +// ============================================================================ +// tests +// ============================================================================ + +#if defined(PLATFORM_WINDOWS) +/* FILETIME of Jan 1 1970 00:00:00. */ +static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL); + +/* + * timezone information is stored outside the kernel so tzp isn't used anymore. + * + * Note: this function is not for Win32 high precision timing purpose. See + * elapsed_time(). + */ +static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp) +{ + FILETIME file_time; + SYSTEMTIME system_time; + ULARGE_INTEGER ularge; + + GetSystemTime(&system_time); + SystemTimeToFileTime(&system_time, &file_time); + ularge.LowPart = file_time.dwLowDateTime; + ularge.HighPart = file_time.dwHighDateTime; + + tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L); + tp->tv_usec = (long) (system_time.wMilliseconds * 1000); + + return 0; +} +#endif + +static char *test_init_uuid_random_function_simple(void) +{ + static char seed_text[100]; + unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE); + + sprintf(seed_text, "%d", seed); + + return seed_text; +} + +static char *test_init_uuid_random_function_better(void) +{ + static char seed_text[100]; + struct timeval current_time; + + gettimeofday(¤t_time, NULL); + + prng_seed_bytes(¤t_time, sizeof(current_time)); + + sprintf(seed_text, "%ld.%ld", + (long)current_time.tv_sec, + (long)current_time.tv_usec); + + return seed_text; +} + +#if defined(PLATFORM_WINDOWS) +static char *test_init_uuid_random_function_better_windows(void) +{ + static char seed_text[100]; + struct timeval current_time; + + gettimeofday_windows(¤t_time, NULL); + + prng_seed_bytes(¤t_time, sizeof(current_time)); + + sprintf(seed_text, "%ld.%ld", + (long)current_time.tv_sec, + (long)current_time.tv_usec); + + return seed_text; +} +#endif + +static unsigned int test_uuid_random_function_simple(int max) +{ + return GetSimpleRandom(max); +} + +static unsigned int test_uuid_random_function_better(int max) +{ + return (max > 0 ? prng_get_uint() % max : 0); +} + +#if defined(PLATFORM_WINDOWS) +#define NUM_UUID_TESTS 3 +#else +#define NUM_UUID_TESTS 2 +#endif + +static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids) +{ + HashTable *hash_seeds = + create_hashtable(get_hash_from_string, hash_key_strings_are_equal, free, NULL); + HashTable *hash_uuids = + create_hashtable(get_hash_from_string, hash_key_strings_are_equal, free, NULL); + static char message[100]; + int i; + + char *random_name = (nr == 0 ? "simple" : "better"); + char *random_type = (always_seed ? "always" : "only once"); + char *(*init_random_function)(void) = + (nr == 0 ? + test_init_uuid_random_function_simple : + test_init_uuid_random_function_better); + unsigned int (*random_function)(int) = + (nr == 0 ? + test_uuid_random_function_simple : + test_uuid_random_function_better); + int xpos = 40; + +#if defined(PLATFORM_WINDOWS) + if (nr == 2) + { + random_name = "windows"; + init_random_function = test_init_uuid_random_function_better_windows; + } +#endif + + ClearField(); + + DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs"); + DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1); + + DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name); + DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type); + DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids); + + DrawTextF(xpos, 180, FC_GREEN, "Please wait ..."); + + BackToFront(); + + // always initialize random number generator at least once + init_random_function(); + + unsigned int time_start = SDL_GetTicks(); + + for (i = 0; i < num_uuids; i++) + { + if (always_seed) + { + char *seed = getStringCopy(init_random_function()); + + hashtable_remove(hash_seeds, seed); + hashtable_insert(hash_seeds, seed, "1"); + } + + char *uuid = getStringCopy(getUUIDExt(random_function)); + + hashtable_remove(hash_uuids, uuid); + hashtable_insert(hash_uuids, uuid, "1"); + } + + int num_unique_seeds = hashtable_count(hash_seeds); + int num_unique_uuids = hashtable_count(hash_uuids); + + unsigned int time_needed = SDL_GetTicks() - time_start; + + DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed); + + DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids); + + if (always_seed) + DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds); + + if (nr == NUM_UUID_TESTS - 1 && always_seed) + DrawTextF(xpos, 300, FC_GREEN, "All tests done!"); + else + DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ..."); + + sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1); + + Request(message, REQ_CONFIRM); + + hashtable_destroy(hash_seeds); + hashtable_destroy(hash_uuids); +} + +void TestGeneratingUUIDs(void) +{ + int num_uuids = 1000000; + int i, j; + + for (i = 0; i < NUM_UUID_TESTS; i++) + for (j = 0; j < 2; j++) + TestGeneratingUUIDs_RunTest(i, j, num_uuids); + + CloseAllAndExit(0); +}