X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=blobdiff_plain;f=src%2Ftools.c;h=79cf0885135681310694fdc4dc562fd40dfa5cf0;hp=27469503dd8eb31ace36c48776bfd56b7934429f;hb=1d7ec87196d24515b3d6e9400c689d9cd48c49f5;hpb=7fde4f8890072581071022bf530a84d7317b739b diff --git a/src/tools.c b/src/tools.c index 27469503..79cf0885 100644 --- a/src/tools.c +++ b/src/tools.c @@ -391,6 +391,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); @@ -462,6 +472,11 @@ void SetDrawtoField(int mode) } } +int GetDrawtoField(void) +{ + return (drawto_field == fieldbuffer ? DRAW_TO_FIELDBUFFER : DRAW_TO_BACKBUFFER); +} + static void RedrawPlayfield_RND(void) { if (game.envelope_active) @@ -497,6 +512,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; @@ -1076,6 +1095,13 @@ void FadeSkipNextFadeOut(void) FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP); } +static int getGlobalGameStatus(int status) +{ + return (status == GAME_MODE_PSEUDO_TYPENAME ? GAME_MODE_MAIN : + status == GAME_MODE_SCOREINFO ? GAME_MODE_SCORES : + status); +} + static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic) { if (graphic == IMG_UNDEFINED) @@ -1098,14 +1124,14 @@ static Bitmap *getGlobalBorderBitmap(int graphic) return getBitmapFromGraphicOrDefault(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); return getGlobalBorderBitmap(graphic); @@ -1401,39 +1427,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]; + static struct XY check[4] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } }; + 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 }; - if (safety > max_fieldx * max_fieldy) - Fail("Something went wrong in 'FloodFill()'. Please debug."); + while (stack_pos > 0) + { + struct XY current = stack_buffer[--stack_pos]; + int i; - old_element = field[from_x][from_y]; - field[from_x][from_y] = fill_element; + field[current.x][current.y] = fill_element; - for (i = 0; i < 4; i++) - { - x = from_x + check[i][0]; - y = from_y + check[i][1]; + for (i = 0; i < 4; i++) + { + int x = current.x + check[i].x; + int y = current.y + check[i].y; - 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); - } + // 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."); - safety--; + if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element) + stack_buffer[stack_pos++] = (struct XY){ x, y }; + } + } } void FloodFillLevel(int from_x, int from_y, int fill_element, @@ -1463,6 +1492,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]; @@ -1471,7 +1534,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)]; } @@ -1517,7 +1580,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); @@ -1544,6 +1607,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) { @@ -1934,22 +2015,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) @@ -2063,8 +2150,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, @@ -2083,6 +2189,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); @@ -2090,9 +2205,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) || @@ -2138,10 +2267,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); } } } @@ -2296,7 +2438,7 @@ 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); } @@ -2377,9 +2519,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)); } @@ -2433,6 +2609,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) @@ -2739,11 +2925,11 @@ 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(); @@ -2805,6 +2991,53 @@ void ShowEnvelope(int envelope_nr) BackToFront(); } +static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy, + int xsize, int ysize) +{ + if (!global.use_envelope_request || + request.sort_priority <= 0) + return; + + if (request.bitmap == NULL || + xsize > request.xsize || + ysize > request.ysize) + { + if (request.bitmap != NULL) + FreeBitmap(request.bitmap); + + request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH); + + SDL_Surface *surface = request.bitmap->surface; + + if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL) + Fail("SDLGetNativeSurface() failed"); + } + + BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0); + + SDLFreeBitmapTextures(request.bitmap); + SDLCreateBitmapTextures(request.bitmap); + + // set envelope request run-time values + request.sx = sx; + request.sy = sy; + request.xsize = xsize; + request.ysize = ysize; +} + +void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage) +{ + 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) + { + BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize, + request.sx, request.sy); + } +} + static void setRequestBasePosition(int *x, int *y) { int sx_base, sy_base; @@ -2955,6 +3188,8 @@ static void DrawEnvelopeRequest(char *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); + PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height); + if (text_door_style) free(text_door_style); } @@ -3036,6 +3271,8 @@ static void AnimateEnvelopeRequest(int anim_mode, int action) } } + PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height); + redraw_mask |= REDRAW_FIELD; BackToFront(); @@ -3347,8 +3584,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) { @@ -3491,7 +3728,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); @@ -3656,10 +3893,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); @@ -3681,12 +3918,23 @@ void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y, 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); } @@ -3694,12 +3942,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); } @@ -3722,9 +3982,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 @@ -3958,7 +4224,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; @@ -4070,7 +4336,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); @@ -4208,10 +4474,11 @@ void WaitForEventToContinue(void) #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()); + int draw_buffer_last = GetDrawtoField(); int width = request.width; int height = request.height; int sx, sy; @@ -4232,11 +4499,11 @@ static int RequestHandleEvents(unsigned int req_state) while (result < 0) { + boolean event_handled = FALSE; + if (game_just_ended) { - // the MM game engine does not use a special (scrollable) field buffer - if (level.game_engine_type != GAME_ENGINE_TYPE_MM) - SetDrawtoField(DRAW_TO_FIELDBUFFER); + SetDrawtoField(draw_buffer_game); HandleGameActions(); @@ -4255,6 +4522,8 @@ static int RequestHandleEvents(unsigned int req_state) while (NextValidEvent(&event)) { + event_handled = TRUE; + switch (event.type) { case EVENT_BUTTONPRESS: @@ -4509,18 +4778,25 @@ static int RequestHandleEvents(unsigned int req_state) } } - if (game_just_ended) + if (event_handled) { - if (global.use_envelope_request) + if (game_just_ended) { - // copy back current state of pressed buttons inside request area - BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy); + 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; @@ -4528,6 +4804,7 @@ static int RequestHandleEvents(unsigned int req_state) static boolean RequestDoor(char *text, unsigned int req_state) { + int draw_buffer_last = GetDrawtoField(); unsigned int old_door_state; int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN; int font_nr = FONT_TEXT_2; @@ -4669,7 +4946,7 @@ static boolean RequestDoor(char *text, unsigned int req_state) SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1); // ---------- handle request buttons ---------- - result = RequestHandleEvents(req_state); + result = RequestHandleEvents(req_state, draw_buffer_last); UnmapToolButtons(); @@ -4710,6 +4987,7 @@ static boolean RequestDoor(char *text, unsigned int req_state) static boolean RequestEnvelope(char *text, unsigned int req_state) { + int draw_buffer_last = GetDrawtoField(); int result; if (game_status == GAME_MODE_PLAYING) @@ -4761,7 +5039,7 @@ static boolean RequestEnvelope(char *text, unsigned int req_state) SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1); // ---------- handle request buttons ---------- - result = RequestHandleEvents(req_state); + result = RequestHandleEvents(req_state, draw_buffer_last); UnmapToolButtons(); @@ -4798,6 +5076,8 @@ boolean Request(char *text, unsigned int req_state) boolean overlay_enabled = GetOverlayEnabled(); boolean result; + game.request_active_or_moving = TRUE; + SetOverlayEnabled(FALSE); if (global.use_envelope_request) @@ -4807,6 +5087,8 @@ boolean Request(char *text, unsigned int req_state) SetOverlayEnabled(overlay_enabled); + game.request_active_or_moving = FALSE; + return result; } @@ -5121,7 +5403,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; @@ -5381,8 +5662,6 @@ unsigned int MoveDoor(unsigned int door_state) SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame); - current_move_delay += max_step_delay; - // prevent OS (Windows) from complaining about program not responding CheckQuitEvent(); } @@ -5578,6 +5857,10 @@ 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); @@ -9091,7 +9374,7 @@ boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame, boolean any_player_dropping) { if (tape.single_step && tape.recording && !tape.pausing) - if (frame == 7 && !any_player_dropping) + if (frame == 7 && !any_player_dropping && FrameCounter > 6) TapeTogglePause(TAPE_TOGGLE_AUTOMATIC); CheckSaveEngineSnapshot_EM(action, frame, any_player_moving, @@ -9351,6 +9634,8 @@ void SetAnimStatus(int anim_status_new) { if (anim_status_new == GAME_MODE_MAIN) anim_status_new = GAME_MODE_PSEUDO_MAINONLY; + else if (anim_status_new == GAME_MODE_NAMES) + anim_status_new = GAME_MODE_PSEUDO_NAMESONLY; else if (anim_status_new == GAME_MODE_SCORES) anim_status_new = GAME_MODE_PSEUDO_SCORESOLD; @@ -9360,7 +9645,11 @@ void SetAnimStatus(int anim_status_new) if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY && global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) || (global.anim_status == GAME_MODE_PSEUDO_TYPENAME && - global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY)) + global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) || + (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY && + global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) || + (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES && + global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY)) global.anim_status = global.anim_status_next; } @@ -9461,9 +9750,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]; @@ -9675,3 +9964,206 @@ void ChangeViewportPropertiesIfNeeded(void) InitGraphicInfo_EM(); } } + +void OpenURL(char *url) +{ + SDL_OpenURL(url); +} + +void OpenURLFromHash(SetupFileHash *hash, int hash_key) +{ + OpenURL(getHashEntry(hash, int2str(hash_key, 0))); +} + + +// ============================================================================ +// 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) +{ + struct hashtable *hash_seeds = + create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal); + struct hashtable *hash_uuids = + create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal); + 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, 0); + hashtable_destroy(hash_uuids, 0); +} + +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); +}