1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 #define DEBUG_FRAME_TIME FALSE
28 // tool button identifiers
29 #define TOOL_CTRL_ID_YES 0
30 #define TOOL_CTRL_ID_NO 1
31 #define TOOL_CTRL_ID_CONFIRM 2
32 #define TOOL_CTRL_ID_PLAYER_1 3
33 #define TOOL_CTRL_ID_PLAYER_2 4
34 #define TOOL_CTRL_ID_PLAYER_3 5
35 #define TOOL_CTRL_ID_PLAYER_4 6
36 #define TOOL_CTRL_ID_TOUCH_YES 7
37 #define TOOL_CTRL_ID_TOUCH_NO 8
38 #define TOOL_CTRL_ID_TOUCH_CONFIRM 9
40 #define NUM_TOOL_BUTTONS 10
42 // constants for number of doors and door parts
44 #define NUM_PANELS NUM_DOORS
45 // #define NUM_PANELS 0
46 #define MAX_PARTS_PER_DOOR 8
47 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
48 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
51 struct DoorPartOrderInfo
57 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
59 struct DoorPartControlInfo
63 struct DoorPartPosInfo *pos;
66 static struct DoorPartControlInfo door_part_controls[] =
70 IMG_GFX_DOOR_1_PART_1,
75 IMG_GFX_DOOR_1_PART_2,
80 IMG_GFX_DOOR_1_PART_3,
85 IMG_GFX_DOOR_1_PART_4,
90 IMG_GFX_DOOR_1_PART_5,
95 IMG_GFX_DOOR_1_PART_6,
100 IMG_GFX_DOOR_1_PART_7,
105 IMG_GFX_DOOR_1_PART_8,
111 IMG_GFX_DOOR_2_PART_1,
116 IMG_GFX_DOOR_2_PART_2,
121 IMG_GFX_DOOR_2_PART_3,
126 IMG_GFX_DOOR_2_PART_4,
131 IMG_GFX_DOOR_2_PART_5,
136 IMG_GFX_DOOR_2_PART_6,
141 IMG_GFX_DOOR_2_PART_7,
146 IMG_GFX_DOOR_2_PART_8,
152 IMG_BACKGROUND_PANEL,
168 static struct XY xy_topdown[] =
177 // forward declaration for internal use
178 static void UnmapToolButtons(void);
179 static void HandleToolButtons(struct GadgetInfo *);
180 static int el_act_dir2crm(int, int, int);
181 static int el_act2crm(int, int);
183 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
184 static int request_gadget_id = -1;
186 static char *print_if_not_empty(int element)
188 static char *s = NULL;
189 char *token_name = element_info[element].token_name;
194 s = checked_malloc(strlen(token_name) + 10 + 1);
196 if (element != EL_EMPTY)
197 sprintf(s, "%d\t['%s']", element, token_name);
199 sprintf(s, "%d", element);
204 int getFieldbufferOffsetX_RND(int dir, int pos)
206 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
207 int dx = (dir & MV_HORIZONTAL ? pos : 0);
208 int dx_var = dx * TILESIZE_VAR / TILESIZE;
211 if (EVEN(SCR_FIELDX))
213 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
214 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
216 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
217 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
219 fx += (dx_var > 0 ? TILEX_VAR : 0);
226 if (full_lev_fieldx <= SCR_FIELDX)
228 if (EVEN(SCR_FIELDX))
229 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
231 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
237 int getFieldbufferOffsetY_RND(int dir, int pos)
239 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
240 int dy = (dir & MV_VERTICAL ? pos : 0);
241 int dy_var = dy * TILESIZE_VAR / TILESIZE;
244 if (EVEN(SCR_FIELDY))
246 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
247 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
249 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
250 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
252 fy += (dy_var > 0 ? TILEY_VAR : 0);
259 if (full_lev_fieldy <= SCR_FIELDY)
261 if (EVEN(SCR_FIELDY))
262 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
264 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
270 static int getLevelFromScreenX_RND(int sx)
272 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
275 int lx = LEVELX((px + dx) / TILESIZE_VAR);
280 static int getLevelFromScreenY_RND(int sy)
282 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
285 int ly = LEVELY((py + dy) / TILESIZE_VAR);
290 static int getLevelFromScreenX_EM(int sx)
292 int level_xsize = level.native_em_level->cav->width;
293 int full_xsize = level_xsize * TILESIZE_VAR;
295 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
297 int fx = getFieldbufferOffsetX_EM();
300 int lx = LEVELX((px + dx) / TILESIZE_VAR);
305 static int getLevelFromScreenY_EM(int sy)
307 int level_ysize = level.native_em_level->cav->height;
308 int full_ysize = level_ysize * TILESIZE_VAR;
310 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
312 int fy = getFieldbufferOffsetY_EM();
315 int ly = LEVELY((py + dy) / TILESIZE_VAR);
320 static int getLevelFromScreenX_SP(int sx)
322 int menBorder = setup.sp_show_border_elements;
323 int level_xsize = level.native_sp_level->width;
324 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
326 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
328 int fx = getFieldbufferOffsetX_SP();
331 int lx = LEVELX((px + dx) / TILESIZE_VAR);
336 static int getLevelFromScreenY_SP(int sy)
338 int menBorder = setup.sp_show_border_elements;
339 int level_ysize = level.native_sp_level->height;
340 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
342 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
344 int fy = getFieldbufferOffsetY_SP();
347 int ly = LEVELY((py + dy) / TILESIZE_VAR);
352 static int getLevelFromScreenX_MM(int sx)
354 int level_xsize = level.native_mm_level->fieldx;
355 int full_xsize = level_xsize * TILESIZE_VAR;
357 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
360 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
365 static int getLevelFromScreenY_MM(int sy)
367 int level_ysize = level.native_mm_level->fieldy;
368 int full_ysize = level_ysize * TILESIZE_VAR;
370 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
373 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
378 int getLevelFromScreenX(int x)
380 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
381 return getLevelFromScreenX_EM(x);
382 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
383 return getLevelFromScreenX_SP(x);
384 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
385 return getLevelFromScreenX_MM(x);
387 return getLevelFromScreenX_RND(x);
390 int getLevelFromScreenY(int y)
392 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
393 return getLevelFromScreenY_EM(y);
394 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
395 return getLevelFromScreenY_SP(y);
396 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
397 return getLevelFromScreenY_MM(y);
399 return getLevelFromScreenY_RND(y);
402 int getScreenFieldSizeX(void)
404 return (tape.playing ? tape.scr_fieldx : SCR_FIELDX);
407 int getScreenFieldSizeY(void)
409 return (tape.playing ? tape.scr_fieldy : SCR_FIELDY);
412 void DumpTile(int x, int y)
419 Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
422 if (!IN_LEV_FIELD(x, y))
424 Info("(not in level field)");
430 token_name = element_info[Tile[x][y]].token_name;
432 Info("Tile: %d\t['%s']", Tile[x][y], token_name);
433 Info("Back: %s", print_if_not_empty(Back[x][y]));
434 Info("Store: %s", print_if_not_empty(Store[x][y]));
435 Info("Store2: %s", print_if_not_empty(Store2[x][y]));
436 Info("StorePlayer: %s", print_if_not_empty(StorePlayer[x][y]));
437 Info("MovPos: %d", MovPos[x][y]);
438 Info("MovDir: %d", MovDir[x][y]);
439 Info("MovDelay: %d", MovDelay[x][y]);
440 Info("ChangeDelay: %d", ChangeDelay[x][y]);
441 Info("CustomValue: %d", CustomValue[x][y]);
442 Info("GfxElement: %d", GfxElement[x][y]);
443 Info("GfxAction: %d", GfxAction[x][y]);
444 Info("GfxFrame: %d [%d]", GfxFrame[x][y], FrameCounter);
445 Info("Player x/y: %d, %d", local_player->jx, local_player->jy);
449 void DumpTileFromScreen(int sx, int sy)
451 int lx = getLevelFromScreenX(sx);
452 int ly = getLevelFromScreenY(sy);
457 void SetDrawtoField(int mode)
459 if (mode == DRAW_TO_FIELDBUFFER)
465 BX2 = SCR_FIELDX + 1;
466 BY2 = SCR_FIELDY + 1;
468 drawto_field = fieldbuffer;
470 else // DRAW_TO_BACKBUFFER
476 BX2 = SCR_FIELDX - 1;
477 BY2 = SCR_FIELDY - 1;
479 drawto_field = backbuffer;
483 int GetDrawtoField(void)
485 return (drawto_field == fieldbuffer ? DRAW_TO_FIELDBUFFER : DRAW_TO_BACKBUFFER);
488 static void RedrawPlayfield_RND(void)
490 if (game.envelope_active)
493 DrawLevel(REDRAW_ALL);
497 void RedrawPlayfield(void)
499 if (game_status != GAME_MODE_PLAYING)
502 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
503 RedrawPlayfield_EM(TRUE);
504 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
505 RedrawPlayfield_SP(TRUE);
506 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
507 RedrawPlayfield_MM();
508 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
509 RedrawPlayfield_RND();
511 BlitScreenToBitmap(backbuffer);
513 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
517 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
520 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
521 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
523 // may happen for "border.draw_masked.*" with undefined "global.border.*"
524 if (src_bitmap == NULL)
527 if (x == -1 && y == -1)
530 if (draw_target == DRAW_TO_SCREEN)
531 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
533 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
536 static void DrawMaskedBorderExt_FIELD(int draw_target)
538 if (global.border_status >= GAME_MODE_MAIN &&
539 global.border_status <= GAME_MODE_PLAYING &&
540 border.draw_masked[global.border_status])
541 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
545 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
547 // when drawing to backbuffer, never draw border over open doors
548 if (draw_target == DRAW_TO_BACKBUFFER &&
549 (GetDoorState() & DOOR_OPEN_1))
552 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
553 (global.border_status != GAME_MODE_EDITOR ||
554 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
555 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
558 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
560 // when drawing to backbuffer, never draw border over open doors
561 if (draw_target == DRAW_TO_BACKBUFFER &&
562 (GetDoorState() & DOOR_OPEN_2))
565 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
566 global.border_status != GAME_MODE_EDITOR)
567 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
570 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
572 // currently not available
575 static void DrawMaskedBorderExt_ALL(int draw_target)
577 DrawMaskedBorderExt_FIELD(draw_target);
578 DrawMaskedBorderExt_DOOR_1(draw_target);
579 DrawMaskedBorderExt_DOOR_2(draw_target);
580 DrawMaskedBorderExt_DOOR_3(draw_target);
583 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
585 // never draw masked screen borders on borderless screens
586 if (global.border_status == GAME_MODE_LOADING ||
587 global.border_status == GAME_MODE_TITLE)
590 if (redraw_mask & REDRAW_ALL)
591 DrawMaskedBorderExt_ALL(draw_target);
594 if (redraw_mask & REDRAW_FIELD)
595 DrawMaskedBorderExt_FIELD(draw_target);
596 if (redraw_mask & REDRAW_DOOR_1)
597 DrawMaskedBorderExt_DOOR_1(draw_target);
598 if (redraw_mask & REDRAW_DOOR_2)
599 DrawMaskedBorderExt_DOOR_2(draw_target);
600 if (redraw_mask & REDRAW_DOOR_3)
601 DrawMaskedBorderExt_DOOR_3(draw_target);
605 void DrawMaskedBorder_FIELD(void)
607 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
610 void DrawMaskedBorder(int redraw_mask)
612 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
615 void DrawMaskedBorderToTarget(int draw_target)
617 if (draw_target == DRAW_TO_BACKBUFFER ||
618 draw_target == DRAW_TO_SCREEN)
620 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
624 int last_border_status = global.border_status;
626 if (draw_target == DRAW_TO_FADE_SOURCE)
628 global.border_status = gfx.fade_border_source_status;
629 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
631 else if (draw_target == DRAW_TO_FADE_TARGET)
633 global.border_status = gfx.fade_border_target_status;
634 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
637 // always use global border for PLAYING when restarting the game
638 if (global.border_status == GAME_MODE_PSEUDO_RESTARTING)
639 global.border_status = GAME_MODE_PLAYING;
641 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
643 global.border_status = last_border_status;
644 gfx.masked_border_bitmap_ptr = backbuffer;
648 void DrawTileCursor(int draw_target)
650 DrawTileCursor_MM(draw_target, game_status == GAME_MODE_PLAYING);
653 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
655 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
658 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
660 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
661 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
663 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
666 void BlitScreenToBitmap(Bitmap *target_bitmap)
668 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
669 BlitScreenToBitmap_EM(target_bitmap);
670 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
671 BlitScreenToBitmap_SP(target_bitmap);
672 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
673 BlitScreenToBitmap_MM(target_bitmap);
674 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
675 BlitScreenToBitmap_RND(target_bitmap);
677 redraw_mask |= REDRAW_FIELD;
680 static void DrawFramesPerSecond(void)
683 int font_nr = FONT_TEXT_2;
684 int font_width = getFontWidth(font_nr);
685 int draw_deactivation_mask = GetDrawDeactivationMask();
686 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
688 // draw FPS with leading space (needed if field buffer deactivated)
689 sprintf(text, " %04.1f fps", global.frames_per_second);
691 // override draw deactivation mask (required for invisible warp mode)
692 SetDrawDeactivationMask(REDRAW_NONE);
694 // draw opaque FPS if field buffer deactivated, else draw masked FPS
695 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
696 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
698 // set draw deactivation mask to previous value
699 SetDrawDeactivationMask(draw_deactivation_mask);
701 // force full-screen redraw in this frame
702 redraw_mask = REDRAW_ALL;
706 static void PrintFrameTimeDebugging(void)
708 static unsigned int last_counter = 0;
709 unsigned int counter = Counter();
710 int diff_1 = counter - last_counter;
711 int diff_2 = diff_1 - GAME_FRAME_DELAY;
713 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
714 char diff_bar[2 * diff_2_max + 5];
718 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
720 for (i = 0; i < diff_2_max; i++)
721 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
722 i >= diff_2_max - diff_2_cut ? '-' : ' ');
724 diff_bar[pos++] = '|';
726 for (i = 0; i < diff_2_max; i++)
727 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
729 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
731 diff_bar[pos++] = '\0';
733 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
736 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
739 last_counter = counter;
743 static int unifiedRedrawMask(int mask)
745 if (mask & REDRAW_ALL)
748 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
754 static boolean equalRedrawMasks(int mask_1, int mask_2)
756 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
759 void BackToFront(void)
761 static int last_redraw_mask = REDRAW_NONE;
763 // force screen redraw in every frame to continue drawing global animations
764 // (but always use the last redraw mask to prevent unwanted side effects)
765 if (redraw_mask == REDRAW_NONE)
766 redraw_mask = last_redraw_mask;
768 last_redraw_mask = redraw_mask;
771 // masked border now drawn immediately when blitting backbuffer to window
773 // draw masked border to all viewports, if defined
774 DrawMaskedBorder(redraw_mask);
777 // draw frames per second (only if debug mode is enabled)
778 if (redraw_mask & REDRAW_FPS)
779 DrawFramesPerSecond();
781 // remove playfield redraw before potentially merging with doors redraw
782 if (DrawingDeactivated(REAL_SX, REAL_SY))
783 redraw_mask &= ~REDRAW_FIELD;
785 // redraw complete window if both playfield and (some) doors need redraw
786 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
787 redraw_mask = REDRAW_ALL;
789 /* although redrawing the whole window would be fine for normal gameplay,
790 being able to only redraw the playfield is required for deactivating
791 certain drawing areas (mainly playfield) to work, which is needed for
792 warp-forward to be fast enough (by skipping redraw of most frames) */
794 if (redraw_mask & REDRAW_ALL)
796 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
798 else if (redraw_mask & REDRAW_FIELD)
800 BlitBitmap(backbuffer, window,
801 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
803 else if (redraw_mask & REDRAW_DOORS)
805 // merge door areas to prevent calling screen redraw more than once
811 if (redraw_mask & REDRAW_DOOR_1)
815 x2 = MAX(x2, DX + DXSIZE);
816 y2 = MAX(y2, DY + DYSIZE);
819 if (redraw_mask & REDRAW_DOOR_2)
823 x2 = MAX(x2, VX + VXSIZE);
824 y2 = MAX(y2, VY + VYSIZE);
827 if (redraw_mask & REDRAW_DOOR_3)
831 x2 = MAX(x2, EX + EXSIZE);
832 y2 = MAX(y2, EY + EYSIZE);
835 // make sure that at least one pixel is blitted, and inside the screen
836 // (else nothing is blitted, causing the animations not to be updated)
837 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
838 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
839 x2 = MIN(MAX(1, x2), WIN_XSIZE);
840 y2 = MIN(MAX(1, y2), WIN_YSIZE);
842 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
845 redraw_mask = REDRAW_NONE;
848 PrintFrameTimeDebugging();
852 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
854 unsigned int frame_delay_value_old = GetVideoFrameDelay();
856 SetVideoFrameDelay(frame_delay_value);
860 SetVideoFrameDelay(frame_delay_value_old);
863 static int fade_type_skip = FADE_TYPE_NONE;
865 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
867 void (*draw_border_function)(void) = NULL;
868 int x, y, width, height;
869 int fade_delay, post_delay;
871 if (fade_type == FADE_TYPE_FADE_OUT)
873 if (fade_type_skip != FADE_TYPE_NONE)
875 // skip all fade operations until specified fade operation
876 if (fade_type & fade_type_skip)
877 fade_type_skip = FADE_TYPE_NONE;
882 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
886 redraw_mask |= fade_mask;
888 if (fade_type == FADE_TYPE_SKIP)
890 fade_type_skip = fade_mode;
895 fade_delay = fading.fade_delay;
896 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
898 if (fade_type_skip != FADE_TYPE_NONE)
900 // skip all fade operations until specified fade operation
901 if (fade_type & fade_type_skip)
902 fade_type_skip = FADE_TYPE_NONE;
907 if (global.autoplay_leveldir)
912 if (fade_mask == REDRAW_FIELD)
917 height = FADE_SYSIZE;
919 if (border.draw_masked_when_fading)
920 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
922 DrawMaskedBorder_FIELD(); // draw once
932 // when switching screens without fading, set fade delay to zero
933 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
936 // do not display black frame when fading out without fade delay
937 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
940 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
941 draw_border_function);
943 redraw_mask &= ~fade_mask;
945 ClearAutoRepeatKeyEvents();
948 static void SetScreenStates_BeforeFadingIn(void)
950 // temporarily set screen mode for animations to screen after fading in
951 global.anim_status = global.anim_status_next;
953 // store backbuffer with all animations that will be started after fading in
954 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
956 // set screen mode for animations back to fading
957 global.anim_status = GAME_MODE_PSEUDO_FADING;
960 static void SetScreenStates_AfterFadingIn(void)
962 // store new source screen (to use correct masked border for fading)
963 gfx.fade_border_source_status = global.border_status;
965 global.anim_status = global.anim_status_next;
968 static void SetScreenStates_BeforeFadingOut(void)
970 // store new target screen (to use correct masked border for fading)
971 gfx.fade_border_target_status = game_status;
973 // set screen mode for animations to fading
974 global.anim_status = GAME_MODE_PSEUDO_FADING;
976 // store backbuffer with all animations that will be stopped for fading out
977 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
980 static void SetScreenStates_AfterFadingOut(void)
982 global.border_status = game_status;
984 // always use global border for PLAYING when restarting the game
985 if (global.border_status == GAME_MODE_PSEUDO_RESTARTING)
986 global.border_status = GAME_MODE_PLAYING;
989 void FadeIn(int fade_mask)
991 SetScreenStates_BeforeFadingIn();
994 DrawMaskedBorder(REDRAW_ALL);
997 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
998 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1000 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1004 FADE_SXSIZE = FULL_SXSIZE;
1005 FADE_SYSIZE = FULL_SYSIZE;
1007 // activate virtual buttons depending on upcoming game status
1008 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1009 game_status == GAME_MODE_PLAYING && !tape.playing)
1010 SetOverlayActive(TRUE);
1012 SetScreenStates_AfterFadingIn();
1014 // force update of global animation status in case of rapid screen changes
1015 redraw_mask = REDRAW_ALL;
1019 void FadeOut(int fade_mask)
1021 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1022 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1023 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1026 SetScreenStates_BeforeFadingOut();
1028 SetTileCursorActive(FALSE);
1029 SetOverlayActive(FALSE);
1032 DrawMaskedBorder(REDRAW_ALL);
1035 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1036 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1038 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1040 SetScreenStates_AfterFadingOut();
1043 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1045 static struct TitleFadingInfo fading_leave_stored;
1048 fading_leave_stored = fading_leave;
1050 fading = fading_leave_stored;
1053 void FadeSetEnterMenu(void)
1055 fading = menu.enter_menu;
1057 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1060 void FadeSetLeaveMenu(void)
1062 fading = menu.leave_menu;
1064 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1067 void FadeSetEnterScreen(void)
1069 fading = menu.enter_screen[game_status];
1071 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1074 void FadeSetNextScreen(void)
1076 fading = menu.next_screen[game_status];
1078 // (do not overwrite fade mode set by FadeSetEnterScreen)
1079 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1082 void FadeSetLeaveScreen(void)
1084 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1087 void FadeSetFromType(int type)
1089 if (type & TYPE_ENTER_SCREEN)
1090 FadeSetEnterScreen();
1091 else if (type & TYPE_ENTER)
1093 else if (type & TYPE_LEAVE)
1097 void FadeSetDisabled(void)
1099 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1101 fading = fading_none;
1104 void FadeSkipNextFadeIn(void)
1106 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1109 void FadeSkipNextFadeOut(void)
1111 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1114 static int getGlobalGameStatus(int status)
1116 return (status == GAME_MODE_PSEUDO_TYPENAME ? GAME_MODE_MAIN :
1117 status == GAME_MODE_SCOREINFO ? GAME_MODE_SCORES :
1121 int getImageFromGraphicOrDefault(int graphic, int default_graphic)
1123 if (graphic == IMG_UNDEFINED)
1124 return IMG_UNDEFINED;
1126 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1128 return (graphic_info[graphic].bitmap != NULL || redefined ?
1129 graphic : default_graphic);
1132 static int getBackgroundImage(int graphic)
1134 return getImageFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1137 static int getGlobalBorderImage(int graphic)
1139 return getImageFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1142 Bitmap *getGlobalBorderBitmapFromStatus(int status_raw)
1144 int status = getGlobalGameStatus(status_raw);
1146 (status == GAME_MODE_MAIN ? IMG_GLOBAL_BORDER_MAIN :
1147 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1148 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1149 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1151 int graphic_final = getGlobalBorderImage(graphic);
1153 return graphic_info[graphic_final].bitmap;
1156 void SetBackgroundImage(int graphic, int redraw_mask)
1158 struct GraphicInfo *g = &graphic_info[graphic];
1159 struct GraphicInfo g_undefined = { 0 };
1161 if (graphic == IMG_UNDEFINED)
1164 // always use original size bitmap for backgrounds, if existing
1165 Bitmap *bitmap = (g->bitmaps != NULL &&
1166 g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] != NULL ?
1167 g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] : g->bitmap);
1169 // remove every mask before setting mask for window, and
1170 // remove window area mask before setting mask for main or door area
1171 int remove_mask = (redraw_mask == REDRAW_ALL ? 0xffff : REDRAW_ALL);
1173 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
1174 SetBackgroundBitmap(NULL, remove_mask, 0, 0, 0, 0); // !!! FIX THIS !!!
1175 SetBackgroundBitmap(bitmap, redraw_mask,
1177 g->width, g->height);
1180 void SetWindowBackgroundImageIfDefined(int graphic)
1182 if (graphic_info[graphic].bitmap)
1183 SetBackgroundImage(graphic, REDRAW_ALL);
1186 void SetMainBackgroundImageIfDefined(int graphic)
1188 if (graphic_info[graphic].bitmap)
1189 SetBackgroundImage(graphic, REDRAW_FIELD);
1192 void SetDoorBackgroundImageIfDefined(int graphic)
1194 if (graphic_info[graphic].bitmap)
1195 SetBackgroundImage(graphic, REDRAW_DOOR_1);
1198 void SetWindowBackgroundImage(int graphic)
1200 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_ALL);
1203 void SetMainBackgroundImage(int graphic)
1205 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_FIELD);
1208 void SetDoorBackgroundImage(int graphic)
1210 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_DOOR_1);
1213 void SetPanelBackground(void)
1215 SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
1218 void DrawBackground(int x, int y, int width, int height)
1220 // "drawto" might still point to playfield buffer here (hall of fame)
1221 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1223 if (IN_GFX_FIELD_FULL(x, y))
1224 redraw_mask |= REDRAW_FIELD;
1225 else if (IN_GFX_DOOR_1(x, y))
1226 redraw_mask |= REDRAW_DOOR_1;
1227 else if (IN_GFX_DOOR_2(x, y))
1228 redraw_mask |= REDRAW_DOOR_2;
1229 else if (IN_GFX_DOOR_3(x, y))
1230 redraw_mask |= REDRAW_DOOR_3;
1233 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1235 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1237 if (font->bitmap == NULL)
1240 DrawBackground(x, y, width, height);
1243 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1245 struct GraphicInfo *g = &graphic_info[graphic];
1247 if (g->bitmap == NULL)
1250 DrawBackground(x, y, width, height);
1253 static int game_status_last = -1;
1254 static Bitmap *global_border_bitmap_last = NULL;
1255 static Bitmap *global_border_bitmap = NULL;
1256 static int real_sx_last = -1, real_sy_last = -1;
1257 static int full_sxsize_last = -1, full_sysize_last = -1;
1258 static int dx_last = -1, dy_last = -1;
1259 static int dxsize_last = -1, dysize_last = -1;
1260 static int vx_last = -1, vy_last = -1;
1261 static int vxsize_last = -1, vysize_last = -1;
1262 static int ex_last = -1, ey_last = -1;
1263 static int exsize_last = -1, eysize_last = -1;
1265 boolean CheckIfGlobalBorderHasChanged(void)
1267 // if game status has not changed, global border has not changed either
1268 if (game_status == game_status_last)
1271 // determine and store new global border bitmap for current game status
1272 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1274 return (global_border_bitmap_last != global_border_bitmap);
1277 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1279 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1280 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1282 // if game status has not changed, nothing has to be redrawn
1283 if (game_status == game_status_last)
1286 // redraw if last screen was title screen
1287 if (game_status_last == GAME_MODE_TITLE)
1290 // redraw if global screen border has changed
1291 if (CheckIfGlobalBorderHasChanged())
1294 // redraw if position or size of playfield area has changed
1295 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1296 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1299 // redraw if position or size of door area has changed
1300 if (dx_last != DX || dy_last != DY ||
1301 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1304 // redraw if position or size of tape area has changed
1305 if (vx_last != VX || vy_last != VY ||
1306 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1309 // redraw if position or size of editor area has changed
1310 if (ex_last != EX || ey_last != EY ||
1311 exsize_last != EXSIZE || eysize_last != EYSIZE)
1318 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1321 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1323 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1326 void RedrawGlobalBorder(void)
1328 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1330 RedrawGlobalBorderFromBitmap(bitmap);
1332 redraw_mask = REDRAW_ALL;
1335 static void RedrawGlobalBorderIfNeeded(void)
1337 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1338 if (game_status == game_status_last)
1342 // copy current draw buffer to later copy back areas that have not changed
1343 if (game_status_last != GAME_MODE_TITLE)
1344 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1346 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1347 if (CheckIfGlobalBorderRedrawIsNeeded())
1349 // determine and store new global border bitmap for current game status
1350 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1353 // redraw global screen border (or clear, if defined to be empty)
1354 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1356 if (game_status == GAME_MODE_EDITOR)
1357 DrawSpecialEditorDoor();
1359 // copy previous playfield and door areas, if they are defined on both
1360 // previous and current screen and if they still have the same size
1362 if (real_sx_last != -1 && real_sy_last != -1 &&
1363 REAL_SX != -1 && REAL_SY != -1 &&
1364 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1365 BlitBitmap(bitmap_db_store_1, backbuffer,
1366 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1369 if (dx_last != -1 && dy_last != -1 &&
1370 DX != -1 && DY != -1 &&
1371 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1372 BlitBitmap(bitmap_db_store_1, backbuffer,
1373 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1375 if (game_status != GAME_MODE_EDITOR)
1377 if (vx_last != -1 && vy_last != -1 &&
1378 VX != -1 && VY != -1 &&
1379 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1380 BlitBitmap(bitmap_db_store_1, backbuffer,
1381 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1385 if (ex_last != -1 && ey_last != -1 &&
1386 EX != -1 && EY != -1 &&
1387 exsize_last == EXSIZE && eysize_last == EYSIZE)
1388 BlitBitmap(bitmap_db_store_1, backbuffer,
1389 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1392 redraw_mask = REDRAW_ALL;
1395 game_status_last = game_status;
1397 global_border_bitmap_last = global_border_bitmap;
1399 real_sx_last = REAL_SX;
1400 real_sy_last = REAL_SY;
1401 full_sxsize_last = FULL_SXSIZE;
1402 full_sysize_last = FULL_SYSIZE;
1405 dxsize_last = DXSIZE;
1406 dysize_last = DYSIZE;
1409 vxsize_last = VXSIZE;
1410 vysize_last = VYSIZE;
1413 exsize_last = EXSIZE;
1414 eysize_last = EYSIZE;
1417 void ClearField(void)
1419 RedrawGlobalBorderIfNeeded();
1421 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1422 // (when entering hall of fame after playing)
1423 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1425 // !!! maybe this should be done before clearing the background !!!
1426 if (game_status == GAME_MODE_PLAYING)
1428 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1429 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1433 SetDrawtoField(DRAW_TO_BACKBUFFER);
1437 void MarkTileDirty(int x, int y)
1439 redraw_mask |= REDRAW_FIELD;
1442 void SetBorderElement(void)
1446 BorderElement = EL_EMPTY;
1448 // only the R'n'D game engine may use an additional steelwall border
1449 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1452 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1454 for (x = 0; x < lev_fieldx; x++)
1456 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1457 BorderElement = EL_STEELWALL;
1459 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1465 void FloodFillLevelExt(int start_x, int start_y, int fill_element,
1466 int max_array_fieldx, int max_array_fieldy,
1467 short field[max_array_fieldx][max_array_fieldy],
1468 int max_fieldx, int max_fieldy)
1470 static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY];
1471 struct XY *check = xy_topdown;
1472 int old_element = field[start_x][start_y];
1475 // do nothing if start field already has the desired content
1476 if (old_element == fill_element)
1479 stack_buffer[stack_pos++] = (struct XY){ start_x, start_y };
1481 while (stack_pos > 0)
1483 struct XY current = stack_buffer[--stack_pos];
1486 field[current.x][current.y] = fill_element;
1488 for (i = 0; i < 4; i++)
1490 int x = current.x + check[i].x;
1491 int y = current.y + check[i].y;
1493 // check for stack buffer overflow (should not happen)
1494 if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY)
1495 Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug.");
1497 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1498 stack_buffer[stack_pos++] = (struct XY){ x, y };
1503 void FloodFillLevel(int from_x, int from_y, int fill_element,
1504 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1505 int max_fieldx, int max_fieldy)
1507 FloodFillLevelExt(from_x, from_y, fill_element,
1508 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1509 max_fieldx, max_fieldy);
1512 void SetRandomAnimationValue(int x, int y)
1514 gfx.anim_random_frame = GfxRandom[x][y];
1517 int getGraphicAnimationFrame(int graphic, int sync_frame)
1519 // animation synchronized with global frame counter, not move position
1520 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1521 sync_frame = FrameCounter;
1522 else if (graphic_info[graphic].anim_global_anim_sync)
1523 sync_frame = getGlobalAnimSyncFrame();
1525 return getAnimationFrame(graphic_info[graphic].anim_frames,
1526 graphic_info[graphic].anim_delay,
1527 graphic_info[graphic].anim_mode,
1528 graphic_info[graphic].anim_start_frame,
1532 int getGraphicAnimationFrameXY(int graphic, int lx, int ly)
1534 if (graphic_info[graphic].anim_mode & ANIM_TILED)
1536 struct GraphicInfo *g = &graphic_info[graphic];
1537 int xsize = MAX(1, g->anim_frames_per_line);
1538 int ysize = MAX(1, g->anim_frames / xsize);
1539 int xoffset = g->anim_start_frame % xsize;
1540 int yoffset = g->anim_start_frame % ysize;
1541 // may be needed if screen field is significantly larger than playfield
1542 int x = (lx + xoffset + SCR_FIELDX * xsize) % xsize;
1543 int y = (ly + yoffset + SCR_FIELDY * ysize) % ysize;
1544 int sync_frame = y * xsize + x;
1546 return sync_frame % g->anim_frames;
1548 else if (graphic_info[graphic].anim_mode & ANIM_RANDOM_STATIC)
1550 struct GraphicInfo *g = &graphic_info[graphic];
1551 // may be needed if screen field is significantly larger than playfield
1552 int x = (lx + SCR_FIELDX * lev_fieldx) % lev_fieldx;
1553 int y = (ly + SCR_FIELDY * lev_fieldy) % lev_fieldy;
1554 int sync_frame = GfxRandomStatic[x][y];
1556 return sync_frame % g->anim_frames;
1560 int sync_frame = (IN_LEV_FIELD(lx, ly) ? GfxFrame[lx][ly] : -1);
1562 return getGraphicAnimationFrame(graphic, sync_frame);
1566 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1568 struct GraphicInfo *g = &graphic_info[graphic];
1569 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1571 if (tilesize == gfx.standard_tile_size)
1572 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1573 else if (tilesize == game.tile_size)
1574 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1576 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1579 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1580 boolean get_backside)
1582 struct GraphicInfo *g = &graphic_info[graphic];
1583 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1584 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1586 if (g->offset_y == 0) // frames are ordered horizontally
1588 int max_width = g->anim_frames_per_line * g->width;
1589 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1591 *x = pos % max_width;
1592 *y = src_y % g->height + pos / max_width * g->height;
1594 else if (g->offset_x == 0) // frames are ordered vertically
1596 int max_height = g->anim_frames_per_line * g->height;
1597 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1599 *x = src_x % g->width + pos / max_height * g->width;
1600 *y = pos % max_height;
1602 else // frames are ordered diagonally
1604 *x = src_x + frame * g->offset_x;
1605 *y = src_y + frame * g->offset_y;
1609 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1610 Bitmap **bitmap, int *x, int *y,
1611 boolean get_backside)
1613 struct GraphicInfo *g = &graphic_info[graphic];
1615 // if no graphics defined at all, use fallback graphics
1616 if (g->bitmaps == NULL)
1617 *g = graphic_info[IMG_CHAR_EXCLAM];
1619 // if no in-game graphics defined, always use standard graphic size
1620 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1621 tilesize = TILESIZE;
1623 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1624 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1626 *x = *x * tilesize / g->tile_size;
1627 *y = *y * tilesize / g->tile_size;
1630 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1631 Bitmap **bitmap, int *x, int *y)
1633 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1636 void getFixedGraphicSource(int graphic, int frame,
1637 Bitmap **bitmap, int *x, int *y)
1639 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1642 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1644 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1647 void getGlobalAnimGraphicSource(int graphic, int frame,
1648 Bitmap **bitmap, int *x, int *y)
1650 struct GraphicInfo *g = &graphic_info[graphic];
1652 // if no graphics defined at all, use fallback graphics
1653 if (g->bitmaps == NULL)
1654 *g = graphic_info[IMG_CHAR_EXCLAM];
1656 // use original size graphics, if existing, else use standard size graphics
1657 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1658 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1660 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1662 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1665 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1666 int *x, int *y, boolean get_backside)
1668 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1672 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1674 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1677 void DrawGraphic(int x, int y, int graphic, int frame)
1680 if (!IN_SCR_FIELD(x, y))
1682 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1683 Debug("draw:DrawGraphic", "This should never happen!");
1689 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1692 MarkTileDirty(x, y);
1695 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1698 if (!IN_SCR_FIELD(x, y))
1700 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1702 Debug("draw:DrawFixedGraphic", "This should never happen!");
1708 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1710 MarkTileDirty(x, y);
1713 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1719 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1721 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1724 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1730 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1731 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1734 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1737 if (!IN_SCR_FIELD(x, y))
1739 Debug("draw:DrawGraphicThruMask", "x = %d, y = %d, graphic = %d",
1741 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1747 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1750 MarkTileDirty(x, y);
1753 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1756 if (!IN_SCR_FIELD(x, y))
1758 Debug("draw:DrawFixedGraphicThruMask", "x = %d, y = %d, graphic = %d",
1760 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1766 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1768 MarkTileDirty(x, y);
1771 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1777 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1779 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1783 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1784 int graphic, int frame)
1789 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1791 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1795 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1797 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1799 MarkTileDirty(x / tilesize, y / tilesize);
1802 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1805 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1806 graphic, frame, tilesize);
1807 MarkTileDirty(x / tilesize, y / tilesize);
1810 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1816 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1817 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1820 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1821 int frame, int tilesize)
1826 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1827 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1830 void DrawMiniGraphic(int x, int y, int graphic)
1832 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX, SY + y * MINI_TILEY, graphic);
1833 MarkTileDirty(x / 2, y / 2);
1836 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1841 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1842 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1845 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1846 int graphic, int frame,
1847 int cut_mode, int mask_mode)
1852 int width = TILEX, height = TILEY;
1855 if (dx || dy) // shifted graphic
1857 if (x < BX1) // object enters playfield from the left
1864 else if (x > BX2) // object enters playfield from the right
1870 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1876 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1878 else if (dx) // general horizontal movement
1879 MarkTileDirty(x + SIGN(dx), y);
1881 if (y < BY1) // object enters playfield from the top
1883 if (cut_mode == CUT_BELOW) // object completely above top border
1891 else if (y > BY2) // object enters playfield from the bottom
1897 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1903 else if (dy > 0 && cut_mode == CUT_ABOVE)
1905 if (y == BY2) // object completely above bottom border
1911 MarkTileDirty(x, y + 1);
1912 } // object leaves playfield to the bottom
1913 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1915 else if (dy) // general vertical movement
1916 MarkTileDirty(x, y + SIGN(dy));
1920 if (!IN_SCR_FIELD(x, y))
1922 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1924 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1930 width = width * TILESIZE_VAR / TILESIZE;
1931 height = height * TILESIZE_VAR / TILESIZE;
1932 cx = cx * TILESIZE_VAR / TILESIZE;
1933 cy = cy * TILESIZE_VAR / TILESIZE;
1934 dx = dx * TILESIZE_VAR / TILESIZE;
1935 dy = dy * TILESIZE_VAR / TILESIZE;
1937 if (width > 0 && height > 0)
1939 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1944 dst_x = FX + x * TILEX_VAR + dx;
1945 dst_y = FY + y * TILEY_VAR + dy;
1947 if (mask_mode == USE_MASKING)
1948 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1951 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1954 MarkTileDirty(x, y);
1958 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1959 int graphic, int frame,
1960 int cut_mode, int mask_mode)
1965 int width = TILEX_VAR, height = TILEY_VAR;
1968 int x2 = x + SIGN(dx);
1969 int y2 = y + SIGN(dy);
1971 // movement with two-tile animations must be sync'ed with movement position,
1972 // not with current GfxFrame (which can be higher when using slow movement)
1973 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1974 int anim_frames = graphic_info[graphic].anim_frames;
1976 // (we also need anim_delay here for movement animations with less frames)
1977 int anim_delay = graphic_info[graphic].anim_delay;
1978 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1980 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1981 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1983 // re-calculate animation frame for two-tile movement animation
1984 frame = getGraphicAnimationFrame(graphic, sync_frame);
1986 // check if movement start graphic inside screen area and should be drawn
1987 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1989 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1991 dst_x = FX + x1 * TILEX_VAR;
1992 dst_y = FY + y1 * TILEY_VAR;
1994 if (mask_mode == USE_MASKING)
1995 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1998 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
2001 MarkTileDirty(x1, y1);
2004 // check if movement end graphic inside screen area and should be drawn
2005 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
2007 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
2009 dst_x = FX + x2 * TILEX_VAR;
2010 dst_y = FY + y2 * TILEY_VAR;
2012 if (mask_mode == USE_MASKING)
2013 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
2016 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
2019 MarkTileDirty(x2, y2);
2023 static void DrawGraphicShifted(int x, int y, int dx, int dy,
2024 int graphic, int frame,
2025 int cut_mode, int mask_mode)
2029 DrawGraphic(x, y, graphic, frame);
2034 if (graphic_info[graphic].double_movement) // EM style movement images
2035 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2037 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2040 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
2041 int graphic, int frame, int cut_mode)
2043 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
2046 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
2047 int cut_mode, int mask_mode)
2049 int lx = LEVELX(x), ly = LEVELY(y);
2053 if (IN_LEV_FIELD(lx, ly))
2055 if (element == EL_EMPTY)
2056 element = GfxElementEmpty[lx][ly];
2058 SetRandomAnimationValue(lx, ly);
2060 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2061 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2063 // do not use double (EM style) movement graphic when not moving
2064 if (graphic_info[graphic].double_movement && !dx && !dy)
2066 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2067 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2070 if (game.use_masked_elements && (dx || dy))
2071 mask_mode = USE_MASKING;
2073 else // border element
2075 graphic = el2img(element);
2076 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2079 if (element == EL_EXPANDABLE_WALL)
2081 boolean left_stopped = FALSE, right_stopped = FALSE;
2083 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2084 left_stopped = TRUE;
2085 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2086 right_stopped = TRUE;
2088 if (left_stopped && right_stopped)
2090 else if (left_stopped)
2092 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2093 frame = graphic_info[graphic].anim_frames - 1;
2095 else if (right_stopped)
2097 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2098 frame = graphic_info[graphic].anim_frames - 1;
2103 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2104 else if (mask_mode == USE_MASKING)
2105 DrawGraphicThruMask(x, y, graphic, frame);
2107 DrawGraphic(x, y, graphic, frame);
2110 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2111 int cut_mode, int mask_mode)
2113 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2114 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2115 cut_mode, mask_mode);
2118 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2121 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2124 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2127 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2130 void DrawLevelElementThruMask(int x, int y, int element)
2132 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2135 void DrawLevelFieldThruMask(int x, int y)
2137 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2140 // !!! implementation of quicksand is totally broken !!!
2141 #define IS_CRUMBLED_TILE(x, y, e) \
2142 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2143 !IS_MOVING(x, y) || \
2144 (e) == EL_QUICKSAND_EMPTYING || \
2145 (e) == EL_QUICKSAND_FAST_EMPTYING))
2147 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2152 int width, height, cx, cy;
2153 int sx = SCREENX(x), sy = SCREENY(y);
2154 int crumbled_border_size = graphic_info[graphic].border_size;
2155 int crumbled_tile_size = graphic_info[graphic].tile_size;
2156 int crumbled_border_size_var =
2157 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2160 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2162 for (i = 1; i < 4; i++)
2164 int dxx = (i & 1 ? dx : 0);
2165 int dyy = (i & 2 ? dy : 0);
2168 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2171 // check if neighbour field is of same crumble type
2172 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2173 graphic_info[graphic].class ==
2174 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2176 // return if check prevents inner corner
2177 if (same == (dxx == dx && dyy == dy))
2181 // if we reach this point, we have an inner corner
2183 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2185 width = crumbled_border_size_var;
2186 height = crumbled_border_size_var;
2187 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2188 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2190 if (game.use_masked_elements)
2192 int graphic0 = el2img(EL_EMPTY);
2193 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2194 Bitmap *src_bitmap0;
2197 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2199 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2201 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2203 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2205 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2208 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2210 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2213 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2218 int width, height, bx, by, cx, cy;
2219 int sx = SCREENX(x), sy = SCREENY(y);
2220 int crumbled_border_size = graphic_info[graphic].border_size;
2221 int crumbled_tile_size = graphic_info[graphic].tile_size;
2222 int crumbled_border_size_var =
2223 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2224 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2227 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2229 // only needed when using masked elements
2230 int graphic0 = el2img(EL_EMPTY);
2231 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2232 Bitmap *src_bitmap0;
2235 if (game.use_masked_elements)
2236 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2238 // draw simple, sloppy, non-corner-accurate crumbled border
2240 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2241 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2242 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2243 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2245 if (game.use_masked_elements)
2247 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2249 FX + sx * TILEX_VAR + cx,
2250 FY + sy * TILEY_VAR + cy);
2252 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2254 FX + sx * TILEX_VAR + cx,
2255 FY + sy * TILEY_VAR + cy);
2258 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2260 FX + sx * TILEX_VAR + cx,
2261 FY + sy * TILEY_VAR + cy);
2263 // (remaining middle border part must be at least as big as corner part)
2264 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2265 crumbled_border_size_var >= TILESIZE_VAR / 3)
2268 // correct corners of crumbled border, if needed
2270 for (i = -1; i <= 1; i += 2)
2272 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2273 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2274 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2277 // check if neighbour field is of same crumble type
2278 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2279 graphic_info[graphic].class ==
2280 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2282 // no crumbled corner, but continued crumbled border
2284 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2285 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2286 int b1 = (i == 1 ? crumbled_border_size_var :
2287 TILESIZE_VAR - 2 * crumbled_border_size_var);
2289 width = crumbled_border_size_var;
2290 height = crumbled_border_size_var;
2292 if (dir == 1 || dir == 2)
2307 if (game.use_masked_elements)
2309 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2311 FX + sx * TILEX_VAR + cx,
2312 FY + sy * TILEY_VAR + cy);
2314 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2316 FX + sx * TILEX_VAR + cx,
2317 FY + sy * TILEY_VAR + cy);
2320 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2322 FX + sx * TILEX_VAR + cx,
2323 FY + sy * TILEY_VAR + cy);
2328 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2330 int sx = SCREENX(x), sy = SCREENY(y);
2333 struct XY *xy = xy_topdown;
2335 if (!IN_LEV_FIELD(x, y))
2338 element = TILE_GFX_ELEMENT(x, y);
2340 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2342 if (!IN_SCR_FIELD(sx, sy))
2345 // crumble field borders towards direct neighbour fields
2346 for (i = 0; i < 4; i++)
2348 int xx = x + xy[i].x;
2349 int yy = y + xy[i].y;
2351 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2354 // check if neighbour field is of same crumble type
2355 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2356 graphic_info[graphic].class ==
2357 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2360 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2363 // crumble inner field corners towards corner neighbour fields
2364 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2365 graphic_info[graphic].anim_frames == 2)
2367 for (i = 0; i < 4; i++)
2369 int dx = (i & 1 ? +1 : -1);
2370 int dy = (i & 2 ? +1 : -1);
2372 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2376 MarkTileDirty(sx, sy);
2378 else // center field is not crumbled -- crumble neighbour fields
2380 // crumble field borders of direct neighbour fields
2381 for (i = 0; i < 4; i++)
2383 int xx = x + xy[i].x;
2384 int yy = y + xy[i].y;
2385 int sxx = sx + xy[i].x;
2386 int syy = sy + xy[i].y;
2388 if (!IN_LEV_FIELD(xx, yy) ||
2389 !IN_SCR_FIELD(sxx, syy))
2392 // do not crumble fields that are being digged or snapped
2393 if (Tile[xx][yy] == EL_EMPTY ||
2394 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2397 element = TILE_GFX_ELEMENT(xx, yy);
2399 if (!IS_CRUMBLED_TILE(xx, yy, element))
2402 graphic = el_act2crm(element, ACTION_DEFAULT);
2404 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2406 MarkTileDirty(sxx, syy);
2409 // crumble inner field corners of corner neighbour fields
2410 for (i = 0; i < 4; i++)
2412 int dx = (i & 1 ? +1 : -1);
2413 int dy = (i & 2 ? +1 : -1);
2419 if (!IN_LEV_FIELD(xx, yy) ||
2420 !IN_SCR_FIELD(sxx, syy))
2423 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2426 element = TILE_GFX_ELEMENT(xx, yy);
2428 if (!IS_CRUMBLED_TILE(xx, yy, element))
2431 graphic = el_act2crm(element, ACTION_DEFAULT);
2433 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2434 graphic_info[graphic].anim_frames == 2)
2435 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2437 MarkTileDirty(sxx, syy);
2442 void DrawLevelFieldCrumbled(int x, int y)
2446 if (!IN_LEV_FIELD(x, y))
2449 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2450 GfxElement[x][y] != EL_UNDEFINED &&
2451 GFX_CRUMBLED(GfxElement[x][y]))
2453 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2458 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2460 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2463 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2466 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2467 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2468 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2469 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2470 int sx = SCREENX(x), sy = SCREENY(y);
2472 DrawScreenGraphic(sx, sy, graphic1, frame1);
2473 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2476 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2478 int sx = SCREENX(x), sy = SCREENY(y);
2479 struct XY *xy = xy_topdown;
2482 // crumble direct neighbour fields (required for field borders)
2483 for (i = 0; i < 4; i++)
2485 int xx = x + xy[i].x;
2486 int yy = y + xy[i].y;
2487 int sxx = sx + xy[i].x;
2488 int syy = sy + xy[i].y;
2490 if (!IN_LEV_FIELD(xx, yy) ||
2491 !IN_SCR_FIELD(sxx, syy) ||
2492 !GFX_CRUMBLED(Tile[xx][yy]) ||
2496 DrawLevelField(xx, yy);
2499 // crumble corner neighbour fields (required for inner field corners)
2500 for (i = 0; i < 4; i++)
2502 int dx = (i & 1 ? +1 : -1);
2503 int dy = (i & 2 ? +1 : -1);
2509 if (!IN_LEV_FIELD(xx, yy) ||
2510 !IN_SCR_FIELD(sxx, syy) ||
2511 !GFX_CRUMBLED(Tile[xx][yy]) ||
2515 int element = TILE_GFX_ELEMENT(xx, yy);
2516 int graphic = el_act2crm(element, ACTION_DEFAULT);
2518 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2519 graphic_info[graphic].anim_frames == 2)
2520 DrawLevelField(xx, yy);
2524 static int getBorderElement(int x, int y)
2528 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2529 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2530 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2531 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2532 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2533 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2534 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2536 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2537 int steel_position = (x == -1 && y == -1 ? 0 :
2538 x == lev_fieldx && y == -1 ? 1 :
2539 x == -1 && y == lev_fieldy ? 2 :
2540 x == lev_fieldx && y == lev_fieldy ? 3 :
2541 x == -1 || x == lev_fieldx ? 4 :
2542 y == -1 || y == lev_fieldy ? 5 : 6);
2544 return border[steel_position][steel_type];
2547 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2549 if (game.use_masked_elements)
2551 if (graphic != el2img(EL_EMPTY))
2552 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2554 DrawGraphicThruMask(x, y, graphic, frame);
2558 DrawGraphic(x, y, graphic, frame);
2562 void DrawLevelGraphic(int x, int y, int graphic, int frame)
2564 DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2567 void DrawScreenElement(int x, int y, int element)
2569 int mask_mode = NO_MASKING;
2571 if (game.use_masked_elements)
2573 int lx = LEVELX(x), ly = LEVELY(y);
2575 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2577 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2579 mask_mode = USE_MASKING;
2583 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2584 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2587 void DrawLevelElement(int x, int y, int element)
2589 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2590 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2593 void DrawScreenField(int x, int y)
2595 int lx = LEVELX(x), ly = LEVELY(y);
2596 int element, content;
2598 if (!IN_LEV_FIELD(lx, ly))
2600 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2603 element = getBorderElement(lx, ly);
2605 DrawScreenElement(x, y, element);
2610 element = Tile[lx][ly];
2611 content = Store[lx][ly];
2613 if (IS_MOVING(lx, ly))
2615 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2616 boolean cut_mode = NO_CUTTING;
2618 if (element == EL_QUICKSAND_EMPTYING ||
2619 element == EL_QUICKSAND_FAST_EMPTYING ||
2620 element == EL_MAGIC_WALL_EMPTYING ||
2621 element == EL_BD_MAGIC_WALL_EMPTYING ||
2622 element == EL_DC_MAGIC_WALL_EMPTYING ||
2623 element == EL_AMOEBA_DROPPING)
2624 cut_mode = CUT_ABOVE;
2625 else if (element == EL_QUICKSAND_FILLING ||
2626 element == EL_QUICKSAND_FAST_FILLING ||
2627 element == EL_MAGIC_WALL_FILLING ||
2628 element == EL_BD_MAGIC_WALL_FILLING ||
2629 element == EL_DC_MAGIC_WALL_FILLING)
2630 cut_mode = CUT_BELOW;
2632 if (cut_mode == CUT_ABOVE)
2633 DrawScreenElement(x, y, element);
2635 DrawScreenElement(x, y, EL_EMPTY);
2637 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2639 int dir = MovDir[lx][ly];
2640 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2641 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2643 if (IN_SCR_FIELD(newx, newy))
2644 DrawScreenElement(newx, newy, EL_EMPTY);
2648 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2649 else if (cut_mode == NO_CUTTING)
2650 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2653 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2655 if (cut_mode == CUT_BELOW &&
2656 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2657 DrawLevelElement(lx, ly + 1, element);
2660 if (content == EL_ACID)
2662 int dir = MovDir[lx][ly];
2663 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2664 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2666 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2668 // prevent target field from being drawn again (but without masking)
2669 // (this would happen if target field is scanned after moving element)
2670 Stop[newlx][newly] = TRUE;
2673 else if (IS_BLOCKED(lx, ly))
2678 boolean cut_mode = NO_CUTTING;
2679 int element_old, content_old;
2681 Blocked2Moving(lx, ly, &oldx, &oldy);
2684 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2685 MovDir[oldx][oldy] == MV_RIGHT);
2687 element_old = Tile[oldx][oldy];
2688 content_old = Store[oldx][oldy];
2690 if (element_old == EL_QUICKSAND_EMPTYING ||
2691 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2692 element_old == EL_MAGIC_WALL_EMPTYING ||
2693 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2694 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2695 element_old == EL_AMOEBA_DROPPING)
2696 cut_mode = CUT_ABOVE;
2698 DrawScreenElement(x, y, EL_EMPTY);
2701 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2703 else if (cut_mode == NO_CUTTING)
2704 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2707 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2710 else if (IS_DRAWABLE(element))
2711 DrawScreenElement(x, y, element);
2713 DrawScreenElement(x, y, EL_EMPTY);
2716 void DrawLevelField(int x, int y)
2718 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2719 DrawScreenField(SCREENX(x), SCREENY(y));
2720 else if (IS_MOVING(x, y))
2724 Moving2Blocked(x, y, &newx, &newy);
2725 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2726 DrawScreenField(SCREENX(newx), SCREENY(newy));
2728 else if (IS_BLOCKED(x, y))
2732 Blocked2Moving(x, y, &oldx, &oldy);
2733 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2734 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2738 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2739 int (*el2img_function)(int), boolean masked,
2740 int element_bits_draw)
2742 int element_base = map_mm_wall_element(element);
2743 int element_bits = (IS_DF_WALL(element) ?
2744 element - EL_DF_WALL_START :
2745 IS_MM_WALL(element) ?
2746 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2747 int graphic = el2img_function(element_base);
2748 int tilesize_draw = tilesize / 2;
2753 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2755 for (i = 0; i < 4; i++)
2757 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2758 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2760 if (!(element_bits_draw & (1 << i)))
2763 if (element_bits & (1 << i))
2766 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2767 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2769 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2770 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2775 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2776 tilesize_draw, tilesize_draw);
2781 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2782 boolean masked, int element_bits_draw)
2784 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2785 element, tilesize, el2edimg, masked, element_bits_draw);
2788 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2789 int (*el2img_function)(int))
2791 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2795 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2798 if (IS_MM_WALL(element))
2800 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2801 element, tilesize, el2edimg, masked, 0x000f);
2805 int graphic = el2edimg(element);
2808 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2810 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2814 void DrawSizedElement(int x, int y, int element, int tilesize)
2816 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2819 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2821 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2824 void DrawMiniElement(int x, int y, int element)
2828 graphic = el2edimg(element);
2829 DrawMiniGraphic(x, y, graphic);
2832 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2835 int x = sx + scroll_x, y = sy + scroll_y;
2837 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2838 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2839 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2840 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2842 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2845 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2847 int x = sx + scroll_x, y = sy + scroll_y;
2849 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2850 DrawMiniElement(sx, sy, EL_EMPTY);
2851 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2852 DrawMiniElement(sx, sy, Tile[x][y]);
2854 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2857 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2858 int x, int y, int xsize, int ysize,
2859 int tile_width, int tile_height)
2863 int dst_x = startx + x * tile_width;
2864 int dst_y = starty + y * tile_height;
2865 int width = graphic_info[graphic].width;
2866 int height = graphic_info[graphic].height;
2867 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2868 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2869 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2870 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2871 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2872 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2873 boolean draw_masked = graphic_info[graphic].draw_masked;
2875 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2877 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2879 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2883 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2884 inner_sx + (x - 1) * tile_width % inner_width);
2885 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2886 inner_sy + (y - 1) * tile_height % inner_height);
2889 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2892 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2896 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2897 int x, int y, int xsize, int ysize,
2900 int font_width = getFontWidth(font_nr);
2901 int font_height = getFontHeight(font_nr);
2903 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2904 font_width, font_height);
2907 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2909 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2910 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2911 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2912 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2913 boolean no_delay = (tape.warp_forward);
2914 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2915 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2916 DelayCounter anim_delay = { anim_delay_value };
2917 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2918 int font_width = getFontWidth(font_nr);
2919 int font_height = getFontHeight(font_nr);
2920 int max_xsize = level.envelope[envelope_nr].xsize;
2921 int max_ysize = level.envelope[envelope_nr].ysize;
2922 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2923 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2924 int xend = max_xsize;
2925 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2926 int xstep = (xstart < xend ? 1 : 0);
2927 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2929 int end = MAX(xend - xstart, yend - ystart);
2932 for (i = start; i <= end; i++)
2934 int last_frame = end; // last frame of this "for" loop
2935 int x = xstart + i * xstep;
2936 int y = ystart + i * ystep;
2937 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2938 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2939 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2940 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2943 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2945 BlitScreenToBitmap(backbuffer);
2947 SetDrawtoField(DRAW_TO_BACKBUFFER);
2949 for (yy = 0; yy < ysize; yy++)
2950 for (xx = 0; xx < xsize; xx++)
2951 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2953 DrawTextArea(sx + font_width, sy + font_height,
2954 level.envelope[envelope_nr].text, font_nr, max_xsize,
2955 xsize - 2, ysize - 2, 0, mask_mode,
2956 level.envelope[envelope_nr].autowrap,
2957 level.envelope[envelope_nr].centered, FALSE);
2959 redraw_mask |= REDRAW_FIELD;
2962 SkipUntilDelayReached(&anim_delay, &i, last_frame);
2965 ClearAutoRepeatKeyEvents();
2968 void ShowEnvelope(int envelope_nr)
2970 int element = EL_ENVELOPE_1 + envelope_nr;
2971 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2972 int sound_opening = element_info[element].sound[ACTION_OPENING];
2973 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2974 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2975 boolean no_delay = (tape.warp_forward);
2976 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2977 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2978 int anim_mode = graphic_info[graphic].anim_mode;
2979 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2980 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2981 boolean overlay_enabled = GetOverlayEnabled();
2983 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2985 SetOverlayEnabled(FALSE);
2988 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2990 if (anim_mode == ANIM_DEFAULT)
2991 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2993 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2996 Delay_WithScreenUpdates(wait_delay_value);
2998 WaitForEventToContinue();
3001 SetOverlayEnabled(overlay_enabled);
3003 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3005 if (anim_mode != ANIM_NONE)
3006 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
3008 if (anim_mode == ANIM_DEFAULT)
3009 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
3011 game.envelope_active = FALSE;
3013 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3015 redraw_mask |= REDRAW_FIELD;
3019 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
3020 int xsize, int ysize)
3022 if (!global.use_envelope_request)
3025 if (request.bitmap == NULL ||
3026 xsize > request.xsize ||
3027 ysize > request.ysize)
3029 if (request.bitmap != NULL)
3030 FreeBitmap(request.bitmap);
3032 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
3034 SDL_Surface *surface = request.bitmap->surface;
3036 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
3037 Fail("SDLGetNativeSurface() failed");
3040 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3042 // create masked surface for request bitmap, if needed
3043 if (graphic_info[IMG_BACKGROUND_REQUEST].draw_masked)
3045 SDL_Surface *surface = request.bitmap->surface;
3046 SDL_Surface *surface_masked = request.bitmap->surface_masked;
3048 SDLBlitSurface(surface, surface_masked, 0, 0, xsize, ysize, 0, 0);
3049 SDL_SetColorKey(surface_masked, SET_TRANSPARENT_PIXEL,
3050 SDL_MapRGB(surface_masked->format, 0x00, 0x00, 0x00));
3053 SDLFreeBitmapTextures(request.bitmap);
3054 SDLCreateBitmapTextures(request.bitmap);
3056 // set envelope request run-time values
3059 request.xsize = xsize;
3060 request.ysize = ysize;
3063 void DrawEnvelopeRequestToScreen(int drawing_target)
3065 if (global.use_envelope_request &&
3066 game.request_active_or_moving &&
3067 drawing_target == DRAW_TO_SCREEN)
3069 if (graphic_info[IMG_BACKGROUND_REQUEST].draw_masked)
3070 BlitToScreenMasked(request.bitmap, 0, 0, request.xsize, request.ysize,
3071 request.sx, request.sy);
3073 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3074 request.sx, request.sy);
3078 static void setRequestBasePosition(int *x, int *y)
3080 int sx_base, sy_base;
3082 if (request.x != -1)
3083 sx_base = request.x;
3084 else if (request.align == ALIGN_LEFT)
3086 else if (request.align == ALIGN_RIGHT)
3087 sx_base = SX + SXSIZE;
3089 sx_base = SX + SXSIZE / 2;
3091 if (request.y != -1)
3092 sy_base = request.y;
3093 else if (request.valign == VALIGN_TOP)
3095 else if (request.valign == VALIGN_BOTTOM)
3096 sy_base = SY + SYSIZE;
3098 sy_base = SY + SYSIZE / 2;
3104 static void setRequestPositionExt(int *x, int *y, int width, int height,
3105 boolean add_border_size)
3107 int border_size = request.border_size;
3108 int sx_base, sy_base;
3111 setRequestBasePosition(&sx_base, &sy_base);
3113 if (request.align == ALIGN_LEFT)
3115 else if (request.align == ALIGN_RIGHT)
3116 sx = sx_base - width;
3118 sx = sx_base - width / 2;
3120 if (request.valign == VALIGN_TOP)
3122 else if (request.valign == VALIGN_BOTTOM)
3123 sy = sy_base - height;
3125 sy = sy_base - height / 2;
3127 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3128 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3130 if (add_border_size)
3140 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3142 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3145 static void DrawEnvelopeRequest(char *text)
3147 char *text_final = text;
3148 char *text_door_style = NULL;
3149 int graphic = IMG_BACKGROUND_REQUEST;
3150 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3151 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3152 int font_nr = FONT_REQUEST;
3153 int font_width = getFontWidth(font_nr);
3154 int font_height = getFontHeight(font_nr);
3155 int border_size = request.border_size;
3156 int line_spacing = request.line_spacing;
3157 int line_height = font_height + line_spacing;
3158 int max_text_width = request.width - 2 * border_size;
3159 int max_text_height = request.height - 2 * border_size;
3160 int line_length = max_text_width / font_width;
3161 int max_lines = max_text_height / line_height;
3162 int text_width = line_length * font_width;
3163 int width = request.width;
3164 int height = request.height;
3165 int tile_size = MAX(request.step_offset, 1);
3166 int x_steps = width / tile_size;
3167 int y_steps = height / tile_size;
3168 int sx_offset = border_size;
3169 int sy_offset = border_size;
3173 if (request.centered)
3174 sx_offset = (request.width - text_width) / 2;
3176 if (request.wrap_single_words && !request.autowrap)
3178 char *src_text_ptr, *dst_text_ptr;
3180 text_door_style = checked_malloc(2 * strlen(text) + 1);
3182 src_text_ptr = text;
3183 dst_text_ptr = text_door_style;
3185 while (*src_text_ptr)
3187 if (*src_text_ptr == ' ' ||
3188 *src_text_ptr == '?' ||
3189 *src_text_ptr == '!')
3190 *dst_text_ptr++ = '\n';
3192 if (*src_text_ptr != ' ')
3193 *dst_text_ptr++ = *src_text_ptr;
3198 *dst_text_ptr = '\0';
3200 text_final = text_door_style;
3203 setRequestPosition(&sx, &sy, FALSE);
3205 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3207 for (y = 0; y < y_steps; y++)
3208 for (x = 0; x < x_steps; x++)
3209 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3210 x, y, x_steps, y_steps,
3211 tile_size, tile_size);
3213 // force DOOR font inside door area
3214 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3216 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3217 line_length, -1, max_lines, line_spacing, mask_mode,
3218 request.autowrap, request.centered, FALSE);
3222 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3223 RedrawGadget(tool_gadget[i]);
3225 // store readily prepared envelope request for later use when animating
3226 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3228 // create masked surface for request bitmap, if needed
3229 if (graphic_info[IMG_BACKGROUND_REQUEST].draw_masked)
3231 if (bitmap_db_store_2->surface_masked == NULL)
3233 if ((bitmap_db_store_2->surface_masked =
3234 SDLGetNativeSurface(bitmap_db_store_2->surface)) == NULL)
3235 Fail("SDLGetNativeSurface() failed");
3238 SDL_Surface *surface = bitmap_db_store_2->surface;
3239 SDL_Surface *surface_masked = bitmap_db_store_2->surface_masked;
3241 SDLBlitSurface(surface, surface_masked, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3242 SDL_SetColorKey(surface_masked, SET_TRANSPARENT_PIXEL,
3243 SDL_MapRGB(surface_masked->format, 0x00, 0x00, 0x00));
3246 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3248 if (text_door_style)
3249 free(text_door_style);
3252 static void AnimateEnvelopeRequest(int anim_mode, int action)
3254 int graphic = IMG_BACKGROUND_REQUEST;
3255 boolean draw_masked = graphic_info[graphic].draw_masked;
3256 int delay_value_normal = request.step_delay;
3257 int delay_value_fast = delay_value_normal / 2;
3258 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3259 boolean no_delay = (tape.warp_forward);
3260 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3261 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3262 DelayCounter anim_delay = { anim_delay_value };
3264 int tile_size = MAX(request.step_offset, 1);
3265 int max_xsize = request.width / tile_size;
3266 int max_ysize = request.height / tile_size;
3267 int max_xsize_inner = max_xsize - 2;
3268 int max_ysize_inner = max_ysize - 2;
3270 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3271 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3272 int xend = max_xsize_inner;
3273 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3274 int xstep = (xstart < xend ? 1 : 0);
3275 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3277 int end = MAX(xend - xstart, yend - ystart);
3280 if (setup.quick_doors)
3287 for (i = start; i <= end; i++)
3289 int last_frame = end; // last frame of this "for" loop
3290 int x = xstart + i * xstep;
3291 int y = ystart + i * ystep;
3292 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3293 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3294 int xsize_size_left = (xsize - 1) * tile_size;
3295 int ysize_size_top = (ysize - 1) * tile_size;
3296 int max_xsize_pos = (max_xsize - 1) * tile_size;
3297 int max_ysize_pos = (max_ysize - 1) * tile_size;
3298 int width = xsize * tile_size;
3299 int height = ysize * tile_size;
3304 setRequestPosition(&src_x, &src_y, FALSE);
3305 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3307 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3309 for (yy = 0; yy < 2; yy++)
3311 for (xx = 0; xx < 2; xx++)
3313 int src_xx = src_x + xx * max_xsize_pos;
3314 int src_yy = src_y + yy * max_ysize_pos;
3315 int dst_xx = dst_x + xx * xsize_size_left;
3316 int dst_yy = dst_y + yy * ysize_size_top;
3317 int xx_size = (xx ? tile_size : xsize_size_left);
3318 int yy_size = (yy ? tile_size : ysize_size_top);
3321 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3322 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3324 BlitBitmap(bitmap_db_store_2, backbuffer,
3325 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3329 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3331 redraw_mask |= REDRAW_FIELD;
3335 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3338 ClearAutoRepeatKeyEvents();
3341 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3343 int graphic = IMG_BACKGROUND_REQUEST;
3344 int sound_opening = SND_REQUEST_OPENING;
3345 int sound_closing = SND_REQUEST_CLOSING;
3346 int anim_mode_1 = request.anim_mode; // (higher priority)
3347 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3348 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3349 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3350 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3352 if (game_status == GAME_MODE_PLAYING)
3353 BlitScreenToBitmap(backbuffer);
3355 SetDrawtoField(DRAW_TO_BACKBUFFER);
3357 // SetDrawBackgroundMask(REDRAW_NONE);
3359 if (action == ACTION_OPENING)
3361 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3363 if (req_state & REQ_ASK)
3365 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3366 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3367 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3368 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3370 else if (req_state & REQ_CONFIRM)
3372 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3373 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3375 else if (req_state & REQ_PLAYER)
3377 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3378 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3379 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3380 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3383 DrawEnvelopeRequest(text);
3386 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3388 if (action == ACTION_OPENING)
3390 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3392 if (anim_mode == ANIM_DEFAULT)
3393 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3395 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3399 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3401 if (anim_mode != ANIM_NONE)
3402 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3404 if (anim_mode == ANIM_DEFAULT)
3405 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3408 game.envelope_active = FALSE;
3410 if (action == ACTION_CLOSING)
3411 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3413 // SetDrawBackgroundMask(last_draw_background_mask);
3415 redraw_mask |= REDRAW_FIELD;
3419 if (action == ACTION_CLOSING &&
3420 game_status == GAME_MODE_PLAYING &&
3421 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3422 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3425 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3427 if (IS_MM_WALL(element))
3429 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3435 int graphic = el2preimg(element);
3437 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3438 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3443 void DrawLevel(int draw_background_mask)
3447 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3448 SetDrawBackgroundMask(draw_background_mask);
3452 for (x = BX1; x <= BX2; x++)
3453 for (y = BY1; y <= BY2; y++)
3454 DrawScreenField(x, y);
3456 redraw_mask |= REDRAW_FIELD;
3459 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3464 for (x = 0; x < size_x; x++)
3465 for (y = 0; y < size_y; y++)
3466 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3468 redraw_mask |= REDRAW_FIELD;
3471 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3475 for (x = 0; x < size_x; x++)
3476 for (y = 0; y < size_y; y++)
3477 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3479 redraw_mask |= REDRAW_FIELD;
3482 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3484 boolean show_level_border = (BorderElement != EL_EMPTY);
3485 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3486 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3487 int tile_size = preview.tile_size;
3488 int preview_width = preview.xsize * tile_size;
3489 int preview_height = preview.ysize * tile_size;
3490 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3491 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3492 int real_preview_width = real_preview_xsize * tile_size;
3493 int real_preview_height = real_preview_ysize * tile_size;
3494 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3495 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3498 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3501 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3503 dst_x += (preview_width - real_preview_width) / 2;
3504 dst_y += (preview_height - real_preview_height) / 2;
3506 for (x = 0; x < real_preview_xsize; x++)
3508 for (y = 0; y < real_preview_ysize; y++)
3510 int lx = from_x + x + (show_level_border ? -1 : 0);
3511 int ly = from_y + y + (show_level_border ? -1 : 0);
3512 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3513 getBorderElement(lx, ly));
3515 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3516 element, tile_size);
3520 redraw_mask |= REDRAW_FIELD;
3523 #define MICROLABEL_EMPTY 0
3524 #define MICROLABEL_LEVEL_NAME 1
3525 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3526 #define MICROLABEL_LEVEL_AUTHOR 3
3527 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3528 #define MICROLABEL_IMPORTED_FROM 5
3529 #define MICROLABEL_IMPORTED_BY_HEAD 6
3530 #define MICROLABEL_IMPORTED_BY 7
3532 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3534 int max_text_width = SXSIZE;
3535 int font_width = getFontWidth(font_nr);
3537 if (pos->align == ALIGN_CENTER)
3538 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3539 else if (pos->align == ALIGN_RIGHT)
3540 max_text_width = pos->x;
3542 max_text_width = SXSIZE - pos->x;
3544 return max_text_width / font_width;
3547 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3549 char label_text[MAX_OUTPUT_LINESIZE + 1];
3550 int max_len_label_text;
3551 int font_nr = pos->font;
3554 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3557 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3558 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3559 mode == MICROLABEL_IMPORTED_BY_HEAD)
3560 font_nr = pos->font_alt;
3562 max_len_label_text = getMaxTextLength(pos, font_nr);
3564 if (pos->size != -1)
3565 max_len_label_text = pos->size;
3567 for (i = 0; i < max_len_label_text; i++)
3568 label_text[i] = ' ';
3569 label_text[max_len_label_text] = '\0';
3571 if (strlen(label_text) > 0)
3572 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3575 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3576 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3577 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3578 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3579 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3580 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3581 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3582 max_len_label_text);
3583 label_text[max_len_label_text] = '\0';
3585 if (strlen(label_text) > 0)
3586 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3588 redraw_mask |= REDRAW_FIELD;
3591 static void DrawPreviewLevelLabel(int mode)
3593 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3596 static void DrawPreviewLevelInfo(int mode)
3598 if (mode == MICROLABEL_LEVEL_NAME)
3599 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3600 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3601 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3604 static void DrawPreviewLevelExt(boolean restart)
3606 static DelayCounter scroll_delay = { 0 };
3607 static DelayCounter label_delay = { 0 };
3608 static int from_x, from_y, scroll_direction;
3609 static int label_state, label_counter;
3610 boolean show_level_border = (BorderElement != EL_EMPTY);
3611 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3612 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3614 scroll_delay.value = preview.step_delay;
3615 label_delay.value = MICROLEVEL_LABEL_DELAY;
3622 if (preview.anim_mode == ANIM_CENTERED)
3624 if (level_xsize > preview.xsize)
3625 from_x = (level_xsize - preview.xsize) / 2;
3626 if (level_ysize > preview.ysize)
3627 from_y = (level_ysize - preview.ysize) / 2;
3630 from_x += preview.xoffset;
3631 from_y += preview.yoffset;
3633 scroll_direction = MV_RIGHT;
3637 DrawPreviewLevelPlayfield(from_x, from_y);
3638 DrawPreviewLevelLabel(label_state);
3640 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3641 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3643 // initialize delay counters
3644 ResetDelayCounter(&scroll_delay);
3645 ResetDelayCounter(&label_delay);
3647 if (leveldir_current->name)
3649 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3650 char label_text[MAX_OUTPUT_LINESIZE + 1];
3651 int font_nr = pos->font;
3652 int max_len_label_text = getMaxTextLength(pos, font_nr);
3654 if (pos->size != -1)
3655 max_len_label_text = pos->size;
3657 strncpy(label_text, leveldir_current->name, max_len_label_text);
3658 label_text[max_len_label_text] = '\0';
3660 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3661 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3667 // scroll preview level, if needed
3668 if (preview.anim_mode != ANIM_NONE &&
3669 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3670 DelayReached(&scroll_delay))
3672 switch (scroll_direction)
3677 from_x -= preview.step_offset;
3678 from_x = (from_x < 0 ? 0 : from_x);
3681 scroll_direction = MV_UP;
3685 if (from_x < level_xsize - preview.xsize)
3687 from_x += preview.step_offset;
3688 from_x = (from_x > level_xsize - preview.xsize ?
3689 level_xsize - preview.xsize : from_x);
3692 scroll_direction = MV_DOWN;
3698 from_y -= preview.step_offset;
3699 from_y = (from_y < 0 ? 0 : from_y);
3702 scroll_direction = MV_RIGHT;
3706 if (from_y < level_ysize - preview.ysize)
3708 from_y += preview.step_offset;
3709 from_y = (from_y > level_ysize - preview.ysize ?
3710 level_ysize - preview.ysize : from_y);
3713 scroll_direction = MV_LEFT;
3720 DrawPreviewLevelPlayfield(from_x, from_y);
3723 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3724 // redraw micro level label, if needed
3725 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3726 !strEqual(level.author, ANONYMOUS_NAME) &&
3727 !strEqual(level.author, leveldir_current->name) &&
3728 DelayReached(&label_delay))
3730 int max_label_counter = 23;
3732 if (leveldir_current->imported_from != NULL &&
3733 strlen(leveldir_current->imported_from) > 0)
3734 max_label_counter += 14;
3735 if (leveldir_current->imported_by != NULL &&
3736 strlen(leveldir_current->imported_by) > 0)
3737 max_label_counter += 14;
3739 label_counter = (label_counter + 1) % max_label_counter;
3740 label_state = (label_counter >= 0 && label_counter <= 7 ?
3741 MICROLABEL_LEVEL_NAME :
3742 label_counter >= 9 && label_counter <= 12 ?
3743 MICROLABEL_LEVEL_AUTHOR_HEAD :
3744 label_counter >= 14 && label_counter <= 21 ?
3745 MICROLABEL_LEVEL_AUTHOR :
3746 label_counter >= 23 && label_counter <= 26 ?
3747 MICROLABEL_IMPORTED_FROM_HEAD :
3748 label_counter >= 28 && label_counter <= 35 ?
3749 MICROLABEL_IMPORTED_FROM :
3750 label_counter >= 37 && label_counter <= 40 ?
3751 MICROLABEL_IMPORTED_BY_HEAD :
3752 label_counter >= 42 && label_counter <= 49 ?
3753 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3755 if (leveldir_current->imported_from == NULL &&
3756 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3757 label_state == MICROLABEL_IMPORTED_FROM))
3758 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3759 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3761 DrawPreviewLevelLabel(label_state);
3765 void DrawPreviewPlayers(void)
3767 if (game_status != GAME_MODE_MAIN)
3770 // do not draw preview players if level preview redefined, but players aren't
3771 if (preview.redefined && !menu.main.preview_players.redefined)
3774 boolean player_found[MAX_PLAYERS];
3775 int num_players = 0;
3778 for (i = 0; i < MAX_PLAYERS; i++)
3779 player_found[i] = FALSE;
3781 // check which players can be found in the level (simple approach)
3782 for (x = 0; x < lev_fieldx; x++)
3784 for (y = 0; y < lev_fieldy; y++)
3786 int element = level.field[x][y];
3788 if (IS_PLAYER_ELEMENT(element))
3790 int player_nr = GET_PLAYER_NR(element);
3792 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3794 if (!player_found[player_nr])
3797 player_found[player_nr] = TRUE;
3802 struct TextPosInfo *pos = &menu.main.preview_players;
3803 int tile_size = pos->tile_size;
3804 int border_size = pos->border_size;
3805 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3806 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3807 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3808 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3809 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3810 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3811 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3812 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3813 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3814 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3815 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3816 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3818 // clear area in which the players will be drawn
3819 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3820 max_players_width, max_players_height);
3822 if (!network.enabled && !setup.team_mode)
3825 // only draw players if level is suited for team mode
3826 if (num_players < 2)
3829 // draw all players that were found in the level
3830 for (i = 0; i < MAX_PLAYERS; i++)
3832 if (player_found[i])
3834 int graphic = el2img(EL_PLAYER_1 + i);
3836 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3838 xpos += player_xoffset;
3839 ypos += player_yoffset;
3844 void DrawPreviewLevelInitial(void)
3846 DrawPreviewLevelExt(TRUE);
3847 DrawPreviewPlayers();
3850 void DrawPreviewLevelAnimation(void)
3852 DrawPreviewLevelExt(FALSE);
3855 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3856 int border_size, int font_nr)
3858 int graphic = el2img(EL_PLAYER_1 + player_nr);
3859 int font_height = getFontHeight(font_nr);
3860 int player_height = MAX(tile_size, font_height);
3861 int xoffset_text = tile_size + border_size;
3862 int yoffset_text = (player_height - font_height) / 2;
3863 int yoffset_graphic = (player_height - tile_size) / 2;
3864 char *player_name = getNetworkPlayerName(player_nr + 1);
3866 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3868 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3871 static void DrawNetworkPlayersExt(boolean force)
3873 if (game_status != GAME_MODE_MAIN)
3876 if (!network.connected && !force)
3879 // do not draw network players if level preview redefined, but players aren't
3880 if (preview.redefined && !menu.main.network_players.redefined)
3883 int num_players = 0;
3886 for (i = 0; i < MAX_PLAYERS; i++)
3887 if (stored_player[i].connected_network)
3890 struct TextPosInfo *pos = &menu.main.network_players;
3891 int tile_size = pos->tile_size;
3892 int border_size = pos->border_size;
3893 int xoffset_text = tile_size + border_size;
3894 int font_nr = pos->font;
3895 int font_width = getFontWidth(font_nr);
3896 int font_height = getFontHeight(font_nr);
3897 int player_height = MAX(tile_size, font_height);
3898 int player_yoffset = player_height + border_size;
3899 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3900 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3901 int all_players_height = num_players * player_yoffset - border_size;
3902 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3903 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3904 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3906 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3907 max_players_width, max_players_height);
3909 // first draw local network player ...
3910 for (i = 0; i < MAX_PLAYERS; i++)
3912 if (stored_player[i].connected_network &&
3913 stored_player[i].connected_locally)
3915 char *player_name = getNetworkPlayerName(i + 1);
3916 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3917 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3919 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3921 ypos += player_yoffset;
3925 // ... then draw all other network players
3926 for (i = 0; i < MAX_PLAYERS; i++)
3928 if (stored_player[i].connected_network &&
3929 !stored_player[i].connected_locally)
3931 char *player_name = getNetworkPlayerName(i + 1);
3932 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3933 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3935 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3937 ypos += player_yoffset;
3942 void DrawNetworkPlayers(void)
3944 DrawNetworkPlayersExt(FALSE);
3947 void ClearNetworkPlayers(void)
3949 DrawNetworkPlayersExt(TRUE);
3952 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3953 int graphic, int lx, int ly,
3956 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3958 if (mask_mode == USE_MASKING)
3959 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3961 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3964 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3965 int graphic, int sync_frame, int mask_mode)
3967 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3969 if (mask_mode == USE_MASKING)
3970 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3972 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3975 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3976 int graphic, int sync_frame, int tilesize,
3979 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3981 if (mask_mode == USE_MASKING)
3982 DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
3984 DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
3987 static void DrawGraphicAnimation(int x, int y, int graphic)
3989 int lx = LEVELX(x), ly = LEVELY(y);
3990 int mask_mode = NO_MASKING;
3992 if (!IN_SCR_FIELD(x, y))
3995 if (game.use_masked_elements)
3997 if (Tile[lx][ly] != EL_EMPTY)
3999 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
4001 mask_mode = USE_MASKING;
4005 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
4006 graphic, lx, ly, mask_mode);
4008 MarkTileDirty(x, y);
4011 void DrawFixedGraphicAnimation(int x, int y, int graphic)
4013 int lx = LEVELX(x), ly = LEVELY(y);
4014 int mask_mode = NO_MASKING;
4016 if (!IN_SCR_FIELD(x, y))
4019 if (game.use_masked_elements)
4021 if (Tile[lx][ly] != EL_EMPTY)
4023 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
4025 mask_mode = USE_MASKING;
4029 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
4030 graphic, lx, ly, mask_mode);
4032 MarkTileDirty(x, y);
4035 void DrawLevelGraphicAnimation(int x, int y, int graphic)
4037 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4040 void DrawLevelElementAnimation(int x, int y, int element)
4042 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4044 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4047 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
4049 int sx = SCREENX(x), sy = SCREENY(y);
4051 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4054 if (Tile[x][y] == EL_EMPTY)
4055 graphic = el2img(GfxElementEmpty[x][y]);
4057 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4060 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4063 DrawGraphicAnimation(sx, sy, graphic);
4066 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4067 DrawLevelFieldCrumbled(x, y);
4069 if (GFX_CRUMBLED(Tile[x][y]))
4070 DrawLevelFieldCrumbled(x, y);
4074 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4076 int sx = SCREENX(x), sy = SCREENY(y);
4079 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4082 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4084 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4087 DrawGraphicAnimation(sx, sy, graphic);
4089 if (GFX_CRUMBLED(element))
4090 DrawLevelFieldCrumbled(x, y);
4093 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4095 if (player->use_murphy)
4097 // this works only because currently only one player can be "murphy" ...
4098 static int last_horizontal_dir = MV_LEFT;
4099 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4101 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4102 last_horizontal_dir = move_dir;
4104 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4106 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4108 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4114 return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4117 static boolean equalGraphics(int graphic1, int graphic2)
4119 struct GraphicInfo *g1 = &graphic_info[graphic1];
4120 struct GraphicInfo *g2 = &graphic_info[graphic2];
4122 return (g1->bitmap == g2->bitmap &&
4123 g1->src_x == g2->src_x &&
4124 g1->src_y == g2->src_y &&
4125 g1->anim_frames == g2->anim_frames &&
4126 g1->anim_delay == g2->anim_delay &&
4127 g1->anim_mode == g2->anim_mode);
4130 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4134 DRAW_PLAYER_STAGE_INIT = 0,
4135 DRAW_PLAYER_STAGE_LAST_FIELD,
4136 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4137 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4138 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4139 DRAW_PLAYER_STAGE_PLAYER,
4141 DRAW_PLAYER_STAGE_PLAYER,
4142 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4144 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4145 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4147 NUM_DRAW_PLAYER_STAGES
4150 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4152 static int static_last_player_graphic[MAX_PLAYERS];
4153 static int static_last_player_frame[MAX_PLAYERS];
4154 static boolean static_player_is_opaque[MAX_PLAYERS];
4155 static boolean draw_player[MAX_PLAYERS];
4156 int pnr = player->index_nr;
4158 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4160 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4161 static_last_player_frame[pnr] = player->Frame;
4162 static_player_is_opaque[pnr] = FALSE;
4164 draw_player[pnr] = TRUE;
4167 if (!draw_player[pnr])
4171 if (!IN_LEV_FIELD(player->jx, player->jy))
4173 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4174 Debug("draw:DrawPlayerExt", "This should never happen!");
4176 draw_player[pnr] = FALSE;
4182 int last_player_graphic = static_last_player_graphic[pnr];
4183 int last_player_frame = static_last_player_frame[pnr];
4184 boolean player_is_opaque = static_player_is_opaque[pnr];
4186 int jx = player->jx;
4187 int jy = player->jy;
4188 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4189 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4190 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4191 int last_jx = (player->is_moving ? jx - dx : jx);
4192 int last_jy = (player->is_moving ? jy - dy : jy);
4193 int next_jx = jx + dx;
4194 int next_jy = jy + dy;
4195 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4196 int sx = SCREENX(jx);
4197 int sy = SCREENY(jy);
4198 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4199 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4200 int element = Tile[jx][jy];
4201 int last_element = Tile[last_jx][last_jy];
4202 int action = (player->is_pushing ? ACTION_PUSHING :
4203 player->is_digging ? ACTION_DIGGING :
4204 player->is_collecting ? ACTION_COLLECTING :
4205 player->is_moving ? ACTION_MOVING :
4206 player->is_snapping ? ACTION_SNAPPING :
4207 player->is_dropping ? ACTION_DROPPING :
4208 player->is_waiting ? player->action_waiting :
4211 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4213 // ------------------------------------------------------------------------
4214 // initialize drawing the player
4215 // ------------------------------------------------------------------------
4217 draw_player[pnr] = FALSE;
4219 // GfxElement[][] is set to the element the player is digging or collecting;
4220 // remove also for off-screen player if the player is not moving anymore
4221 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4222 GfxElement[jx][jy] = EL_UNDEFINED;
4224 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4227 if (element == EL_EXPLOSION)
4230 InitPlayerGfxAnimation(player, action, move_dir);
4232 draw_player[pnr] = TRUE;
4234 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4236 // ------------------------------------------------------------------------
4237 // draw things in the field the player is leaving, if needed
4238 // ------------------------------------------------------------------------
4240 if (!IN_SCR_FIELD(sx, sy))
4241 draw_player[pnr] = FALSE;
4243 if (!player->is_moving)
4246 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4248 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4250 if (last_element == EL_DYNAMITE_ACTIVE ||
4251 last_element == EL_EM_DYNAMITE_ACTIVE ||
4252 last_element == EL_SP_DISK_RED_ACTIVE)
4253 DrawDynamite(last_jx, last_jy);
4255 DrawLevelFieldThruMask(last_jx, last_jy);
4257 else if (last_element == EL_DYNAMITE_ACTIVE ||
4258 last_element == EL_EM_DYNAMITE_ACTIVE ||
4259 last_element == EL_SP_DISK_RED_ACTIVE)
4260 DrawDynamite(last_jx, last_jy);
4262 DrawLevelField(last_jx, last_jy);
4264 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4266 // ------------------------------------------------------------------------
4267 // draw things behind the player, if needed
4268 // ------------------------------------------------------------------------
4272 DrawLevelElement(jx, jy, Back[jx][jy]);
4277 if (IS_ACTIVE_BOMB(element))
4279 DrawLevelElement(jx, jy, EL_EMPTY);
4284 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4286 int old_element = GfxElement[jx][jy];
4287 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4288 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4290 if (GFX_CRUMBLED(old_element))
4291 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4293 DrawScreenGraphic(sx, sy, old_graphic, frame);
4295 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4296 static_player_is_opaque[pnr] = TRUE;
4300 GfxElement[jx][jy] = EL_UNDEFINED;
4302 // make sure that pushed elements are drawn with correct frame rate
4303 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4305 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4306 GfxFrame[jx][jy] = player->StepFrame;
4308 DrawLevelField(jx, jy);
4311 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4313 // ------------------------------------------------------------------------
4314 // draw things the player is pushing, if needed
4315 // ------------------------------------------------------------------------
4317 if (!player->is_pushing || !player->is_moving)
4320 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4323 int gfx_frame = GfxFrame[jx][jy];
4325 if (!IS_MOVING(jx, jy)) // push movement already finished
4327 element = Tile[next_jx][next_jy];
4328 gfx_frame = GfxFrame[next_jx][next_jy];
4331 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4332 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4333 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4335 // draw background element under pushed element (like the Sokoban field)
4336 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4338 // this allows transparent pushing animation over non-black background
4341 DrawLevelElement(jx, jy, Back[jx][jy]);
4343 DrawLevelElement(jx, jy, EL_EMPTY);
4346 if (Back[next_jx][next_jy])
4347 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4349 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4351 int px = SCREENX(jx), py = SCREENY(jy);
4352 int pxx = (TILEX - ABS(sxx)) * dx;
4353 int pyy = (TILEY - ABS(syy)) * dy;
4356 // do not draw (EM style) pushing animation when pushing is finished
4357 // (two-tile animations usually do not contain start and end frame)
4358 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4359 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4361 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4363 // masked drawing is needed for EMC style (double) movement graphics
4364 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4365 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4368 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4370 // ------------------------------------------------------------------------
4371 // draw player himself
4372 // ------------------------------------------------------------------------
4374 int graphic = getPlayerGraphic(player, move_dir);
4376 // in the case of changed player action or direction, prevent the current
4377 // animation frame from being restarted for identical animations
4378 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4379 player->Frame = last_player_frame;
4381 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4383 if (player_is_opaque)
4384 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4386 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4388 if (SHIELD_ON(player))
4390 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4391 IMG_SHIELD_NORMAL_ACTIVE);
4392 frame = getGraphicAnimationFrame(graphic, -1);
4394 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4397 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4399 // ------------------------------------------------------------------------
4400 // draw things in front of player (active dynamite or dynabombs)
4401 // ------------------------------------------------------------------------
4403 if (IS_ACTIVE_BOMB(element))
4405 int graphic = el2img(element);
4406 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4408 if (game.emulation == EMU_SUPAPLEX)
4409 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4411 DrawGraphicThruMask(sx, sy, graphic, frame);
4414 if (player_is_moving && last_element == EL_EXPLOSION)
4416 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4417 GfxElement[last_jx][last_jy] : EL_EMPTY);
4418 int graphic = el_act2img(element, ACTION_EXPLODING);
4419 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4420 int phase = ExplodePhase[last_jx][last_jy] - 1;
4421 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4424 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4427 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4429 // ------------------------------------------------------------------------
4430 // draw elements the player is just walking/passing through/under
4431 // ------------------------------------------------------------------------
4433 if (player_is_moving)
4435 // handle the field the player is leaving ...
4436 if (IS_ACCESSIBLE_INSIDE(last_element))
4437 DrawLevelField(last_jx, last_jy);
4438 else if (IS_ACCESSIBLE_UNDER(last_element))
4439 DrawLevelFieldThruMask(last_jx, last_jy);
4442 // do not redraw accessible elements if the player is just pushing them
4443 if (!player_is_moving || !player->is_pushing)
4445 // ... and the field the player is entering
4446 if (IS_ACCESSIBLE_INSIDE(element))
4447 DrawLevelField(jx, jy);
4448 else if (IS_ACCESSIBLE_UNDER(element))
4449 DrawLevelFieldThruMask(jx, jy);
4452 MarkTileDirty(sx, sy);
4456 void DrawPlayer(struct PlayerInfo *player)
4460 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4461 DrawPlayerExt(player, i);
4464 void DrawAllPlayers(void)
4468 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4469 for (j = 0; j < MAX_PLAYERS; j++)
4470 if (stored_player[j].active)
4471 DrawPlayerExt(&stored_player[j], i);
4474 void DrawPlayerField(int x, int y)
4476 if (!IS_PLAYER(x, y))
4479 DrawPlayer(PLAYERINFO(x, y));
4482 // ----------------------------------------------------------------------------
4484 void WaitForEventToContinue(void)
4486 boolean first_wait = TRUE;
4487 boolean still_wait = TRUE;
4489 if (program.headless)
4492 // simulate releasing mouse button over last gadget, if still pressed
4494 HandleGadgets(-1, -1, 0);
4496 button_status = MB_RELEASED;
4499 ClearPlayerAction();
4505 if (NextValidEvent(&event))
4509 case EVENT_BUTTONPRESS:
4510 case EVENT_FINGERPRESS:
4514 case EVENT_BUTTONRELEASE:
4515 case EVENT_FINGERRELEASE:
4516 still_wait = first_wait;
4519 case EVENT_KEYPRESS:
4520 case SDL_CONTROLLERBUTTONDOWN:
4521 case SDL_JOYBUTTONDOWN:
4526 HandleOtherEvents(&event);
4530 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4535 if (!PendingEvent())
4540 #define MAX_REQUEST_LINES 13
4541 #define MAX_REQUEST_LINE_FONT1_LEN 7
4542 #define MAX_REQUEST_LINE_FONT2_LEN 10
4544 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4546 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4548 int draw_buffer_last = GetDrawtoField();
4549 int width = request.width;
4550 int height = request.height;
4554 // when showing request dialog after game ended, deactivate game panel
4555 if (game_just_ended)
4556 game.panel.active = FALSE;
4558 game.request_active = TRUE;
4560 setRequestPosition(&sx, &sy, FALSE);
4562 button_status = MB_RELEASED;
4564 request_gadget_id = -1;
4569 boolean event_handled = FALSE;
4571 if (game_just_ended)
4573 SetDrawtoField(draw_buffer_game);
4575 HandleGameActions();
4577 SetDrawtoField(DRAW_TO_BACKBUFFER);
4579 if (global.use_envelope_request)
4581 // copy current state of request area to middle of playfield area
4582 if (graphic_info[IMG_BACKGROUND_REQUEST].draw_masked)
4583 BlitBitmapMasked(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4585 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4593 while (NextValidEvent(&event))
4595 event_handled = TRUE;
4599 case EVENT_BUTTONPRESS:
4600 case EVENT_BUTTONRELEASE:
4601 case EVENT_MOTIONNOTIFY:
4605 if (event.type == EVENT_MOTIONNOTIFY)
4610 motion_status = TRUE;
4611 mx = ((MotionEvent *) &event)->x;
4612 my = ((MotionEvent *) &event)->y;
4616 motion_status = FALSE;
4617 mx = ((ButtonEvent *) &event)->x;
4618 my = ((ButtonEvent *) &event)->y;
4619 if (event.type == EVENT_BUTTONPRESS)
4620 button_status = ((ButtonEvent *) &event)->button;
4622 button_status = MB_RELEASED;
4625 // this sets 'request_gadget_id'
4626 HandleGadgets(mx, my, button_status);
4628 switch (request_gadget_id)
4630 case TOOL_CTRL_ID_YES:
4631 case TOOL_CTRL_ID_TOUCH_YES:
4634 case TOOL_CTRL_ID_NO:
4635 case TOOL_CTRL_ID_TOUCH_NO:
4638 case TOOL_CTRL_ID_CONFIRM:
4639 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4640 result = TRUE | FALSE;
4643 case TOOL_CTRL_ID_PLAYER_1:
4646 case TOOL_CTRL_ID_PLAYER_2:
4649 case TOOL_CTRL_ID_PLAYER_3:
4652 case TOOL_CTRL_ID_PLAYER_4:
4657 // only check clickable animations if no request gadget clicked
4658 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4665 case SDL_WINDOWEVENT:
4666 HandleWindowEvent((WindowEvent *) &event);
4669 case SDL_APP_WILLENTERBACKGROUND:
4670 case SDL_APP_DIDENTERBACKGROUND:
4671 case SDL_APP_WILLENTERFOREGROUND:
4672 case SDL_APP_DIDENTERFOREGROUND:
4673 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4676 case EVENT_KEYPRESS:
4678 Key key = GetEventKey((KeyEvent *)&event);
4683 if (req_state & REQ_CONFIRM)
4692 #if defined(KSYM_Rewind)
4693 case KSYM_Rewind: // for Amazon Fire TV remote
4702 #if defined(KSYM_FastForward)
4703 case KSYM_FastForward: // for Amazon Fire TV remote
4709 HandleKeysDebug(key, KEY_PRESSED);
4713 if (req_state & REQ_PLAYER)
4715 int old_player_nr = setup.network_player_nr;
4718 result = old_player_nr + 1;
4723 result = old_player_nr + 1;
4754 case EVENT_FINGERRELEASE:
4755 case EVENT_KEYRELEASE:
4756 ClearPlayerAction();
4759 case SDL_CONTROLLERBUTTONDOWN:
4760 switch (event.cbutton.button)
4762 case SDL_CONTROLLER_BUTTON_A:
4763 case SDL_CONTROLLER_BUTTON_X:
4764 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4765 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4769 case SDL_CONTROLLER_BUTTON_B:
4770 case SDL_CONTROLLER_BUTTON_Y:
4771 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4772 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4773 case SDL_CONTROLLER_BUTTON_BACK:
4778 if (req_state & REQ_PLAYER)
4780 int old_player_nr = setup.network_player_nr;
4783 result = old_player_nr + 1;
4785 switch (event.cbutton.button)
4787 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4788 case SDL_CONTROLLER_BUTTON_Y:
4792 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4793 case SDL_CONTROLLER_BUTTON_B:
4797 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4798 case SDL_CONTROLLER_BUTTON_A:
4802 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4803 case SDL_CONTROLLER_BUTTON_X:
4814 case SDL_CONTROLLERBUTTONUP:
4815 HandleJoystickEvent(&event);
4816 ClearPlayerAction();
4820 HandleOtherEvents(&event);
4825 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4827 int joy = AnyJoystick();
4829 if (joy & JOY_BUTTON_1)
4831 else if (joy & JOY_BUTTON_2)
4834 else if (AnyJoystick())
4836 int joy = AnyJoystick();
4838 if (req_state & REQ_PLAYER)
4842 else if (joy & JOY_RIGHT)
4844 else if (joy & JOY_DOWN)
4846 else if (joy & JOY_LEFT)
4853 if (game_just_ended)
4855 if (global.use_envelope_request)
4857 // copy back current state of pressed buttons inside request area
4858 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4862 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4868 SetDrawtoField(draw_buffer_last);
4870 game.request_active = FALSE;
4875 static boolean RequestDoor(char *text, unsigned int req_state)
4877 int draw_buffer_last = GetDrawtoField();
4878 unsigned int old_door_state;
4879 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4880 int font_nr = FONT_TEXT_2;
4885 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4887 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4888 font_nr = FONT_TEXT_1;
4891 if (game_status == GAME_MODE_PLAYING)
4892 BlitScreenToBitmap(backbuffer);
4894 // disable deactivated drawing when quick-loading level tape recording
4895 if (tape.playing && tape.deactivate_display)
4896 TapeDeactivateDisplayOff(TRUE);
4898 SetMouseCursor(CURSOR_DEFAULT);
4900 // pause network game while waiting for request to answer
4901 if (network.enabled &&
4902 game_status == GAME_MODE_PLAYING &&
4903 !game.all_players_gone &&
4904 req_state & REQUEST_WAIT_FOR_INPUT)
4905 SendToServer_PausePlaying();
4907 old_door_state = GetDoorState();
4909 // simulate releasing mouse button over last gadget, if still pressed
4911 HandleGadgets(-1, -1, 0);
4915 // draw released gadget before proceeding
4918 if (old_door_state & DOOR_OPEN_1)
4920 CloseDoor(DOOR_CLOSE_1);
4922 // save old door content
4923 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4924 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4927 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4928 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4930 // clear door drawing field
4931 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4933 // force DOOR font inside door area
4934 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4936 // write text for request
4937 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4939 char text_line[max_request_line_len + 1];
4945 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4947 tc = *(text_ptr + tx);
4948 // if (!tc || tc == ' ')
4949 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4953 if ((tc == '?' || tc == '!') && tl == 0)
4963 strncpy(text_line, text_ptr, tl);
4966 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4967 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4968 text_line, font_nr);
4970 text_ptr += tl + (tc == ' ' ? 1 : 0);
4971 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4976 if (req_state & REQ_ASK)
4978 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4979 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4980 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4981 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4983 else if (req_state & REQ_CONFIRM)
4985 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4986 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4988 else if (req_state & REQ_PLAYER)
4990 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4991 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4992 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4993 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4996 // copy request gadgets to door backbuffer
4997 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4999 OpenDoor(DOOR_OPEN_1);
5001 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5003 if (game_status == GAME_MODE_PLAYING)
5005 SetPanelBackground();
5006 SetDrawBackgroundMask(REDRAW_DOOR_1);
5010 SetDrawBackgroundMask(REDRAW_FIELD);
5016 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5018 // ---------- handle request buttons ----------
5019 result = RequestHandleEvents(req_state, draw_buffer_last);
5023 if (!(req_state & REQ_STAY_OPEN))
5025 CloseDoor(DOOR_CLOSE_1);
5027 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
5028 (req_state & REQ_REOPEN))
5029 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
5034 if (game_status == GAME_MODE_PLAYING)
5036 SetPanelBackground();
5037 SetDrawBackgroundMask(REDRAW_DOOR_1);
5041 SetDrawBackgroundMask(REDRAW_FIELD);
5044 // continue network game after request
5045 if (network.enabled &&
5046 game_status == GAME_MODE_PLAYING &&
5047 !game.all_players_gone &&
5048 req_state & REQUEST_WAIT_FOR_INPUT)
5049 SendToServer_ContinuePlaying();
5051 // restore deactivated drawing when quick-loading level tape recording
5052 if (tape.playing && tape.deactivate_display)
5053 TapeDeactivateDisplayOn();
5058 static boolean RequestEnvelope(char *text, unsigned int req_state)
5060 int draw_buffer_last = GetDrawtoField();
5063 if (game_status == GAME_MODE_PLAYING)
5064 BlitScreenToBitmap(backbuffer);
5066 // disable deactivated drawing when quick-loading level tape recording
5067 if (tape.playing && tape.deactivate_display)
5068 TapeDeactivateDisplayOff(TRUE);
5070 SetMouseCursor(CURSOR_DEFAULT);
5072 // pause network game while waiting for request to answer
5073 if (network.enabled &&
5074 game_status == GAME_MODE_PLAYING &&
5075 !game.all_players_gone &&
5076 req_state & REQUEST_WAIT_FOR_INPUT)
5077 SendToServer_PausePlaying();
5079 // simulate releasing mouse button over last gadget, if still pressed
5081 HandleGadgets(-1, -1, 0);
5085 // (replace with setting corresponding request background)
5086 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5087 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5089 // clear door drawing field
5090 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5092 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5094 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5096 if (game_status == GAME_MODE_PLAYING)
5098 SetPanelBackground();
5099 SetDrawBackgroundMask(REDRAW_DOOR_1);
5103 SetDrawBackgroundMask(REDRAW_FIELD);
5109 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5111 // ---------- handle request buttons ----------
5112 result = RequestHandleEvents(req_state, draw_buffer_last);
5116 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5120 if (game_status == GAME_MODE_PLAYING)
5122 SetPanelBackground();
5123 SetDrawBackgroundMask(REDRAW_DOOR_1);
5127 SetDrawBackgroundMask(REDRAW_FIELD);
5130 // continue network game after request
5131 if (network.enabled &&
5132 game_status == GAME_MODE_PLAYING &&
5133 !game.all_players_gone &&
5134 req_state & REQUEST_WAIT_FOR_INPUT)
5135 SendToServer_ContinuePlaying();
5137 // restore deactivated drawing when quick-loading level tape recording
5138 if (tape.playing && tape.deactivate_display)
5139 TapeDeactivateDisplayOn();
5144 boolean Request(char *text, unsigned int req_state)
5146 boolean overlay_enabled = GetOverlayEnabled();
5149 game.request_active_or_moving = TRUE;
5151 SetOverlayEnabled(FALSE);
5153 if (global.use_envelope_request)
5154 result = RequestEnvelope(text, req_state);
5156 result = RequestDoor(text, req_state);
5158 SetOverlayEnabled(overlay_enabled);
5160 game.request_active_or_moving = FALSE;
5165 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5167 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5168 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5171 if (dpo1->sort_priority != dpo2->sort_priority)
5172 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5174 compare_result = dpo1->nr - dpo2->nr;
5176 return compare_result;
5179 void InitGraphicCompatibilityInfo_Doors(void)
5185 struct DoorInfo *door;
5189 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5190 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5192 { -1, -1, -1, NULL }
5194 struct Rect door_rect_list[] =
5196 { DX, DY, DXSIZE, DYSIZE },
5197 { VX, VY, VXSIZE, VYSIZE }
5201 for (i = 0; doors[i].door_token != -1; i++)
5203 int door_token = doors[i].door_token;
5204 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5205 int part_1 = doors[i].part_1;
5206 int part_8 = doors[i].part_8;
5207 int part_2 = part_1 + 1;
5208 int part_3 = part_1 + 2;
5209 struct DoorInfo *door = doors[i].door;
5210 struct Rect *door_rect = &door_rect_list[door_index];
5211 boolean door_gfx_redefined = FALSE;
5213 // check if any door part graphic definitions have been redefined
5215 for (j = 0; door_part_controls[j].door_token != -1; j++)
5217 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5218 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5220 if (dpc->door_token == door_token && fi->redefined)
5221 door_gfx_redefined = TRUE;
5224 // check for old-style door graphic/animation modifications
5226 if (!door_gfx_redefined)
5228 if (door->anim_mode & ANIM_STATIC_PANEL)
5230 door->panel.step_xoffset = 0;
5231 door->panel.step_yoffset = 0;
5234 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5236 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5237 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5238 int num_door_steps, num_panel_steps;
5240 // remove door part graphics other than the two default wings
5242 for (j = 0; door_part_controls[j].door_token != -1; j++)
5244 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5245 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5247 if (dpc->graphic >= part_3 &&
5248 dpc->graphic <= part_8)
5252 // set graphics and screen positions of the default wings
5254 g_part_1->width = door_rect->width;
5255 g_part_1->height = door_rect->height;
5256 g_part_2->width = door_rect->width;
5257 g_part_2->height = door_rect->height;
5258 g_part_2->src_x = door_rect->width;
5259 g_part_2->src_y = g_part_1->src_y;
5261 door->part_2.x = door->part_1.x;
5262 door->part_2.y = door->part_1.y;
5264 if (door->width != -1)
5266 g_part_1->width = door->width;
5267 g_part_2->width = door->width;
5269 // special treatment for graphics and screen position of right wing
5270 g_part_2->src_x += door_rect->width - door->width;
5271 door->part_2.x += door_rect->width - door->width;
5274 if (door->height != -1)
5276 g_part_1->height = door->height;
5277 g_part_2->height = door->height;
5279 // special treatment for graphics and screen position of bottom wing
5280 g_part_2->src_y += door_rect->height - door->height;
5281 door->part_2.y += door_rect->height - door->height;
5284 // set animation delays for the default wings and panels
5286 door->part_1.step_delay = door->step_delay;
5287 door->part_2.step_delay = door->step_delay;
5288 door->panel.step_delay = door->step_delay;
5290 // set animation draw order for the default wings
5292 door->part_1.sort_priority = 2; // draw left wing over ...
5293 door->part_2.sort_priority = 1; // ... right wing
5295 // set animation draw offset for the default wings
5297 if (door->anim_mode & ANIM_HORIZONTAL)
5299 door->part_1.step_xoffset = door->step_offset;
5300 door->part_1.step_yoffset = 0;
5301 door->part_2.step_xoffset = door->step_offset * -1;
5302 door->part_2.step_yoffset = 0;
5304 num_door_steps = g_part_1->width / door->step_offset;
5306 else // ANIM_VERTICAL
5308 door->part_1.step_xoffset = 0;
5309 door->part_1.step_yoffset = door->step_offset;
5310 door->part_2.step_xoffset = 0;
5311 door->part_2.step_yoffset = door->step_offset * -1;
5313 num_door_steps = g_part_1->height / door->step_offset;
5316 // set animation draw offset for the default panels
5318 if (door->step_offset > 1)
5320 num_panel_steps = 2 * door_rect->height / door->step_offset;
5321 door->panel.start_step = num_panel_steps - num_door_steps;
5322 door->panel.start_step_closing = door->panel.start_step;
5326 num_panel_steps = door_rect->height / door->step_offset;
5327 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5328 door->panel.start_step_closing = door->panel.start_step;
5329 door->panel.step_delay *= 2;
5336 void InitDoors(void)
5340 for (i = 0; door_part_controls[i].door_token != -1; i++)
5342 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5343 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5345 // initialize "start_step_opening" and "start_step_closing", if needed
5346 if (dpc->pos->start_step_opening == 0 &&
5347 dpc->pos->start_step_closing == 0)
5349 // dpc->pos->start_step_opening = dpc->pos->start_step;
5350 dpc->pos->start_step_closing = dpc->pos->start_step;
5353 // fill structure for door part draw order (sorted below)
5355 dpo->sort_priority = dpc->pos->sort_priority;
5358 // sort door part controls according to sort_priority and graphic number
5359 qsort(door_part_order, MAX_DOOR_PARTS,
5360 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5363 unsigned int OpenDoor(unsigned int door_state)
5365 if (door_state & DOOR_COPY_BACK)
5367 if (door_state & DOOR_OPEN_1)
5368 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5369 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5371 if (door_state & DOOR_OPEN_2)
5372 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5373 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5375 door_state &= ~DOOR_COPY_BACK;
5378 return MoveDoor(door_state);
5381 unsigned int CloseDoor(unsigned int door_state)
5383 unsigned int old_door_state = GetDoorState();
5385 if (!(door_state & DOOR_NO_COPY_BACK))
5387 if (old_door_state & DOOR_OPEN_1)
5388 BlitBitmap(backbuffer, bitmap_db_door_1,
5389 DX, DY, DXSIZE, DYSIZE, 0, 0);
5391 if (old_door_state & DOOR_OPEN_2)
5392 BlitBitmap(backbuffer, bitmap_db_door_2,
5393 VX, VY, VXSIZE, VYSIZE, 0, 0);
5395 door_state &= ~DOOR_NO_COPY_BACK;
5398 return MoveDoor(door_state);
5401 unsigned int GetDoorState(void)
5403 return MoveDoor(DOOR_GET_STATE);
5406 unsigned int SetDoorState(unsigned int door_state)
5408 return MoveDoor(door_state | DOOR_SET_STATE);
5411 static int euclid(int a, int b)
5413 return (b ? euclid(b, a % b) : a);
5416 unsigned int MoveDoor(unsigned int door_state)
5418 struct Rect door_rect_list[] =
5420 { DX, DY, DXSIZE, DYSIZE },
5421 { VX, VY, VXSIZE, VYSIZE }
5423 static int door1 = DOOR_CLOSE_1;
5424 static int door2 = DOOR_CLOSE_2;
5425 DelayCounter door_delay = { 0 };
5428 if (door_state == DOOR_GET_STATE)
5429 return (door1 | door2);
5431 if (door_state & DOOR_SET_STATE)
5433 if (door_state & DOOR_ACTION_1)
5434 door1 = door_state & DOOR_ACTION_1;
5435 if (door_state & DOOR_ACTION_2)
5436 door2 = door_state & DOOR_ACTION_2;
5438 return (door1 | door2);
5441 if (!(door_state & DOOR_FORCE_REDRAW))
5443 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5444 door_state &= ~DOOR_OPEN_1;
5445 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5446 door_state &= ~DOOR_CLOSE_1;
5447 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5448 door_state &= ~DOOR_OPEN_2;
5449 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5450 door_state &= ~DOOR_CLOSE_2;
5453 if (global.autoplay_leveldir)
5455 door_state |= DOOR_NO_DELAY;
5456 door_state &= ~DOOR_CLOSE_ALL;
5459 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5460 door_state |= DOOR_NO_DELAY;
5462 if (door_state & DOOR_ACTION)
5464 boolean door_panel_drawn[NUM_DOORS];
5465 boolean panel_has_doors[NUM_DOORS];
5466 boolean door_part_skip[MAX_DOOR_PARTS];
5467 boolean door_part_done[MAX_DOOR_PARTS];
5468 boolean door_part_done_all;
5469 int num_steps[MAX_DOOR_PARTS];
5470 int max_move_delay = 0; // delay for complete animations of all doors
5471 int max_step_delay = 0; // delay (ms) between two animation frames
5472 int num_move_steps = 0; // number of animation steps for all doors
5473 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5474 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5478 for (i = 0; i < NUM_DOORS; i++)
5479 panel_has_doors[i] = FALSE;
5481 for (i = 0; i < MAX_DOOR_PARTS; i++)
5483 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5484 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5485 int door_token = dpc->door_token;
5487 door_part_done[i] = FALSE;
5488 door_part_skip[i] = (!(door_state & door_token) ||
5492 for (i = 0; i < MAX_DOOR_PARTS; i++)
5494 int nr = door_part_order[i].nr;
5495 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5496 struct DoorPartPosInfo *pos = dpc->pos;
5497 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5498 int door_token = dpc->door_token;
5499 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5500 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5501 int step_xoffset = ABS(pos->step_xoffset);
5502 int step_yoffset = ABS(pos->step_yoffset);
5503 int step_delay = pos->step_delay;
5504 int current_door_state = door_state & door_token;
5505 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5506 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5507 boolean part_opening = (is_panel ? door_closing : door_opening);
5508 int start_step = (part_opening ? pos->start_step_opening :
5509 pos->start_step_closing);
5510 float move_xsize = (step_xoffset ? g->width : 0);
5511 float move_ysize = (step_yoffset ? g->height : 0);
5512 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5513 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5514 int move_steps = (move_xsteps && move_ysteps ?
5515 MIN(move_xsteps, move_ysteps) :
5516 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5517 int move_delay = move_steps * step_delay;
5519 if (door_part_skip[nr])
5522 max_move_delay = MAX(max_move_delay, move_delay);
5523 max_step_delay = (max_step_delay == 0 ? step_delay :
5524 euclid(max_step_delay, step_delay));
5525 num_steps[nr] = move_steps;
5529 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5531 panel_has_doors[door_index] = TRUE;
5535 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5537 num_move_steps = max_move_delay / max_step_delay;
5538 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5540 door_delay.value = max_step_delay;
5542 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5544 start = num_move_steps - 1;
5548 // opening door sound has priority over simultaneously closing door
5549 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5551 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5553 if (door_state & DOOR_OPEN_1)
5554 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5555 if (door_state & DOOR_OPEN_2)
5556 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5558 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5560 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5562 if (door_state & DOOR_CLOSE_1)
5563 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5564 if (door_state & DOOR_CLOSE_2)
5565 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5569 for (k = start; k < num_move_steps; k++)
5571 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5573 door_part_done_all = TRUE;
5575 for (i = 0; i < NUM_DOORS; i++)
5576 door_panel_drawn[i] = FALSE;
5578 for (i = 0; i < MAX_DOOR_PARTS; i++)
5580 int nr = door_part_order[i].nr;
5581 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5582 struct DoorPartPosInfo *pos = dpc->pos;
5583 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5584 int door_token = dpc->door_token;
5585 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5586 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5587 boolean is_panel_and_door_has_closed = FALSE;
5588 struct Rect *door_rect = &door_rect_list[door_index];
5589 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5591 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5592 int current_door_state = door_state & door_token;
5593 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5594 boolean door_closing = !door_opening;
5595 boolean part_opening = (is_panel ? door_closing : door_opening);
5596 boolean part_closing = !part_opening;
5597 int start_step = (part_opening ? pos->start_step_opening :
5598 pos->start_step_closing);
5599 int step_delay = pos->step_delay;
5600 int step_factor = step_delay / max_step_delay;
5601 int k1 = (step_factor ? k / step_factor + 1 : k);
5602 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5603 int kk = MAX(0, k2);
5606 int src_x, src_y, src_xx, src_yy;
5607 int dst_x, dst_y, dst_xx, dst_yy;
5610 if (door_part_skip[nr])
5613 if (!(door_state & door_token))
5621 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5622 int kk_door = MAX(0, k2_door);
5623 int sync_frame = kk_door * door_delay.value;
5624 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5626 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5627 &g_src_x, &g_src_y);
5632 if (!door_panel_drawn[door_index])
5634 ClearRectangle(drawto, door_rect->x, door_rect->y,
5635 door_rect->width, door_rect->height);
5637 door_panel_drawn[door_index] = TRUE;
5640 // draw opening or closing door parts
5642 if (pos->step_xoffset < 0) // door part on right side
5645 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5648 if (dst_xx + width > door_rect->width)
5649 width = door_rect->width - dst_xx;
5651 else // door part on left side
5654 dst_xx = pos->x - kk * pos->step_xoffset;
5658 src_xx = ABS(dst_xx);
5662 width = g->width - src_xx;
5664 if (width > door_rect->width)
5665 width = door_rect->width;
5667 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5670 if (pos->step_yoffset < 0) // door part on bottom side
5673 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5676 if (dst_yy + height > door_rect->height)
5677 height = door_rect->height - dst_yy;
5679 else // door part on top side
5682 dst_yy = pos->y - kk * pos->step_yoffset;
5686 src_yy = ABS(dst_yy);
5690 height = g->height - src_yy;
5693 src_x = g_src_x + src_xx;
5694 src_y = g_src_y + src_yy;
5696 dst_x = door_rect->x + dst_xx;
5697 dst_y = door_rect->y + dst_yy;
5699 is_panel_and_door_has_closed =
5702 panel_has_doors[door_index] &&
5703 k >= num_move_steps_doors_only - 1);
5705 if (width >= 0 && width <= g->width &&
5706 height >= 0 && height <= g->height &&
5707 !is_panel_and_door_has_closed)
5709 if (is_panel || !pos->draw_masked)
5710 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5713 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5717 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5719 if ((part_opening && (width < 0 || height < 0)) ||
5720 (part_closing && (width >= g->width && height >= g->height)))
5721 door_part_done[nr] = TRUE;
5723 // continue door part animations, but not panel after door has closed
5724 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5725 door_part_done_all = FALSE;
5728 if (!(door_state & DOOR_NO_DELAY))
5732 SkipUntilDelayReached(&door_delay, &k, last_frame);
5734 // prevent OS (Windows) from complaining about program not responding
5738 if (door_part_done_all)
5742 if (!(door_state & DOOR_NO_DELAY))
5744 // wait for specified door action post delay
5745 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5746 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5747 else if (door_state & DOOR_ACTION_1)
5748 door_delay.value = door_1.post_delay;
5749 else if (door_state & DOOR_ACTION_2)
5750 door_delay.value = door_2.post_delay;
5752 while (!DelayReached(&door_delay))
5757 if (door_state & DOOR_ACTION_1)
5758 door1 = door_state & DOOR_ACTION_1;
5759 if (door_state & DOOR_ACTION_2)
5760 door2 = door_state & DOOR_ACTION_2;
5762 // draw masked border over door area
5763 DrawMaskedBorder(REDRAW_DOOR_1);
5764 DrawMaskedBorder(REDRAW_DOOR_2);
5766 ClearAutoRepeatKeyEvents();
5768 return (door1 | door2);
5771 static boolean useSpecialEditorDoor(void)
5773 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5774 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5776 // do not draw special editor door if editor border defined or redefined
5777 if (graphic_info[graphic].bitmap != NULL || redefined)
5780 // do not draw special editor door if global border defined to be empty
5781 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5784 // do not draw special editor door if viewport definitions do not match
5788 EY + EYSIZE != VY + VYSIZE)
5794 void DrawSpecialEditorDoor(void)
5796 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5797 int top_border_width = gfx1->width;
5798 int top_border_height = gfx1->height;
5799 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5800 int ex = EX - outer_border;
5801 int ey = EY - outer_border;
5802 int vy = VY - outer_border;
5803 int exsize = EXSIZE + 2 * outer_border;
5805 if (!useSpecialEditorDoor())
5808 // draw bigger level editor toolbox window
5809 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5810 top_border_width, top_border_height, ex, ey - top_border_height);
5811 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5812 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5814 redraw_mask |= REDRAW_ALL;
5817 void UndrawSpecialEditorDoor(void)
5819 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5820 int top_border_width = gfx1->width;
5821 int top_border_height = gfx1->height;
5822 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5823 int ex = EX - outer_border;
5824 int ey = EY - outer_border;
5825 int ey_top = ey - top_border_height;
5826 int exsize = EXSIZE + 2 * outer_border;
5827 int eysize = EYSIZE + 2 * outer_border;
5829 if (!useSpecialEditorDoor())
5832 // draw normal tape recorder window
5833 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5835 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5836 ex, ey_top, top_border_width, top_border_height,
5838 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5839 ex, ey, exsize, eysize, ex, ey);
5843 // if screen background is set to "[NONE]", clear editor toolbox window
5844 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5845 ClearRectangle(drawto, ex, ey, exsize, eysize);
5848 redraw_mask |= REDRAW_ALL;
5852 // ---------- new tool button stuff -------------------------------------------
5857 struct TextPosInfo *pos;
5859 boolean is_touch_button;
5861 } toolbutton_info[NUM_TOOL_BUTTONS] =
5864 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5865 TOOL_CTRL_ID_YES, FALSE, "yes"
5868 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5869 TOOL_CTRL_ID_NO, FALSE, "no"
5872 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5873 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5876 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5877 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5880 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5881 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5884 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5885 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5888 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5889 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5892 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5893 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5896 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5897 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5900 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5901 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5905 void CreateToolButtons(void)
5909 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5911 int graphic = toolbutton_info[i].graphic;
5912 struct GraphicInfo *gfx = &graphic_info[graphic];
5913 struct TextPosInfo *pos = toolbutton_info[i].pos;
5914 struct GadgetInfo *gi;
5915 Bitmap *deco_bitmap = None;
5916 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5917 unsigned int event_mask = GD_EVENT_RELEASED;
5918 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5919 int base_x = (is_touch_button ? 0 : DX);
5920 int base_y = (is_touch_button ? 0 : DY);
5921 int gd_x = gfx->src_x;
5922 int gd_y = gfx->src_y;
5923 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5924 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5929 // do not use touch buttons if overlay touch buttons are disabled
5930 if (is_touch_button && !setup.touch.overlay_buttons)
5933 if (global.use_envelope_request && !is_touch_button)
5935 setRequestPosition(&base_x, &base_y, TRUE);
5937 // check if request buttons are outside of envelope and fix, if needed
5938 if (x < 0 || x + gfx->width > request.width ||
5939 y < 0 || y + gfx->height > request.height)
5941 if (id == TOOL_CTRL_ID_YES)
5944 y = request.height - 2 * request.border_size - gfx->height;
5946 else if (id == TOOL_CTRL_ID_NO)
5948 x = request.width - 2 * request.border_size - gfx->width;
5949 y = request.height - 2 * request.border_size - gfx->height;
5951 else if (id == TOOL_CTRL_ID_CONFIRM)
5953 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5954 y = request.height - 2 * request.border_size - gfx->height;
5956 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5958 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5960 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5961 y = request.height - 2 * request.border_size - gfx->height * 2;
5963 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5964 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5969 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5972 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5974 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5975 pos->size, &deco_bitmap, &deco_x, &deco_y);
5976 deco_xpos = (gfx->width - pos->size) / 2;
5977 deco_ypos = (gfx->height - pos->size) / 2;
5980 gi = CreateGadget(GDI_CUSTOM_ID, id,
5981 GDI_IMAGE_ID, graphic,
5982 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5985 GDI_WIDTH, gfx->width,
5986 GDI_HEIGHT, gfx->height,
5987 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5988 GDI_STATE, GD_BUTTON_UNPRESSED,
5989 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5990 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5991 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5992 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5993 GDI_DECORATION_SIZE, pos->size, pos->size,
5994 GDI_DECORATION_SHIFTING, 1, 1,
5995 GDI_DIRECT_DRAW, FALSE,
5996 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5997 GDI_EVENT_MASK, event_mask,
5998 GDI_CALLBACK_ACTION, HandleToolButtons,
6002 Fail("cannot create gadget");
6004 tool_gadget[id] = gi;
6008 void FreeToolButtons(void)
6012 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
6013 FreeGadget(tool_gadget[i]);
6016 static void UnmapToolButtons(void)
6020 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
6021 UnmapGadget(tool_gadget[i]);
6024 static void HandleToolButtons(struct GadgetInfo *gi)
6026 request_gadget_id = gi->custom_id;
6029 static struct Mapping_EM_to_RND_object
6032 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
6033 boolean is_backside; // backside of moving element
6039 em_object_mapping_list[GAME_TILE_MAX + 1] =
6042 Zborder, FALSE, FALSE,
6046 Zplayer, FALSE, FALSE,
6055 Ztank, FALSE, FALSE,
6059 Zeater, FALSE, FALSE,
6063 Zdynamite, FALSE, FALSE,
6067 Zboom, FALSE, FALSE,
6072 Xchain, FALSE, FALSE,
6073 EL_DEFAULT, ACTION_EXPLODING, -1
6076 Xboom_bug, FALSE, FALSE,
6077 EL_BUG, ACTION_EXPLODING, -1
6080 Xboom_tank, FALSE, FALSE,
6081 EL_SPACESHIP, ACTION_EXPLODING, -1
6084 Xboom_android, FALSE, FALSE,
6085 EL_EMC_ANDROID, ACTION_OTHER, -1
6088 Xboom_1, FALSE, FALSE,
6089 EL_DEFAULT, ACTION_EXPLODING, -1
6092 Xboom_2, FALSE, FALSE,
6093 EL_DEFAULT, ACTION_EXPLODING, -1
6097 Xblank, TRUE, FALSE,
6102 Xsplash_e, FALSE, FALSE,
6103 EL_ACID_SPLASH_RIGHT, -1, -1
6106 Xsplash_w, FALSE, FALSE,
6107 EL_ACID_SPLASH_LEFT, -1, -1
6111 Xplant, TRUE, FALSE,
6112 EL_EMC_PLANT, -1, -1
6115 Yplant, FALSE, FALSE,
6116 EL_EMC_PLANT, -1, -1
6120 Xacid_1, TRUE, FALSE,
6124 Xacid_2, FALSE, FALSE,
6128 Xacid_3, FALSE, FALSE,
6132 Xacid_4, FALSE, FALSE,
6136 Xacid_5, FALSE, FALSE,
6140 Xacid_6, FALSE, FALSE,
6144 Xacid_7, FALSE, FALSE,
6148 Xacid_8, FALSE, FALSE,
6153 Xfake_acid_1, TRUE, FALSE,
6154 EL_EMC_FAKE_ACID, -1, -1
6157 Xfake_acid_2, FALSE, FALSE,
6158 EL_EMC_FAKE_ACID, -1, -1
6161 Xfake_acid_3, FALSE, FALSE,
6162 EL_EMC_FAKE_ACID, -1, -1
6165 Xfake_acid_4, FALSE, FALSE,
6166 EL_EMC_FAKE_ACID, -1, -1
6169 Xfake_acid_5, FALSE, FALSE,
6170 EL_EMC_FAKE_ACID, -1, -1
6173 Xfake_acid_6, FALSE, FALSE,
6174 EL_EMC_FAKE_ACID, -1, -1
6177 Xfake_acid_7, FALSE, FALSE,
6178 EL_EMC_FAKE_ACID, -1, -1
6181 Xfake_acid_8, FALSE, FALSE,
6182 EL_EMC_FAKE_ACID, -1, -1
6186 Xfake_acid_1_player, FALSE, FALSE,
6187 EL_EMC_FAKE_ACID, -1, -1
6190 Xfake_acid_2_player, FALSE, FALSE,
6191 EL_EMC_FAKE_ACID, -1, -1
6194 Xfake_acid_3_player, FALSE, FALSE,
6195 EL_EMC_FAKE_ACID, -1, -1
6198 Xfake_acid_4_player, FALSE, FALSE,
6199 EL_EMC_FAKE_ACID, -1, -1
6202 Xfake_acid_5_player, FALSE, FALSE,
6203 EL_EMC_FAKE_ACID, -1, -1
6206 Xfake_acid_6_player, FALSE, FALSE,
6207 EL_EMC_FAKE_ACID, -1, -1
6210 Xfake_acid_7_player, FALSE, FALSE,
6211 EL_EMC_FAKE_ACID, -1, -1
6214 Xfake_acid_8_player, FALSE, FALSE,
6215 EL_EMC_FAKE_ACID, -1, -1
6219 Xgrass, TRUE, FALSE,
6220 EL_EMC_GRASS, -1, -1
6223 Ygrass_nB, FALSE, FALSE,
6224 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6227 Ygrass_eB, FALSE, FALSE,
6228 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6231 Ygrass_sB, FALSE, FALSE,
6232 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6235 Ygrass_wB, FALSE, FALSE,
6236 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6244 Ydirt_nB, FALSE, FALSE,
6245 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6248 Ydirt_eB, FALSE, FALSE,
6249 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6252 Ydirt_sB, FALSE, FALSE,
6253 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6256 Ydirt_wB, FALSE, FALSE,
6257 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6261 Xandroid, TRUE, FALSE,
6262 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6265 Xandroid_1_n, FALSE, FALSE,
6266 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6269 Xandroid_2_n, FALSE, FALSE,
6270 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6273 Xandroid_1_e, FALSE, FALSE,
6274 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6277 Xandroid_2_e, FALSE, FALSE,
6278 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6281 Xandroid_1_w, FALSE, FALSE,
6282 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6285 Xandroid_2_w, FALSE, FALSE,
6286 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6289 Xandroid_1_s, FALSE, FALSE,
6290 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6293 Xandroid_2_s, FALSE, FALSE,
6294 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6297 Yandroid_n, FALSE, FALSE,
6298 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6301 Yandroid_nB, FALSE, TRUE,
6302 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6305 Yandroid_ne, FALSE, FALSE,
6306 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6309 Yandroid_neB, FALSE, TRUE,
6310 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6313 Yandroid_e, FALSE, FALSE,
6314 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6317 Yandroid_eB, FALSE, TRUE,
6318 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6321 Yandroid_se, FALSE, FALSE,
6322 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6325 Yandroid_seB, FALSE, TRUE,
6326 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6329 Yandroid_s, FALSE, FALSE,
6330 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6333 Yandroid_sB, FALSE, TRUE,
6334 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6337 Yandroid_sw, FALSE, FALSE,
6338 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6341 Yandroid_swB, FALSE, TRUE,
6342 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6345 Yandroid_w, FALSE, FALSE,
6346 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6349 Yandroid_wB, FALSE, TRUE,
6350 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6353 Yandroid_nw, FALSE, FALSE,
6354 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6357 Yandroid_nwB, FALSE, TRUE,
6358 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6362 Xeater_n, TRUE, FALSE,
6363 EL_YAMYAM_UP, -1, -1
6366 Xeater_e, TRUE, FALSE,
6367 EL_YAMYAM_RIGHT, -1, -1
6370 Xeater_w, TRUE, FALSE,
6371 EL_YAMYAM_LEFT, -1, -1
6374 Xeater_s, TRUE, FALSE,
6375 EL_YAMYAM_DOWN, -1, -1
6378 Yeater_n, FALSE, FALSE,
6379 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6382 Yeater_nB, FALSE, TRUE,
6383 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6386 Yeater_e, FALSE, FALSE,
6387 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6390 Yeater_eB, FALSE, TRUE,
6391 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6394 Yeater_s, FALSE, FALSE,
6395 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6398 Yeater_sB, FALSE, TRUE,
6399 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6402 Yeater_w, FALSE, FALSE,
6403 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6406 Yeater_wB, FALSE, TRUE,
6407 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6410 Yeater_stone, FALSE, FALSE,
6411 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6414 Yeater_spring, FALSE, FALSE,
6415 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6419 Xalien, TRUE, FALSE,
6423 Xalien_pause, FALSE, FALSE,
6427 Yalien_n, FALSE, FALSE,
6428 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6431 Yalien_nB, FALSE, TRUE,
6432 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6435 Yalien_e, FALSE, FALSE,
6436 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6439 Yalien_eB, FALSE, TRUE,
6440 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6443 Yalien_s, FALSE, FALSE,
6444 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6447 Yalien_sB, FALSE, TRUE,
6448 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6451 Yalien_w, FALSE, FALSE,
6452 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6455 Yalien_wB, FALSE, TRUE,
6456 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6459 Yalien_stone, FALSE, FALSE,
6460 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6463 Yalien_spring, FALSE, FALSE,
6464 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6468 Xbug_1_n, TRUE, FALSE,
6472 Xbug_1_e, TRUE, FALSE,
6473 EL_BUG_RIGHT, -1, -1
6476 Xbug_1_s, TRUE, FALSE,
6480 Xbug_1_w, TRUE, FALSE,
6484 Xbug_2_n, FALSE, FALSE,
6488 Xbug_2_e, FALSE, FALSE,
6489 EL_BUG_RIGHT, -1, -1
6492 Xbug_2_s, FALSE, FALSE,
6496 Xbug_2_w, FALSE, FALSE,
6500 Ybug_n, FALSE, FALSE,
6501 EL_BUG, ACTION_MOVING, MV_BIT_UP
6504 Ybug_nB, FALSE, TRUE,
6505 EL_BUG, ACTION_MOVING, MV_BIT_UP
6508 Ybug_e, FALSE, FALSE,
6509 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6512 Ybug_eB, FALSE, TRUE,
6513 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6516 Ybug_s, FALSE, FALSE,
6517 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6520 Ybug_sB, FALSE, TRUE,
6521 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6524 Ybug_w, FALSE, FALSE,
6525 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6528 Ybug_wB, FALSE, TRUE,
6529 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6532 Ybug_w_n, FALSE, FALSE,
6533 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6536 Ybug_n_e, FALSE, FALSE,
6537 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6540 Ybug_e_s, FALSE, FALSE,
6541 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6544 Ybug_s_w, FALSE, FALSE,
6545 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6548 Ybug_e_n, FALSE, FALSE,
6549 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6552 Ybug_s_e, FALSE, FALSE,
6553 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6556 Ybug_w_s, FALSE, FALSE,
6557 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6560 Ybug_n_w, FALSE, FALSE,
6561 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6564 Ybug_stone, FALSE, FALSE,
6565 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6568 Ybug_spring, FALSE, FALSE,
6569 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6573 Xtank_1_n, TRUE, FALSE,
6574 EL_SPACESHIP_UP, -1, -1
6577 Xtank_1_e, TRUE, FALSE,
6578 EL_SPACESHIP_RIGHT, -1, -1
6581 Xtank_1_s, TRUE, FALSE,
6582 EL_SPACESHIP_DOWN, -1, -1
6585 Xtank_1_w, TRUE, FALSE,
6586 EL_SPACESHIP_LEFT, -1, -1
6589 Xtank_2_n, FALSE, FALSE,
6590 EL_SPACESHIP_UP, -1, -1
6593 Xtank_2_e, FALSE, FALSE,
6594 EL_SPACESHIP_RIGHT, -1, -1
6597 Xtank_2_s, FALSE, FALSE,
6598 EL_SPACESHIP_DOWN, -1, -1
6601 Xtank_2_w, FALSE, FALSE,
6602 EL_SPACESHIP_LEFT, -1, -1
6605 Ytank_n, FALSE, FALSE,
6606 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6609 Ytank_nB, FALSE, TRUE,
6610 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6613 Ytank_e, FALSE, FALSE,
6614 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6617 Ytank_eB, FALSE, TRUE,
6618 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6621 Ytank_s, FALSE, FALSE,
6622 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6625 Ytank_sB, FALSE, TRUE,
6626 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6629 Ytank_w, FALSE, FALSE,
6630 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6633 Ytank_wB, FALSE, TRUE,
6634 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6637 Ytank_w_n, FALSE, FALSE,
6638 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6641 Ytank_n_e, FALSE, FALSE,
6642 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6645 Ytank_e_s, FALSE, FALSE,
6646 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6649 Ytank_s_w, FALSE, FALSE,
6650 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6653 Ytank_e_n, FALSE, FALSE,
6654 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6657 Ytank_s_e, FALSE, FALSE,
6658 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6661 Ytank_w_s, FALSE, FALSE,
6662 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6665 Ytank_n_w, FALSE, FALSE,
6666 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6669 Ytank_stone, FALSE, FALSE,
6670 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6673 Ytank_spring, FALSE, FALSE,
6674 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6678 Xemerald, TRUE, FALSE,
6682 Xemerald_pause, FALSE, FALSE,
6686 Xemerald_fall, FALSE, FALSE,
6690 Xemerald_shine, FALSE, FALSE,
6691 EL_EMERALD, ACTION_TWINKLING, -1
6694 Yemerald_s, FALSE, FALSE,
6695 EL_EMERALD, ACTION_FALLING, -1
6698 Yemerald_sB, FALSE, TRUE,
6699 EL_EMERALD, ACTION_FALLING, -1
6702 Yemerald_e, FALSE, FALSE,
6703 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6706 Yemerald_eB, FALSE, TRUE,
6707 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6710 Yemerald_w, FALSE, FALSE,
6711 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6714 Yemerald_wB, FALSE, TRUE,
6715 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6718 Yemerald_blank, FALSE, FALSE,
6719 EL_EMERALD, ACTION_COLLECTING, -1
6723 Xdiamond, TRUE, FALSE,
6727 Xdiamond_pause, FALSE, FALSE,
6731 Xdiamond_fall, FALSE, FALSE,
6735 Xdiamond_shine, FALSE, FALSE,
6736 EL_DIAMOND, ACTION_TWINKLING, -1
6739 Ydiamond_s, FALSE, FALSE,
6740 EL_DIAMOND, ACTION_FALLING, -1
6743 Ydiamond_sB, FALSE, TRUE,
6744 EL_DIAMOND, ACTION_FALLING, -1
6747 Ydiamond_e, FALSE, FALSE,
6748 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6751 Ydiamond_eB, FALSE, TRUE,
6752 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6755 Ydiamond_w, FALSE, FALSE,
6756 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6759 Ydiamond_wB, FALSE, TRUE,
6760 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6763 Ydiamond_blank, FALSE, FALSE,
6764 EL_DIAMOND, ACTION_COLLECTING, -1
6767 Ydiamond_stone, FALSE, FALSE,
6768 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6772 Xstone, TRUE, FALSE,
6776 Xstone_pause, FALSE, FALSE,
6780 Xstone_fall, FALSE, FALSE,
6784 Ystone_s, FALSE, FALSE,
6785 EL_ROCK, ACTION_FALLING, -1
6788 Ystone_sB, FALSE, TRUE,
6789 EL_ROCK, ACTION_FALLING, -1
6792 Ystone_e, FALSE, FALSE,
6793 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6796 Ystone_eB, FALSE, TRUE,
6797 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6800 Ystone_w, FALSE, FALSE,
6801 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6804 Ystone_wB, FALSE, TRUE,
6805 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6813 Xbomb_pause, FALSE, FALSE,
6817 Xbomb_fall, FALSE, FALSE,
6821 Ybomb_s, FALSE, FALSE,
6822 EL_BOMB, ACTION_FALLING, -1
6825 Ybomb_sB, FALSE, TRUE,
6826 EL_BOMB, ACTION_FALLING, -1
6829 Ybomb_e, FALSE, FALSE,
6830 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6833 Ybomb_eB, FALSE, TRUE,
6834 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6837 Ybomb_w, FALSE, FALSE,
6838 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6841 Ybomb_wB, FALSE, TRUE,
6842 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6845 Ybomb_blank, FALSE, FALSE,
6846 EL_BOMB, ACTION_ACTIVATING, -1
6854 Xnut_pause, FALSE, FALSE,
6858 Xnut_fall, FALSE, FALSE,
6862 Ynut_s, FALSE, FALSE,
6863 EL_NUT, ACTION_FALLING, -1
6866 Ynut_sB, FALSE, TRUE,
6867 EL_NUT, ACTION_FALLING, -1
6870 Ynut_e, FALSE, FALSE,
6871 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6874 Ynut_eB, FALSE, TRUE,
6875 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6878 Ynut_w, FALSE, FALSE,
6879 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6882 Ynut_wB, FALSE, TRUE,
6883 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6886 Ynut_stone, FALSE, FALSE,
6887 EL_NUT, ACTION_BREAKING, -1
6891 Xspring, TRUE, FALSE,
6895 Xspring_pause, FALSE, FALSE,
6899 Xspring_e, TRUE, FALSE,
6900 EL_SPRING_RIGHT, -1, -1
6903 Xspring_w, TRUE, FALSE,
6904 EL_SPRING_LEFT, -1, -1
6907 Xspring_fall, FALSE, FALSE,
6911 Yspring_s, FALSE, FALSE,
6912 EL_SPRING, ACTION_FALLING, -1
6915 Yspring_sB, FALSE, TRUE,
6916 EL_SPRING, ACTION_FALLING, -1
6919 Yspring_e, FALSE, FALSE,
6920 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6923 Yspring_eB, FALSE, TRUE,
6924 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6927 Yspring_w, FALSE, FALSE,
6928 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6931 Yspring_wB, FALSE, TRUE,
6932 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6935 Yspring_alien_e, FALSE, FALSE,
6936 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6939 Yspring_alien_eB, FALSE, TRUE,
6940 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6943 Yspring_alien_w, FALSE, FALSE,
6944 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6947 Yspring_alien_wB, FALSE, TRUE,
6948 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6952 Xpush_emerald_e, FALSE, FALSE,
6953 EL_EMERALD, -1, MV_BIT_RIGHT
6956 Xpush_emerald_w, FALSE, FALSE,
6957 EL_EMERALD, -1, MV_BIT_LEFT
6960 Xpush_diamond_e, FALSE, FALSE,
6961 EL_DIAMOND, -1, MV_BIT_RIGHT
6964 Xpush_diamond_w, FALSE, FALSE,
6965 EL_DIAMOND, -1, MV_BIT_LEFT
6968 Xpush_stone_e, FALSE, FALSE,
6969 EL_ROCK, -1, MV_BIT_RIGHT
6972 Xpush_stone_w, FALSE, FALSE,
6973 EL_ROCK, -1, MV_BIT_LEFT
6976 Xpush_bomb_e, FALSE, FALSE,
6977 EL_BOMB, -1, MV_BIT_RIGHT
6980 Xpush_bomb_w, FALSE, FALSE,
6981 EL_BOMB, -1, MV_BIT_LEFT
6984 Xpush_nut_e, FALSE, FALSE,
6985 EL_NUT, -1, MV_BIT_RIGHT
6988 Xpush_nut_w, FALSE, FALSE,
6989 EL_NUT, -1, MV_BIT_LEFT
6992 Xpush_spring_e, FALSE, FALSE,
6993 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6996 Xpush_spring_w, FALSE, FALSE,
6997 EL_SPRING_LEFT, -1, MV_BIT_LEFT
7001 Xdynamite, TRUE, FALSE,
7002 EL_EM_DYNAMITE, -1, -1
7005 Ydynamite_blank, FALSE, FALSE,
7006 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
7009 Xdynamite_1, TRUE, FALSE,
7010 EL_EM_DYNAMITE_ACTIVE, -1, -1
7013 Xdynamite_2, FALSE, FALSE,
7014 EL_EM_DYNAMITE_ACTIVE, -1, -1
7017 Xdynamite_3, FALSE, FALSE,
7018 EL_EM_DYNAMITE_ACTIVE, -1, -1
7021 Xdynamite_4, FALSE, FALSE,
7022 EL_EM_DYNAMITE_ACTIVE, -1, -1
7026 Xkey_1, TRUE, FALSE,
7030 Xkey_2, TRUE, FALSE,
7034 Xkey_3, TRUE, FALSE,
7038 Xkey_4, TRUE, FALSE,
7042 Xkey_5, TRUE, FALSE,
7043 EL_EMC_KEY_5, -1, -1
7046 Xkey_6, TRUE, FALSE,
7047 EL_EMC_KEY_6, -1, -1
7050 Xkey_7, TRUE, FALSE,
7051 EL_EMC_KEY_7, -1, -1
7054 Xkey_8, TRUE, FALSE,
7055 EL_EMC_KEY_8, -1, -1
7059 Xdoor_1, TRUE, FALSE,
7060 EL_EM_GATE_1, -1, -1
7063 Xdoor_2, TRUE, FALSE,
7064 EL_EM_GATE_2, -1, -1
7067 Xdoor_3, TRUE, FALSE,
7068 EL_EM_GATE_3, -1, -1
7071 Xdoor_4, TRUE, FALSE,
7072 EL_EM_GATE_4, -1, -1
7075 Xdoor_5, TRUE, FALSE,
7076 EL_EMC_GATE_5, -1, -1
7079 Xdoor_6, TRUE, FALSE,
7080 EL_EMC_GATE_6, -1, -1
7083 Xdoor_7, TRUE, FALSE,
7084 EL_EMC_GATE_7, -1, -1
7087 Xdoor_8, TRUE, FALSE,
7088 EL_EMC_GATE_8, -1, -1
7092 Xfake_door_1, TRUE, FALSE,
7093 EL_EM_GATE_1_GRAY, -1, -1
7096 Xfake_door_2, TRUE, FALSE,
7097 EL_EM_GATE_2_GRAY, -1, -1
7100 Xfake_door_3, TRUE, FALSE,
7101 EL_EM_GATE_3_GRAY, -1, -1
7104 Xfake_door_4, TRUE, FALSE,
7105 EL_EM_GATE_4_GRAY, -1, -1
7108 Xfake_door_5, TRUE, FALSE,
7109 EL_EMC_GATE_5_GRAY, -1, -1
7112 Xfake_door_6, TRUE, FALSE,
7113 EL_EMC_GATE_6_GRAY, -1, -1
7116 Xfake_door_7, TRUE, FALSE,
7117 EL_EMC_GATE_7_GRAY, -1, -1
7120 Xfake_door_8, TRUE, FALSE,
7121 EL_EMC_GATE_8_GRAY, -1, -1
7125 Xballoon, TRUE, FALSE,
7129 Yballoon_n, FALSE, FALSE,
7130 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7133 Yballoon_nB, FALSE, TRUE,
7134 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7137 Yballoon_e, FALSE, FALSE,
7138 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7141 Yballoon_eB, FALSE, TRUE,
7142 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7145 Yballoon_s, FALSE, FALSE,
7146 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7149 Yballoon_sB, FALSE, TRUE,
7150 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7153 Yballoon_w, FALSE, FALSE,
7154 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7157 Yballoon_wB, FALSE, TRUE,
7158 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7162 Xball_1, TRUE, FALSE,
7163 EL_EMC_MAGIC_BALL, -1, -1
7166 Yball_1, FALSE, FALSE,
7167 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7170 Xball_2, FALSE, FALSE,
7171 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7174 Yball_2, FALSE, FALSE,
7175 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7178 Yball_blank, FALSE, FALSE,
7179 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7183 Xamoeba_1, TRUE, FALSE,
7184 EL_AMOEBA_DRY, ACTION_OTHER, -1
7187 Xamoeba_2, FALSE, FALSE,
7188 EL_AMOEBA_DRY, ACTION_OTHER, -1
7191 Xamoeba_3, FALSE, FALSE,
7192 EL_AMOEBA_DRY, ACTION_OTHER, -1
7195 Xamoeba_4, FALSE, FALSE,
7196 EL_AMOEBA_DRY, ACTION_OTHER, -1
7199 Xamoeba_5, TRUE, FALSE,
7200 EL_AMOEBA_WET, ACTION_OTHER, -1
7203 Xamoeba_6, FALSE, FALSE,
7204 EL_AMOEBA_WET, ACTION_OTHER, -1
7207 Xamoeba_7, FALSE, FALSE,
7208 EL_AMOEBA_WET, ACTION_OTHER, -1
7211 Xamoeba_8, FALSE, FALSE,
7212 EL_AMOEBA_WET, ACTION_OTHER, -1
7217 EL_AMOEBA_DROP, ACTION_GROWING, -1
7220 Xdrip_fall, FALSE, FALSE,
7221 EL_AMOEBA_DROP, -1, -1
7224 Xdrip_stretch, FALSE, FALSE,
7225 EL_AMOEBA_DROP, ACTION_FALLING, -1
7228 Xdrip_stretchB, FALSE, TRUE,
7229 EL_AMOEBA_DROP, ACTION_FALLING, -1
7232 Ydrip_1_s, FALSE, FALSE,
7233 EL_AMOEBA_DROP, ACTION_FALLING, -1
7236 Ydrip_1_sB, FALSE, TRUE,
7237 EL_AMOEBA_DROP, ACTION_FALLING, -1
7240 Ydrip_2_s, FALSE, FALSE,
7241 EL_AMOEBA_DROP, ACTION_FALLING, -1
7244 Ydrip_2_sB, FALSE, TRUE,
7245 EL_AMOEBA_DROP, ACTION_FALLING, -1
7249 Xwonderwall, TRUE, FALSE,
7250 EL_MAGIC_WALL, -1, -1
7253 Ywonderwall, FALSE, FALSE,
7254 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7258 Xwheel, TRUE, FALSE,
7259 EL_ROBOT_WHEEL, -1, -1
7262 Ywheel, FALSE, FALSE,
7263 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7267 Xswitch, TRUE, FALSE,
7268 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7271 Yswitch, FALSE, FALSE,
7272 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7276 Xbumper, TRUE, FALSE,
7277 EL_EMC_SPRING_BUMPER, -1, -1
7280 Ybumper, FALSE, FALSE,
7281 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7285 Xacid_nw, TRUE, FALSE,
7286 EL_ACID_POOL_TOPLEFT, -1, -1
7289 Xacid_ne, TRUE, FALSE,
7290 EL_ACID_POOL_TOPRIGHT, -1, -1
7293 Xacid_sw, TRUE, FALSE,
7294 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7297 Xacid_s, TRUE, FALSE,
7298 EL_ACID_POOL_BOTTOM, -1, -1
7301 Xacid_se, TRUE, FALSE,
7302 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7306 Xfake_blank, TRUE, FALSE,
7307 EL_INVISIBLE_WALL, -1, -1
7310 Yfake_blank, FALSE, FALSE,
7311 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7315 Xfake_grass, TRUE, FALSE,
7316 EL_EMC_FAKE_GRASS, -1, -1
7319 Yfake_grass, FALSE, FALSE,
7320 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7324 Xfake_amoeba, TRUE, FALSE,
7325 EL_EMC_DRIPPER, -1, -1
7328 Yfake_amoeba, FALSE, FALSE,
7329 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7333 Xlenses, TRUE, FALSE,
7334 EL_EMC_LENSES, -1, -1
7338 Xmagnify, TRUE, FALSE,
7339 EL_EMC_MAGNIFIER, -1, -1
7344 EL_QUICKSAND_EMPTY, -1, -1
7347 Xsand_stone, TRUE, FALSE,
7348 EL_QUICKSAND_FULL, -1, -1
7351 Xsand_stonein_1, FALSE, TRUE,
7352 EL_ROCK, ACTION_FILLING, -1
7355 Xsand_stonein_2, FALSE, TRUE,
7356 EL_ROCK, ACTION_FILLING, -1
7359 Xsand_stonein_3, FALSE, TRUE,
7360 EL_ROCK, ACTION_FILLING, -1
7363 Xsand_stonein_4, FALSE, TRUE,
7364 EL_ROCK, ACTION_FILLING, -1
7367 Xsand_sandstone_1, FALSE, FALSE,
7368 EL_QUICKSAND_FILLING, -1, -1
7371 Xsand_sandstone_2, FALSE, FALSE,
7372 EL_QUICKSAND_FILLING, -1, -1
7375 Xsand_sandstone_3, FALSE, FALSE,
7376 EL_QUICKSAND_FILLING, -1, -1
7379 Xsand_sandstone_4, FALSE, FALSE,
7380 EL_QUICKSAND_FILLING, -1, -1
7383 Xsand_stonesand_1, FALSE, FALSE,
7384 EL_QUICKSAND_EMPTYING, -1, -1
7387 Xsand_stonesand_2, FALSE, FALSE,
7388 EL_QUICKSAND_EMPTYING, -1, -1
7391 Xsand_stonesand_3, FALSE, FALSE,
7392 EL_QUICKSAND_EMPTYING, -1, -1
7395 Xsand_stonesand_4, FALSE, FALSE,
7396 EL_QUICKSAND_EMPTYING, -1, -1
7399 Xsand_stoneout_1, FALSE, FALSE,
7400 EL_ROCK, ACTION_EMPTYING, -1
7403 Xsand_stoneout_2, FALSE, FALSE,
7404 EL_ROCK, ACTION_EMPTYING, -1
7407 Xsand_stonesand_quickout_1, FALSE, FALSE,
7408 EL_QUICKSAND_EMPTYING, -1, -1
7411 Xsand_stonesand_quickout_2, FALSE, FALSE,
7412 EL_QUICKSAND_EMPTYING, -1, -1
7416 Xslide_ns, TRUE, FALSE,
7417 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7420 Yslide_ns_blank, FALSE, FALSE,
7421 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7424 Xslide_ew, TRUE, FALSE,
7425 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7428 Yslide_ew_blank, FALSE, FALSE,
7429 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7433 Xwind_n, TRUE, FALSE,
7434 EL_BALLOON_SWITCH_UP, -1, -1
7437 Xwind_e, TRUE, FALSE,
7438 EL_BALLOON_SWITCH_RIGHT, -1, -1
7441 Xwind_s, TRUE, FALSE,
7442 EL_BALLOON_SWITCH_DOWN, -1, -1
7445 Xwind_w, TRUE, FALSE,
7446 EL_BALLOON_SWITCH_LEFT, -1, -1
7449 Xwind_any, TRUE, FALSE,
7450 EL_BALLOON_SWITCH_ANY, -1, -1
7453 Xwind_stop, TRUE, FALSE,
7454 EL_BALLOON_SWITCH_NONE, -1, -1
7459 EL_EM_EXIT_CLOSED, -1, -1
7462 Xexit_1, TRUE, FALSE,
7463 EL_EM_EXIT_OPEN, -1, -1
7466 Xexit_2, FALSE, FALSE,
7467 EL_EM_EXIT_OPEN, -1, -1
7470 Xexit_3, FALSE, FALSE,
7471 EL_EM_EXIT_OPEN, -1, -1
7475 Xpause, FALSE, FALSE,
7480 Xwall_1, TRUE, FALSE,
7484 Xwall_2, TRUE, FALSE,
7485 EL_EMC_WALL_14, -1, -1
7488 Xwall_3, TRUE, FALSE,
7489 EL_EMC_WALL_15, -1, -1
7492 Xwall_4, TRUE, FALSE,
7493 EL_EMC_WALL_16, -1, -1
7497 Xroundwall_1, TRUE, FALSE,
7498 EL_WALL_SLIPPERY, -1, -1
7501 Xroundwall_2, TRUE, FALSE,
7502 EL_EMC_WALL_SLIPPERY_2, -1, -1
7505 Xroundwall_3, TRUE, FALSE,
7506 EL_EMC_WALL_SLIPPERY_3, -1, -1
7509 Xroundwall_4, TRUE, FALSE,
7510 EL_EMC_WALL_SLIPPERY_4, -1, -1
7514 Xsteel_1, TRUE, FALSE,
7515 EL_STEELWALL, -1, -1
7518 Xsteel_2, TRUE, FALSE,
7519 EL_EMC_STEELWALL_2, -1, -1
7522 Xsteel_3, TRUE, FALSE,
7523 EL_EMC_STEELWALL_3, -1, -1
7526 Xsteel_4, TRUE, FALSE,
7527 EL_EMC_STEELWALL_4, -1, -1
7531 Xdecor_1, TRUE, FALSE,
7532 EL_EMC_WALL_8, -1, -1
7535 Xdecor_2, TRUE, FALSE,
7536 EL_EMC_WALL_6, -1, -1
7539 Xdecor_3, TRUE, FALSE,
7540 EL_EMC_WALL_4, -1, -1
7543 Xdecor_4, TRUE, FALSE,
7544 EL_EMC_WALL_7, -1, -1
7547 Xdecor_5, TRUE, FALSE,
7548 EL_EMC_WALL_5, -1, -1
7551 Xdecor_6, TRUE, FALSE,
7552 EL_EMC_WALL_9, -1, -1
7555 Xdecor_7, TRUE, FALSE,
7556 EL_EMC_WALL_10, -1, -1
7559 Xdecor_8, TRUE, FALSE,
7560 EL_EMC_WALL_1, -1, -1
7563 Xdecor_9, TRUE, FALSE,
7564 EL_EMC_WALL_2, -1, -1
7567 Xdecor_10, TRUE, FALSE,
7568 EL_EMC_WALL_3, -1, -1
7571 Xdecor_11, TRUE, FALSE,
7572 EL_EMC_WALL_11, -1, -1
7575 Xdecor_12, TRUE, FALSE,
7576 EL_EMC_WALL_12, -1, -1
7580 Xalpha_0, TRUE, FALSE,
7581 EL_CHAR('0'), -1, -1
7584 Xalpha_1, TRUE, FALSE,
7585 EL_CHAR('1'), -1, -1
7588 Xalpha_2, TRUE, FALSE,
7589 EL_CHAR('2'), -1, -1
7592 Xalpha_3, TRUE, FALSE,
7593 EL_CHAR('3'), -1, -1
7596 Xalpha_4, TRUE, FALSE,
7597 EL_CHAR('4'), -1, -1
7600 Xalpha_5, TRUE, FALSE,
7601 EL_CHAR('5'), -1, -1
7604 Xalpha_6, TRUE, FALSE,
7605 EL_CHAR('6'), -1, -1
7608 Xalpha_7, TRUE, FALSE,
7609 EL_CHAR('7'), -1, -1
7612 Xalpha_8, TRUE, FALSE,
7613 EL_CHAR('8'), -1, -1
7616 Xalpha_9, TRUE, FALSE,
7617 EL_CHAR('9'), -1, -1
7620 Xalpha_excla, TRUE, FALSE,
7621 EL_CHAR('!'), -1, -1
7624 Xalpha_apost, TRUE, FALSE,
7625 EL_CHAR('\''), -1, -1
7628 Xalpha_comma, TRUE, FALSE,
7629 EL_CHAR(','), -1, -1
7632 Xalpha_minus, TRUE, FALSE,
7633 EL_CHAR('-'), -1, -1
7636 Xalpha_perio, TRUE, FALSE,
7637 EL_CHAR('.'), -1, -1
7640 Xalpha_colon, TRUE, FALSE,
7641 EL_CHAR(':'), -1, -1
7644 Xalpha_quest, TRUE, FALSE,
7645 EL_CHAR('?'), -1, -1
7648 Xalpha_a, TRUE, FALSE,
7649 EL_CHAR('A'), -1, -1
7652 Xalpha_b, TRUE, FALSE,
7653 EL_CHAR('B'), -1, -1
7656 Xalpha_c, TRUE, FALSE,
7657 EL_CHAR('C'), -1, -1
7660 Xalpha_d, TRUE, FALSE,
7661 EL_CHAR('D'), -1, -1
7664 Xalpha_e, TRUE, FALSE,
7665 EL_CHAR('E'), -1, -1
7668 Xalpha_f, TRUE, FALSE,
7669 EL_CHAR('F'), -1, -1
7672 Xalpha_g, TRUE, FALSE,
7673 EL_CHAR('G'), -1, -1
7676 Xalpha_h, TRUE, FALSE,
7677 EL_CHAR('H'), -1, -1
7680 Xalpha_i, TRUE, FALSE,
7681 EL_CHAR('I'), -1, -1
7684 Xalpha_j, TRUE, FALSE,
7685 EL_CHAR('J'), -1, -1
7688 Xalpha_k, TRUE, FALSE,
7689 EL_CHAR('K'), -1, -1
7692 Xalpha_l, TRUE, FALSE,
7693 EL_CHAR('L'), -1, -1
7696 Xalpha_m, TRUE, FALSE,
7697 EL_CHAR('M'), -1, -1
7700 Xalpha_n, TRUE, FALSE,
7701 EL_CHAR('N'), -1, -1
7704 Xalpha_o, TRUE, FALSE,
7705 EL_CHAR('O'), -1, -1
7708 Xalpha_p, TRUE, FALSE,
7709 EL_CHAR('P'), -1, -1
7712 Xalpha_q, TRUE, FALSE,
7713 EL_CHAR('Q'), -1, -1
7716 Xalpha_r, TRUE, FALSE,
7717 EL_CHAR('R'), -1, -1
7720 Xalpha_s, TRUE, FALSE,
7721 EL_CHAR('S'), -1, -1
7724 Xalpha_t, TRUE, FALSE,
7725 EL_CHAR('T'), -1, -1
7728 Xalpha_u, TRUE, FALSE,
7729 EL_CHAR('U'), -1, -1
7732 Xalpha_v, TRUE, FALSE,
7733 EL_CHAR('V'), -1, -1
7736 Xalpha_w, TRUE, FALSE,
7737 EL_CHAR('W'), -1, -1
7740 Xalpha_x, TRUE, FALSE,
7741 EL_CHAR('X'), -1, -1
7744 Xalpha_y, TRUE, FALSE,
7745 EL_CHAR('Y'), -1, -1
7748 Xalpha_z, TRUE, FALSE,
7749 EL_CHAR('Z'), -1, -1
7752 Xalpha_arrow_e, TRUE, FALSE,
7753 EL_CHAR('>'), -1, -1
7756 Xalpha_arrow_w, TRUE, FALSE,
7757 EL_CHAR('<'), -1, -1
7760 Xalpha_copyr, TRUE, FALSE,
7761 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7765 Ykey_1_blank, FALSE, FALSE,
7766 EL_EM_KEY_1, ACTION_COLLECTING, -1
7769 Ykey_2_blank, FALSE, FALSE,
7770 EL_EM_KEY_2, ACTION_COLLECTING, -1
7773 Ykey_3_blank, FALSE, FALSE,
7774 EL_EM_KEY_3, ACTION_COLLECTING, -1
7777 Ykey_4_blank, FALSE, FALSE,
7778 EL_EM_KEY_4, ACTION_COLLECTING, -1
7781 Ykey_5_blank, FALSE, FALSE,
7782 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7785 Ykey_6_blank, FALSE, FALSE,
7786 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7789 Ykey_7_blank, FALSE, FALSE,
7790 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7793 Ykey_8_blank, FALSE, FALSE,
7794 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7797 Ylenses_blank, FALSE, FALSE,
7798 EL_EMC_LENSES, ACTION_COLLECTING, -1
7801 Ymagnify_blank, FALSE, FALSE,
7802 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7805 Ygrass_blank, FALSE, FALSE,
7806 EL_EMC_GRASS, ACTION_SNAPPING, -1
7809 Ydirt_blank, FALSE, FALSE,
7810 EL_SAND, ACTION_SNAPPING, -1
7819 static struct Mapping_EM_to_RND_player
7828 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7832 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7836 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7840 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7844 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7848 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7852 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7856 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7860 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7864 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7868 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7872 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7876 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7880 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7884 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7888 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7892 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7896 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7900 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7904 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7908 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7912 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7916 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7920 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7924 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7928 EL_PLAYER_1, ACTION_DEFAULT, -1,
7932 EL_PLAYER_2, ACTION_DEFAULT, -1,
7936 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7940 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7944 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7948 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7952 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7956 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7960 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7964 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7968 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7972 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7976 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7980 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7984 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7988 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7992 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7996 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
8000 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
8004 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
8008 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
8012 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
8016 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
8020 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
8024 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
8028 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
8032 EL_PLAYER_3, ACTION_DEFAULT, -1,
8036 EL_PLAYER_4, ACTION_DEFAULT, -1,
8045 int map_element_RND_to_EM_cave(int element_rnd)
8047 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
8048 static boolean mapping_initialized = FALSE;
8050 if (!mapping_initialized)
8054 // return "Xalpha_quest" for all undefined elements in mapping array
8055 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8056 mapping_RND_to_EM[i] = Xalpha_quest;
8058 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8059 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
8060 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
8061 em_object_mapping_list[i].element_em;
8063 mapping_initialized = TRUE;
8066 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
8068 Warn("invalid RND level element %d", element_rnd);
8073 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
8076 int map_element_EM_to_RND_cave(int element_em_cave)
8078 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8079 static boolean mapping_initialized = FALSE;
8081 if (!mapping_initialized)
8085 // return "EL_UNKNOWN" for all undefined elements in mapping array
8086 for (i = 0; i < GAME_TILE_MAX; i++)
8087 mapping_EM_to_RND[i] = EL_UNKNOWN;
8089 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8090 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8091 em_object_mapping_list[i].element_rnd;
8093 mapping_initialized = TRUE;
8096 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8098 Warn("invalid EM cave element %d", element_em_cave);
8103 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8106 int map_element_EM_to_RND_game(int element_em_game)
8108 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8109 static boolean mapping_initialized = FALSE;
8111 if (!mapping_initialized)
8115 // return "EL_UNKNOWN" for all undefined elements in mapping array
8116 for (i = 0; i < GAME_TILE_MAX; i++)
8117 mapping_EM_to_RND[i] = EL_UNKNOWN;
8119 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8120 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8121 em_object_mapping_list[i].element_rnd;
8123 mapping_initialized = TRUE;
8126 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8128 Warn("invalid EM game element %d", element_em_game);
8133 return mapping_EM_to_RND[element_em_game];
8136 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8138 struct LevelInfo_EM *level_em = level->native_em_level;
8139 struct CAVE *cav = level_em->cav;
8142 for (i = 0; i < GAME_TILE_MAX; i++)
8143 cav->android_array[i] = Cblank;
8145 for (i = 0; i < level->num_android_clone_elements; i++)
8147 int element_rnd = level->android_clone_element[i];
8148 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8150 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8151 if (em_object_mapping_list[j].element_rnd == element_rnd)
8152 cav->android_array[em_object_mapping_list[j].element_em] =
8157 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8159 struct LevelInfo_EM *level_em = level->native_em_level;
8160 struct CAVE *cav = level_em->cav;
8163 level->num_android_clone_elements = 0;
8165 for (i = 0; i < GAME_TILE_MAX; i++)
8167 int element_em_cave = cav->android_array[i];
8169 boolean element_found = FALSE;
8171 if (element_em_cave == Cblank)
8174 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8176 for (j = 0; j < level->num_android_clone_elements; j++)
8177 if (level->android_clone_element[j] == element_rnd)
8178 element_found = TRUE;
8182 level->android_clone_element[level->num_android_clone_elements++] =
8185 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8190 if (level->num_android_clone_elements == 0)
8192 level->num_android_clone_elements = 1;
8193 level->android_clone_element[0] = EL_EMPTY;
8197 int map_direction_RND_to_EM(int direction)
8199 return (direction == MV_UP ? 0 :
8200 direction == MV_RIGHT ? 1 :
8201 direction == MV_DOWN ? 2 :
8202 direction == MV_LEFT ? 3 :
8206 int map_direction_EM_to_RND(int direction)
8208 return (direction == 0 ? MV_UP :
8209 direction == 1 ? MV_RIGHT :
8210 direction == 2 ? MV_DOWN :
8211 direction == 3 ? MV_LEFT :
8215 int map_element_RND_to_SP(int element_rnd)
8217 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8219 if (element_rnd >= EL_SP_START &&
8220 element_rnd <= EL_SP_END)
8221 element_sp = element_rnd - EL_SP_START;
8222 else if (element_rnd == EL_EMPTY_SPACE)
8224 else if (element_rnd == EL_INVISIBLE_WALL)
8230 int map_element_SP_to_RND(int element_sp)
8232 int element_rnd = EL_UNKNOWN;
8234 if (element_sp >= 0x00 &&
8236 element_rnd = EL_SP_START + element_sp;
8237 else if (element_sp == 0x28)
8238 element_rnd = EL_INVISIBLE_WALL;
8243 int map_action_SP_to_RND(int action_sp)
8247 case actActive: return ACTION_ACTIVE;
8248 case actImpact: return ACTION_IMPACT;
8249 case actExploding: return ACTION_EXPLODING;
8250 case actDigging: return ACTION_DIGGING;
8251 case actSnapping: return ACTION_SNAPPING;
8252 case actCollecting: return ACTION_COLLECTING;
8253 case actPassing: return ACTION_PASSING;
8254 case actPushing: return ACTION_PUSHING;
8255 case actDropping: return ACTION_DROPPING;
8257 default: return ACTION_DEFAULT;
8261 int map_element_RND_to_MM(int element_rnd)
8263 return (element_rnd >= EL_MM_START_1 &&
8264 element_rnd <= EL_MM_END_1 ?
8265 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8267 element_rnd >= EL_MM_START_2 &&
8268 element_rnd <= EL_MM_END_2 ?
8269 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8271 element_rnd >= EL_MM_START_3 &&
8272 element_rnd <= EL_MM_END_3 ?
8273 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
8275 element_rnd >= EL_CHAR_START &&
8276 element_rnd <= EL_CHAR_END ?
8277 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8279 element_rnd >= EL_MM_RUNTIME_START &&
8280 element_rnd <= EL_MM_RUNTIME_END ?
8281 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8283 EL_MM_EMPTY_NATIVE);
8286 int map_element_MM_to_RND(int element_mm)
8288 return (element_mm == EL_MM_EMPTY_NATIVE ||
8289 element_mm == EL_DF_EMPTY_NATIVE ?
8292 element_mm >= EL_MM_START_1_NATIVE &&
8293 element_mm <= EL_MM_END_1_NATIVE ?
8294 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8296 element_mm >= EL_MM_START_2_NATIVE &&
8297 element_mm <= EL_MM_END_2_NATIVE ?
8298 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8300 element_mm >= EL_MM_START_3_NATIVE &&
8301 element_mm <= EL_MM_END_3_NATIVE ?
8302 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
8304 element_mm >= EL_MM_CHAR_START_NATIVE &&
8305 element_mm <= EL_MM_CHAR_END_NATIVE ?
8306 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8308 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8309 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8310 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8315 int map_action_MM_to_RND(int action_mm)
8317 // all MM actions are defined to exactly match their RND counterparts
8321 int map_sound_MM_to_RND(int sound_mm)
8325 case SND_MM_GAME_LEVELTIME_CHARGING:
8326 return SND_GAME_LEVELTIME_CHARGING;
8328 case SND_MM_GAME_HEALTH_CHARGING:
8329 return SND_GAME_HEALTH_CHARGING;
8332 return SND_UNDEFINED;
8336 int map_mm_wall_element(int element)
8338 return (element >= EL_MM_STEEL_WALL_START &&
8339 element <= EL_MM_STEEL_WALL_END ?
8342 element >= EL_MM_WOODEN_WALL_START &&
8343 element <= EL_MM_WOODEN_WALL_END ?
8346 element >= EL_MM_ICE_WALL_START &&
8347 element <= EL_MM_ICE_WALL_END ?
8350 element >= EL_MM_AMOEBA_WALL_START &&
8351 element <= EL_MM_AMOEBA_WALL_END ?
8354 element >= EL_DF_STEEL_WALL_START &&
8355 element <= EL_DF_STEEL_WALL_END ?
8358 element >= EL_DF_WOODEN_WALL_START &&
8359 element <= EL_DF_WOODEN_WALL_END ?
8365 int map_mm_wall_element_editor(int element)
8369 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8370 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8371 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8372 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8373 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8374 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8376 default: return element;
8380 int get_next_element(int element)
8384 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8385 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8386 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8387 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8388 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8389 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8390 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8391 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8392 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8393 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8394 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8396 default: return element;
8400 int el2img_mm(int element_mm)
8402 return el2img(map_element_MM_to_RND(element_mm));
8405 int el_act2img_mm(int element_mm, int action)
8407 return el_act2img(map_element_MM_to_RND(element_mm), action);
8410 int el_act_dir2img(int element, int action, int direction)
8412 element = GFX_ELEMENT(element);
8413 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8415 // direction_graphic[][] == graphic[] for undefined direction graphics
8416 return element_info[element].direction_graphic[action][direction];
8419 static int el_act_dir2crm(int element, int action, int direction)
8421 element = GFX_ELEMENT(element);
8422 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8424 // direction_graphic[][] == graphic[] for undefined direction graphics
8425 return element_info[element].direction_crumbled[action][direction];
8428 int el_act2img(int element, int action)
8430 element = GFX_ELEMENT(element);
8432 return element_info[element].graphic[action];
8435 int el_act2crm(int element, int action)
8437 element = GFX_ELEMENT(element);
8439 return element_info[element].crumbled[action];
8442 int el_dir2img(int element, int direction)
8444 element = GFX_ELEMENT(element);
8446 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8449 int el2baseimg(int element)
8451 return element_info[element].graphic[ACTION_DEFAULT];
8454 int el2img(int element)
8456 element = GFX_ELEMENT(element);
8458 return element_info[element].graphic[ACTION_DEFAULT];
8461 int el2edimg(int element)
8463 element = GFX_ELEMENT(element);
8465 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8468 int el2preimg(int element)
8470 element = GFX_ELEMENT(element);
8472 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8475 int el2panelimg(int element)
8477 element = GFX_ELEMENT(element);
8479 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8482 int font2baseimg(int font_nr)
8484 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8487 int getBeltNrFromBeltElement(int element)
8489 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8490 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8491 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8494 int getBeltNrFromBeltActiveElement(int element)
8496 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8497 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8498 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8501 int getBeltNrFromBeltSwitchElement(int element)
8503 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8504 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8505 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8508 int getBeltDirNrFromBeltElement(int element)
8510 static int belt_base_element[4] =
8512 EL_CONVEYOR_BELT_1_LEFT,
8513 EL_CONVEYOR_BELT_2_LEFT,
8514 EL_CONVEYOR_BELT_3_LEFT,
8515 EL_CONVEYOR_BELT_4_LEFT
8518 int belt_nr = getBeltNrFromBeltElement(element);
8519 int belt_dir_nr = element - belt_base_element[belt_nr];
8521 return (belt_dir_nr % 3);
8524 int getBeltDirNrFromBeltSwitchElement(int element)
8526 static int belt_base_element[4] =
8528 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8529 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8530 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8531 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8534 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8535 int belt_dir_nr = element - belt_base_element[belt_nr];
8537 return (belt_dir_nr % 3);
8540 int getBeltDirFromBeltElement(int element)
8542 static int belt_move_dir[3] =
8549 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8551 return belt_move_dir[belt_dir_nr];
8554 int getBeltDirFromBeltSwitchElement(int element)
8556 static int belt_move_dir[3] =
8563 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8565 return belt_move_dir[belt_dir_nr];
8568 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8570 static int belt_base_element[4] =
8572 EL_CONVEYOR_BELT_1_LEFT,
8573 EL_CONVEYOR_BELT_2_LEFT,
8574 EL_CONVEYOR_BELT_3_LEFT,
8575 EL_CONVEYOR_BELT_4_LEFT
8578 return belt_base_element[belt_nr] + belt_dir_nr;
8581 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8583 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8585 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8588 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8590 static int belt_base_element[4] =
8592 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8593 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8594 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8595 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8598 return belt_base_element[belt_nr] + belt_dir_nr;
8601 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8603 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8605 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8608 boolean swapTiles_EM(boolean is_pre_emc_cave)
8610 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8613 boolean getTeamMode_EM(void)
8615 return game.team_mode || network_playing;
8618 boolean isActivePlayer_EM(int player_nr)
8620 return stored_player[player_nr].active;
8623 unsigned int InitRND(int seed)
8625 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8626 return InitEngineRandom_EM(seed);
8627 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8628 return InitEngineRandom_SP(seed);
8629 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8630 return InitEngineRandom_MM(seed);
8632 return InitEngineRandom_RND(seed);
8635 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8636 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8638 static int get_effective_element_EM(int tile, int frame_em)
8640 int element = object_mapping[tile].element_rnd;
8641 int action = object_mapping[tile].action;
8642 boolean is_backside = object_mapping[tile].is_backside;
8643 boolean action_removing = (action == ACTION_DIGGING ||
8644 action == ACTION_SNAPPING ||
8645 action == ACTION_COLLECTING);
8653 return (frame_em > 5 ? EL_EMPTY : element);
8659 else // frame_em == 7
8670 case Ydiamond_stone:
8674 case Xdrip_stretchB:
8690 case Ymagnify_blank:
8693 case Xsand_stonein_1:
8694 case Xsand_stonein_2:
8695 case Xsand_stonein_3:
8696 case Xsand_stonein_4:
8700 return (is_backside || action_removing ? EL_EMPTY : element);
8705 static boolean check_linear_animation_EM(int tile)
8709 case Xsand_stonesand_1:
8710 case Xsand_stonesand_quickout_1:
8711 case Xsand_sandstone_1:
8712 case Xsand_stonein_1:
8713 case Xsand_stoneout_1:
8741 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8742 boolean has_crumbled_graphics,
8743 int crumbled, int sync_frame)
8745 // if element can be crumbled, but certain action graphics are just empty
8746 // space (like instantly snapping sand to empty space in 1 frame), do not
8747 // treat these empty space graphics as crumbled graphics in EMC engine
8748 if (crumbled == IMG_EMPTY_SPACE)
8749 has_crumbled_graphics = FALSE;
8751 if (has_crumbled_graphics)
8753 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8754 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8755 g_crumbled->anim_delay,
8756 g_crumbled->anim_mode,
8757 g_crumbled->anim_start_frame,
8760 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8761 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8763 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8764 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8766 g_em->has_crumbled_graphics = TRUE;
8770 g_em->crumbled_bitmap = NULL;
8771 g_em->crumbled_src_x = 0;
8772 g_em->crumbled_src_y = 0;
8773 g_em->crumbled_border_size = 0;
8774 g_em->crumbled_tile_size = 0;
8776 g_em->has_crumbled_graphics = FALSE;
8781 void ResetGfxAnimation_EM(int x, int y, int tile)
8787 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8788 int tile, int frame_em, int x, int y)
8790 int action = object_mapping[tile].action;
8791 int direction = object_mapping[tile].direction;
8792 int effective_element = get_effective_element_EM(tile, frame_em);
8793 int graphic = (direction == MV_NONE ?
8794 el_act2img(effective_element, action) :
8795 el_act_dir2img(effective_element, action, direction));
8796 struct GraphicInfo *g = &graphic_info[graphic];
8798 boolean action_removing = (action == ACTION_DIGGING ||
8799 action == ACTION_SNAPPING ||
8800 action == ACTION_COLLECTING);
8801 boolean action_moving = (action == ACTION_FALLING ||
8802 action == ACTION_MOVING ||
8803 action == ACTION_PUSHING ||
8804 action == ACTION_EATING ||
8805 action == ACTION_FILLING ||
8806 action == ACTION_EMPTYING);
8807 boolean action_falling = (action == ACTION_FALLING ||
8808 action == ACTION_FILLING ||
8809 action == ACTION_EMPTYING);
8811 // special case: graphic uses "2nd movement tile" and has defined
8812 // 7 frames for movement animation (or less) => use default graphic
8813 // for last (8th) frame which ends the movement animation
8814 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8816 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8817 graphic = (direction == MV_NONE ?
8818 el_act2img(effective_element, action) :
8819 el_act_dir2img(effective_element, action, direction));
8821 g = &graphic_info[graphic];
8824 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8828 else if (action_moving)
8830 boolean is_backside = object_mapping[tile].is_backside;
8834 int direction = object_mapping[tile].direction;
8835 int move_dir = (action_falling ? MV_DOWN : direction);
8840 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8841 if (g->double_movement && frame_em == 0)
8845 if (move_dir == MV_LEFT)
8846 GfxFrame[x - 1][y] = GfxFrame[x][y];
8847 else if (move_dir == MV_RIGHT)
8848 GfxFrame[x + 1][y] = GfxFrame[x][y];
8849 else if (move_dir == MV_UP)
8850 GfxFrame[x][y - 1] = GfxFrame[x][y];
8851 else if (move_dir == MV_DOWN)
8852 GfxFrame[x][y + 1] = GfxFrame[x][y];
8859 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8860 if (tile == Xsand_stonesand_quickout_1 ||
8861 tile == Xsand_stonesand_quickout_2)
8865 if (graphic_info[graphic].anim_global_sync)
8866 sync_frame = FrameCounter;
8867 else if (graphic_info[graphic].anim_global_anim_sync)
8868 sync_frame = getGlobalAnimSyncFrame();
8869 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8870 sync_frame = GfxFrame[x][y];
8872 sync_frame = 0; // playfield border (pseudo steel)
8874 SetRandomAnimationValue(x, y);
8876 int frame = getAnimationFrame(g->anim_frames,
8879 g->anim_start_frame,
8882 g_em->unique_identifier =
8883 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8886 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8887 int tile, int frame_em, int x, int y)
8889 int action = object_mapping[tile].action;
8890 int direction = object_mapping[tile].direction;
8891 boolean is_backside = object_mapping[tile].is_backside;
8892 int effective_element = get_effective_element_EM(tile, frame_em);
8893 int effective_action = action;
8894 int graphic = (direction == MV_NONE ?
8895 el_act2img(effective_element, effective_action) :
8896 el_act_dir2img(effective_element, effective_action,
8898 int crumbled = (direction == MV_NONE ?
8899 el_act2crm(effective_element, effective_action) :
8900 el_act_dir2crm(effective_element, effective_action,
8902 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8903 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8904 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8905 struct GraphicInfo *g = &graphic_info[graphic];
8908 // special case: graphic uses "2nd movement tile" and has defined
8909 // 7 frames for movement animation (or less) => use default graphic
8910 // for last (8th) frame which ends the movement animation
8911 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8913 effective_action = ACTION_DEFAULT;
8914 graphic = (direction == MV_NONE ?
8915 el_act2img(effective_element, effective_action) :
8916 el_act_dir2img(effective_element, effective_action,
8918 crumbled = (direction == MV_NONE ?
8919 el_act2crm(effective_element, effective_action) :
8920 el_act_dir2crm(effective_element, effective_action,
8923 g = &graphic_info[graphic];
8926 if (graphic_info[graphic].anim_global_sync)
8927 sync_frame = FrameCounter;
8928 else if (graphic_info[graphic].anim_global_anim_sync)
8929 sync_frame = getGlobalAnimSyncFrame();
8930 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8931 sync_frame = GfxFrame[x][y];
8933 sync_frame = 0; // playfield border (pseudo steel)
8935 SetRandomAnimationValue(x, y);
8937 int frame = getAnimationFrame(g->anim_frames,
8940 g->anim_start_frame,
8943 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8944 g->double_movement && is_backside);
8946 // (updating the "crumbled" graphic definitions is probably not really needed,
8947 // as animations for crumbled graphics can't be longer than one EMC cycle)
8948 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8952 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8953 int player_nr, int anim, int frame_em)
8955 int element = player_mapping[player_nr][anim].element_rnd;
8956 int action = player_mapping[player_nr][anim].action;
8957 int direction = player_mapping[player_nr][anim].direction;
8958 int graphic = (direction == MV_NONE ?
8959 el_act2img(element, action) :
8960 el_act_dir2img(element, action, direction));
8961 struct GraphicInfo *g = &graphic_info[graphic];
8964 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8966 stored_player[player_nr].StepFrame = frame_em;
8968 sync_frame = stored_player[player_nr].Frame;
8970 int frame = getAnimationFrame(g->anim_frames,
8973 g->anim_start_frame,
8976 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8977 &g_em->src_x, &g_em->src_y, FALSE);
8980 void InitGraphicInfo_EM(void)
8984 // always start with reliable default values
8985 for (i = 0; i < GAME_TILE_MAX; i++)
8987 object_mapping[i].element_rnd = EL_UNKNOWN;
8988 object_mapping[i].is_backside = FALSE;
8989 object_mapping[i].action = ACTION_DEFAULT;
8990 object_mapping[i].direction = MV_NONE;
8993 // always start with reliable default values
8994 for (p = 0; p < MAX_PLAYERS; p++)
8996 for (i = 0; i < PLY_MAX; i++)
8998 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8999 player_mapping[p][i].action = ACTION_DEFAULT;
9000 player_mapping[p][i].direction = MV_NONE;
9004 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
9006 int e = em_object_mapping_list[i].element_em;
9008 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
9009 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
9011 if (em_object_mapping_list[i].action != -1)
9012 object_mapping[e].action = em_object_mapping_list[i].action;
9014 if (em_object_mapping_list[i].direction != -1)
9015 object_mapping[e].direction =
9016 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
9019 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
9021 int a = em_player_mapping_list[i].action_em;
9022 int p = em_player_mapping_list[i].player_nr;
9024 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
9026 if (em_player_mapping_list[i].action != -1)
9027 player_mapping[p][a].action = em_player_mapping_list[i].action;
9029 if (em_player_mapping_list[i].direction != -1)
9030 player_mapping[p][a].direction =
9031 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
9034 for (i = 0; i < GAME_TILE_MAX; i++)
9036 int element = object_mapping[i].element_rnd;
9037 int action = object_mapping[i].action;
9038 int direction = object_mapping[i].direction;
9039 boolean is_backside = object_mapping[i].is_backside;
9040 boolean action_exploding = ((action == ACTION_EXPLODING ||
9041 action == ACTION_SMASHED_BY_ROCK ||
9042 action == ACTION_SMASHED_BY_SPRING) &&
9043 element != EL_DIAMOND);
9044 boolean action_active = (action == ACTION_ACTIVE);
9045 boolean action_other = (action == ACTION_OTHER);
9047 for (j = 0; j < 8; j++)
9049 int effective_element = get_effective_element_EM(i, j);
9050 int effective_action = (j < 7 ? action :
9051 i == Xdrip_stretch ? action :
9052 i == Xdrip_stretchB ? action :
9053 i == Ydrip_1_s ? action :
9054 i == Ydrip_1_sB ? action :
9055 i == Yball_1 ? action :
9056 i == Xball_2 ? action :
9057 i == Yball_2 ? action :
9058 i == Yball_blank ? action :
9059 i == Ykey_1_blank ? action :
9060 i == Ykey_2_blank ? action :
9061 i == Ykey_3_blank ? action :
9062 i == Ykey_4_blank ? action :
9063 i == Ykey_5_blank ? action :
9064 i == Ykey_6_blank ? action :
9065 i == Ykey_7_blank ? action :
9066 i == Ykey_8_blank ? action :
9067 i == Ylenses_blank ? action :
9068 i == Ymagnify_blank ? action :
9069 i == Ygrass_blank ? action :
9070 i == Ydirt_blank ? action :
9071 i == Xsand_stonein_1 ? action :
9072 i == Xsand_stonein_2 ? action :
9073 i == Xsand_stonein_3 ? action :
9074 i == Xsand_stonein_4 ? action :
9075 i == Xsand_stoneout_1 ? action :
9076 i == Xsand_stoneout_2 ? action :
9077 i == Xboom_android ? ACTION_EXPLODING :
9078 action_exploding ? ACTION_EXPLODING :
9079 action_active ? action :
9080 action_other ? action :
9082 int graphic = (el_act_dir2img(effective_element, effective_action,
9084 int crumbled = (el_act_dir2crm(effective_element, effective_action,
9086 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
9087 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
9088 boolean has_action_graphics = (graphic != base_graphic);
9089 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9090 struct GraphicInfo *g = &graphic_info[graphic];
9091 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9094 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9095 boolean special_animation = (action != ACTION_DEFAULT &&
9096 g->anim_frames == 3 &&
9097 g->anim_delay == 2 &&
9098 g->anim_mode & ANIM_LINEAR);
9099 int sync_frame = (i == Xdrip_stretch ? 7 :
9100 i == Xdrip_stretchB ? 7 :
9101 i == Ydrip_2_s ? j + 8 :
9102 i == Ydrip_2_sB ? j + 8 :
9111 i == Xfake_acid_1 ? 0 :
9112 i == Xfake_acid_2 ? 10 :
9113 i == Xfake_acid_3 ? 20 :
9114 i == Xfake_acid_4 ? 30 :
9115 i == Xfake_acid_5 ? 40 :
9116 i == Xfake_acid_6 ? 50 :
9117 i == Xfake_acid_7 ? 60 :
9118 i == Xfake_acid_8 ? 70 :
9119 i == Xfake_acid_1_player ? 0 :
9120 i == Xfake_acid_2_player ? 10 :
9121 i == Xfake_acid_3_player ? 20 :
9122 i == Xfake_acid_4_player ? 30 :
9123 i == Xfake_acid_5_player ? 40 :
9124 i == Xfake_acid_6_player ? 50 :
9125 i == Xfake_acid_7_player ? 60 :
9126 i == Xfake_acid_8_player ? 70 :
9128 i == Yball_2 ? j + 8 :
9129 i == Yball_blank ? j + 1 :
9130 i == Ykey_1_blank ? j + 1 :
9131 i == Ykey_2_blank ? j + 1 :
9132 i == Ykey_3_blank ? j + 1 :
9133 i == Ykey_4_blank ? j + 1 :
9134 i == Ykey_5_blank ? j + 1 :
9135 i == Ykey_6_blank ? j + 1 :
9136 i == Ykey_7_blank ? j + 1 :
9137 i == Ykey_8_blank ? j + 1 :
9138 i == Ylenses_blank ? j + 1 :
9139 i == Ymagnify_blank ? j + 1 :
9140 i == Ygrass_blank ? j + 1 :
9141 i == Ydirt_blank ? j + 1 :
9142 i == Xamoeba_1 ? 0 :
9143 i == Xamoeba_2 ? 1 :
9144 i == Xamoeba_3 ? 2 :
9145 i == Xamoeba_4 ? 3 :
9146 i == Xamoeba_5 ? 0 :
9147 i == Xamoeba_6 ? 1 :
9148 i == Xamoeba_7 ? 2 :
9149 i == Xamoeba_8 ? 3 :
9150 i == Xexit_2 ? j + 8 :
9151 i == Xexit_3 ? j + 16 :
9152 i == Xdynamite_1 ? 0 :
9153 i == Xdynamite_2 ? 8 :
9154 i == Xdynamite_3 ? 16 :
9155 i == Xdynamite_4 ? 24 :
9156 i == Xsand_stonein_1 ? j + 1 :
9157 i == Xsand_stonein_2 ? j + 9 :
9158 i == Xsand_stonein_3 ? j + 17 :
9159 i == Xsand_stonein_4 ? j + 25 :
9160 i == Xsand_stoneout_1 && j == 0 ? 0 :
9161 i == Xsand_stoneout_1 && j == 1 ? 0 :
9162 i == Xsand_stoneout_1 && j == 2 ? 1 :
9163 i == Xsand_stoneout_1 && j == 3 ? 2 :
9164 i == Xsand_stoneout_1 && j == 4 ? 2 :
9165 i == Xsand_stoneout_1 && j == 5 ? 3 :
9166 i == Xsand_stoneout_1 && j == 6 ? 4 :
9167 i == Xsand_stoneout_1 && j == 7 ? 4 :
9168 i == Xsand_stoneout_2 && j == 0 ? 5 :
9169 i == Xsand_stoneout_2 && j == 1 ? 6 :
9170 i == Xsand_stoneout_2 && j == 2 ? 7 :
9171 i == Xsand_stoneout_2 && j == 3 ? 8 :
9172 i == Xsand_stoneout_2 && j == 4 ? 9 :
9173 i == Xsand_stoneout_2 && j == 5 ? 11 :
9174 i == Xsand_stoneout_2 && j == 6 ? 13 :
9175 i == Xsand_stoneout_2 && j == 7 ? 15 :
9176 i == Xboom_bug && j == 1 ? 2 :
9177 i == Xboom_bug && j == 2 ? 2 :
9178 i == Xboom_bug && j == 3 ? 4 :
9179 i == Xboom_bug && j == 4 ? 4 :
9180 i == Xboom_bug && j == 5 ? 2 :
9181 i == Xboom_bug && j == 6 ? 2 :
9182 i == Xboom_bug && j == 7 ? 0 :
9183 i == Xboom_tank && j == 1 ? 2 :
9184 i == Xboom_tank && j == 2 ? 2 :
9185 i == Xboom_tank && j == 3 ? 4 :
9186 i == Xboom_tank && j == 4 ? 4 :
9187 i == Xboom_tank && j == 5 ? 2 :
9188 i == Xboom_tank && j == 6 ? 2 :
9189 i == Xboom_tank && j == 7 ? 0 :
9190 i == Xboom_android && j == 7 ? 6 :
9191 i == Xboom_1 && j == 1 ? 2 :
9192 i == Xboom_1 && j == 2 ? 2 :
9193 i == Xboom_1 && j == 3 ? 4 :
9194 i == Xboom_1 && j == 4 ? 4 :
9195 i == Xboom_1 && j == 5 ? 6 :
9196 i == Xboom_1 && j == 6 ? 6 :
9197 i == Xboom_1 && j == 7 ? 8 :
9198 i == Xboom_2 && j == 0 ? 8 :
9199 i == Xboom_2 && j == 1 ? 8 :
9200 i == Xboom_2 && j == 2 ? 10 :
9201 i == Xboom_2 && j == 3 ? 10 :
9202 i == Xboom_2 && j == 4 ? 10 :
9203 i == Xboom_2 && j == 5 ? 12 :
9204 i == Xboom_2 && j == 6 ? 12 :
9205 i == Xboom_2 && j == 7 ? 12 :
9206 special_animation && j == 4 ? 3 :
9207 effective_action != action ? 0 :
9209 int frame = getAnimationFrame(g->anim_frames,
9212 g->anim_start_frame,
9215 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9216 g->double_movement && is_backside);
9218 g_em->bitmap = src_bitmap;
9219 g_em->src_x = src_x;
9220 g_em->src_y = src_y;
9221 g_em->src_offset_x = 0;
9222 g_em->src_offset_y = 0;
9223 g_em->dst_offset_x = 0;
9224 g_em->dst_offset_y = 0;
9225 g_em->width = TILEX;
9226 g_em->height = TILEY;
9228 g_em->preserve_background = FALSE;
9230 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9233 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9234 effective_action == ACTION_MOVING ||
9235 effective_action == ACTION_PUSHING ||
9236 effective_action == ACTION_EATING)) ||
9237 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9238 effective_action == ACTION_EMPTYING)))
9241 (effective_action == ACTION_FALLING ||
9242 effective_action == ACTION_FILLING ||
9243 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9244 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9245 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9246 int num_steps = (i == Ydrip_1_s ? 16 :
9247 i == Ydrip_1_sB ? 16 :
9248 i == Ydrip_2_s ? 16 :
9249 i == Ydrip_2_sB ? 16 :
9250 i == Xsand_stonein_1 ? 32 :
9251 i == Xsand_stonein_2 ? 32 :
9252 i == Xsand_stonein_3 ? 32 :
9253 i == Xsand_stonein_4 ? 32 :
9254 i == Xsand_stoneout_1 ? 16 :
9255 i == Xsand_stoneout_2 ? 16 : 8);
9256 int cx = ABS(dx) * (TILEX / num_steps);
9257 int cy = ABS(dy) * (TILEY / num_steps);
9258 int step_frame = (i == Ydrip_2_s ? j + 8 :
9259 i == Ydrip_2_sB ? j + 8 :
9260 i == Xsand_stonein_2 ? j + 8 :
9261 i == Xsand_stonein_3 ? j + 16 :
9262 i == Xsand_stonein_4 ? j + 24 :
9263 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9264 int step = (is_backside ? step_frame : num_steps - step_frame);
9266 if (is_backside) // tile where movement starts
9268 if (dx < 0 || dy < 0)
9270 g_em->src_offset_x = cx * step;
9271 g_em->src_offset_y = cy * step;
9275 g_em->dst_offset_x = cx * step;
9276 g_em->dst_offset_y = cy * step;
9279 else // tile where movement ends
9281 if (dx < 0 || dy < 0)
9283 g_em->dst_offset_x = cx * step;
9284 g_em->dst_offset_y = cy * step;
9288 g_em->src_offset_x = cx * step;
9289 g_em->src_offset_y = cy * step;
9293 g_em->width = TILEX - cx * step;
9294 g_em->height = TILEY - cy * step;
9297 // create unique graphic identifier to decide if tile must be redrawn
9298 /* bit 31 - 16 (16 bit): EM style graphic
9299 bit 15 - 12 ( 4 bit): EM style frame
9300 bit 11 - 6 ( 6 bit): graphic width
9301 bit 5 - 0 ( 6 bit): graphic height */
9302 g_em->unique_identifier =
9303 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9307 for (i = 0; i < GAME_TILE_MAX; i++)
9309 for (j = 0; j < 8; j++)
9311 int element = object_mapping[i].element_rnd;
9312 int action = object_mapping[i].action;
9313 int direction = object_mapping[i].direction;
9314 boolean is_backside = object_mapping[i].is_backside;
9315 int graphic_action = el_act_dir2img(element, action, direction);
9316 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9318 if ((action == ACTION_SMASHED_BY_ROCK ||
9319 action == ACTION_SMASHED_BY_SPRING ||
9320 action == ACTION_EATING) &&
9321 graphic_action == graphic_default)
9323 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9324 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9325 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9326 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9329 // no separate animation for "smashed by rock" -- use rock instead
9330 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9331 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9333 g_em->bitmap = g_xx->bitmap;
9334 g_em->src_x = g_xx->src_x;
9335 g_em->src_y = g_xx->src_y;
9336 g_em->src_offset_x = g_xx->src_offset_x;
9337 g_em->src_offset_y = g_xx->src_offset_y;
9338 g_em->dst_offset_x = g_xx->dst_offset_x;
9339 g_em->dst_offset_y = g_xx->dst_offset_y;
9340 g_em->width = g_xx->width;
9341 g_em->height = g_xx->height;
9342 g_em->unique_identifier = g_xx->unique_identifier;
9345 g_em->preserve_background = TRUE;
9350 for (p = 0; p < MAX_PLAYERS; p++)
9352 for (i = 0; i < PLY_MAX; i++)
9354 int element = player_mapping[p][i].element_rnd;
9355 int action = player_mapping[p][i].action;
9356 int direction = player_mapping[p][i].direction;
9358 for (j = 0; j < 8; j++)
9360 int effective_element = element;
9361 int effective_action = action;
9362 int graphic = (direction == MV_NONE ?
9363 el_act2img(effective_element, effective_action) :
9364 el_act_dir2img(effective_element, effective_action,
9366 struct GraphicInfo *g = &graphic_info[graphic];
9367 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9371 int frame = getAnimationFrame(g->anim_frames,
9374 g->anim_start_frame,
9377 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9379 g_em->bitmap = src_bitmap;
9380 g_em->src_x = src_x;
9381 g_em->src_y = src_y;
9382 g_em->src_offset_x = 0;
9383 g_em->src_offset_y = 0;
9384 g_em->dst_offset_x = 0;
9385 g_em->dst_offset_y = 0;
9386 g_em->width = TILEX;
9387 g_em->height = TILEY;
9393 static void CheckSaveEngineSnapshot_EM(int frame,
9394 boolean any_player_moving,
9395 boolean any_player_snapping,
9396 boolean any_player_dropping)
9398 if (frame == 7 && !any_player_dropping)
9400 if (!local_player->was_waiting)
9402 if (!CheckSaveEngineSnapshotToList())
9405 local_player->was_waiting = TRUE;
9408 else if (any_player_moving || any_player_snapping || any_player_dropping)
9410 local_player->was_waiting = FALSE;
9414 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9415 boolean murphy_is_dropping)
9417 if (murphy_is_waiting)
9419 if (!local_player->was_waiting)
9421 if (!CheckSaveEngineSnapshotToList())
9424 local_player->was_waiting = TRUE;
9429 local_player->was_waiting = FALSE;
9433 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9434 boolean button_released)
9436 if (button_released)
9438 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9439 CheckSaveEngineSnapshotToList();
9441 else if (element_clicked)
9443 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9444 CheckSaveEngineSnapshotToList();
9446 game.snapshot.changed_action = TRUE;
9450 boolean CheckSingleStepMode_EM(int frame,
9451 boolean any_player_moving,
9452 boolean any_player_snapping,
9453 boolean any_player_dropping)
9455 if (tape.single_step && tape.recording && !tape.pausing)
9456 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9457 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9459 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9460 any_player_snapping, any_player_dropping);
9462 return tape.pausing;
9465 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9466 boolean murphy_is_dropping)
9468 boolean murphy_starts_dropping = FALSE;
9471 for (i = 0; i < MAX_PLAYERS; i++)
9472 if (stored_player[i].force_dropping)
9473 murphy_starts_dropping = TRUE;
9475 if (tape.single_step && tape.recording && !tape.pausing)
9476 if (murphy_is_waiting && !murphy_starts_dropping)
9477 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9479 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9482 void CheckSingleStepMode_MM(boolean element_clicked,
9483 boolean button_released)
9485 if (tape.single_step && tape.recording && !tape.pausing)
9486 if (button_released)
9487 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9489 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9492 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9493 int graphic, int sync_frame)
9495 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9497 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9500 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9502 return (IS_NEXT_FRAME(sync_frame, graphic));
9505 int getGraphicInfo_Delay(int graphic)
9507 return graphic_info[graphic].anim_delay;
9510 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9512 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9515 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9521 void PlayMenuSoundExt(int sound)
9523 if (sound == SND_UNDEFINED)
9526 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9527 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9530 if (IS_LOOP_SOUND(sound))
9531 PlaySoundLoop(sound);
9536 void PlayMenuSound(void)
9538 PlayMenuSoundExt(menu.sound[game_status]);
9541 void PlayMenuSoundStereo(int sound, int stereo_position)
9543 if (sound == SND_UNDEFINED)
9546 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9547 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9550 if (IS_LOOP_SOUND(sound))
9551 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9553 PlaySoundStereo(sound, stereo_position);
9556 void PlayMenuSoundIfLoopExt(int sound)
9558 if (sound == SND_UNDEFINED)
9561 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9562 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9565 if (IS_LOOP_SOUND(sound))
9566 PlaySoundLoop(sound);
9569 void PlayMenuSoundIfLoop(void)
9571 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9574 void PlayMenuMusicExt(int music)
9576 if (music == MUS_UNDEFINED)
9579 if (!setup.sound_music)
9582 if (IS_LOOP_MUSIC(music))
9583 PlayMusicLoop(music);
9588 void PlayMenuMusic(void)
9590 char *curr_music = getCurrentlyPlayingMusicFilename();
9591 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9593 if (!strEqual(curr_music, next_music))
9594 PlayMenuMusicExt(menu.music[game_status]);
9597 void PlayMenuSoundsAndMusic(void)
9603 static void FadeMenuSounds(void)
9608 static void FadeMenuMusic(void)
9610 char *curr_music = getCurrentlyPlayingMusicFilename();
9611 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9613 if (!strEqual(curr_music, next_music))
9617 void FadeMenuSoundsAndMusic(void)
9623 void PlaySoundActivating(void)
9626 PlaySound(SND_MENU_ITEM_ACTIVATING);
9630 void PlaySoundSelecting(void)
9633 PlaySound(SND_MENU_ITEM_SELECTING);
9637 void ToggleFullscreenIfNeeded(void)
9639 // if setup and video fullscreen state are already matching, nothing do do
9640 if (setup.fullscreen == video.fullscreen_enabled ||
9641 !video.fullscreen_available)
9644 SDLSetWindowFullscreen(setup.fullscreen);
9646 // set setup value according to successfully changed fullscreen mode
9647 setup.fullscreen = video.fullscreen_enabled;
9650 void ChangeWindowScalingIfNeeded(void)
9652 // if setup and video window scaling are already matching, nothing do do
9653 if (setup.window_scaling_percent == video.window_scaling_percent ||
9654 video.fullscreen_enabled)
9657 SDLSetWindowScaling(setup.window_scaling_percent);
9659 // set setup value according to successfully changed window scaling
9660 setup.window_scaling_percent = video.window_scaling_percent;
9663 void ChangeVsyncModeIfNeeded(void)
9665 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9666 int video_vsync_mode = video.vsync_mode;
9668 // if setup and video vsync mode are already matching, nothing do do
9669 if (setup_vsync_mode == video_vsync_mode)
9672 // if renderer is using OpenGL, vsync mode can directly be changed
9673 SDLSetScreenVsyncMode(setup.vsync_mode);
9675 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9676 if (video.vsync_mode == video_vsync_mode)
9678 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9680 // save backbuffer content which gets lost when re-creating screen
9681 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9683 // force re-creating screen and renderer to set new vsync mode
9684 video.fullscreen_enabled = !setup.fullscreen;
9686 // when creating new renderer, destroy textures linked to old renderer
9687 FreeAllImageTextures(); // needs old renderer to free the textures
9689 // re-create screen and renderer (including change of vsync mode)
9690 ChangeVideoModeIfNeeded(setup.fullscreen);
9692 // set setup value according to successfully changed fullscreen mode
9693 setup.fullscreen = video.fullscreen_enabled;
9695 // restore backbuffer content from temporary backbuffer backup bitmap
9696 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9697 FreeBitmap(tmp_backbuffer);
9699 // update visible window/screen
9700 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9702 // when changing vsync mode, re-create textures for new renderer
9703 InitImageTextures();
9706 // set setup value according to successfully changed vsync mode
9707 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9710 static void JoinRectangles(int *x, int *y, int *width, int *height,
9711 int x2, int y2, int width2, int height2)
9713 // do not join with "off-screen" rectangle
9714 if (x2 == -1 || y2 == -1)
9719 *width = MAX(*width, width2);
9720 *height = MAX(*height, height2);
9723 void SetAnimStatus(int anim_status_new)
9725 if (anim_status_new == GAME_MODE_MAIN)
9726 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9727 else if (anim_status_new == GAME_MODE_NAMES)
9728 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9729 else if (anim_status_new == GAME_MODE_SCORES)
9730 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9732 global.anim_status_next = anim_status_new;
9734 // directly set screen modes that are entered without fading
9735 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9736 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9737 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9738 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9739 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9740 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9741 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9742 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9743 global.anim_status = global.anim_status_next;
9746 void SetGameStatus(int game_status_new)
9748 if (game_status_new != game_status)
9749 game_status_last_screen = game_status;
9751 game_status = game_status_new;
9753 SetAnimStatus(game_status_new);
9756 void SetFontStatus(int game_status_new)
9758 static int last_game_status = -1;
9760 if (game_status_new != -1)
9762 // set game status for font use after storing last game status
9763 last_game_status = game_status;
9764 game_status = game_status_new;
9768 // reset game status after font use from last stored game status
9769 game_status = last_game_status;
9773 void ResetFontStatus(void)
9778 void SetLevelSetInfo(char *identifier, int level_nr)
9780 setString(&levelset.identifier, identifier);
9782 levelset.level_nr = level_nr;
9785 boolean CheckIfAllViewportsHaveChanged(void)
9787 // if game status has not changed, viewports have not changed either
9788 if (game_status == game_status_last)
9791 // check if all viewports have changed with current game status
9793 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9794 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9795 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9796 int new_real_sx = vp_playfield->x;
9797 int new_real_sy = vp_playfield->y;
9798 int new_full_sxsize = vp_playfield->width;
9799 int new_full_sysize = vp_playfield->height;
9800 int new_dx = vp_door_1->x;
9801 int new_dy = vp_door_1->y;
9802 int new_dxsize = vp_door_1->width;
9803 int new_dysize = vp_door_1->height;
9804 int new_vx = vp_door_2->x;
9805 int new_vy = vp_door_2->y;
9806 int new_vxsize = vp_door_2->width;
9807 int new_vysize = vp_door_2->height;
9809 boolean playfield_viewport_has_changed =
9810 (new_real_sx != REAL_SX ||
9811 new_real_sy != REAL_SY ||
9812 new_full_sxsize != FULL_SXSIZE ||
9813 new_full_sysize != FULL_SYSIZE);
9815 boolean door_1_viewport_has_changed =
9818 new_dxsize != DXSIZE ||
9819 new_dysize != DYSIZE);
9821 boolean door_2_viewport_has_changed =
9824 new_vxsize != VXSIZE ||
9825 new_vysize != VYSIZE ||
9826 game_status_last == GAME_MODE_EDITOR);
9828 return (playfield_viewport_has_changed &&
9829 door_1_viewport_has_changed &&
9830 door_2_viewport_has_changed);
9833 boolean CheckFadeAll(void)
9835 return (CheckIfGlobalBorderHasChanged() ||
9836 CheckIfAllViewportsHaveChanged());
9839 void ChangeViewportPropertiesIfNeeded(void)
9841 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9842 FALSE : setup.small_game_graphics);
9843 int gfx_game_mode = getGlobalGameStatus(game_status);
9844 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9846 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9847 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9848 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9849 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9850 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9851 int new_win_xsize = vp_window->width;
9852 int new_win_ysize = vp_window->height;
9853 int border_left = vp_playfield->border_left;
9854 int border_right = vp_playfield->border_right;
9855 int border_top = vp_playfield->border_top;
9856 int border_bottom = vp_playfield->border_bottom;
9857 int new_sx = vp_playfield->x + border_left;
9858 int new_sy = vp_playfield->y + border_top;
9859 int new_sxsize = vp_playfield->width - border_left - border_right;
9860 int new_sysize = vp_playfield->height - border_top - border_bottom;
9861 int new_real_sx = vp_playfield->x;
9862 int new_real_sy = vp_playfield->y;
9863 int new_full_sxsize = vp_playfield->width;
9864 int new_full_sysize = vp_playfield->height;
9865 int new_dx = vp_door_1->x;
9866 int new_dy = vp_door_1->y;
9867 int new_dxsize = vp_door_1->width;
9868 int new_dysize = vp_door_1->height;
9869 int new_vx = vp_door_2->x;
9870 int new_vy = vp_door_2->y;
9871 int new_vxsize = vp_door_2->width;
9872 int new_vysize = vp_door_2->height;
9873 int new_ex = vp_door_3->x;
9874 int new_ey = vp_door_3->y;
9875 int new_exsize = vp_door_3->width;
9876 int new_eysize = vp_door_3->height;
9877 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9878 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9879 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9880 int new_scr_fieldx = new_sxsize / tilesize;
9881 int new_scr_fieldy = new_sysize / tilesize;
9882 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9883 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9884 boolean init_gfx_buffers = FALSE;
9885 boolean init_video_buffer = FALSE;
9886 boolean init_gadgets_and_anims = FALSE;
9887 boolean init_em_graphics = FALSE;
9889 if (new_win_xsize != WIN_XSIZE ||
9890 new_win_ysize != WIN_YSIZE)
9892 WIN_XSIZE = new_win_xsize;
9893 WIN_YSIZE = new_win_ysize;
9895 init_video_buffer = TRUE;
9896 init_gfx_buffers = TRUE;
9897 init_gadgets_and_anims = TRUE;
9899 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9902 if (new_scr_fieldx != SCR_FIELDX ||
9903 new_scr_fieldy != SCR_FIELDY)
9905 // this always toggles between MAIN and GAME when using small tile size
9907 SCR_FIELDX = new_scr_fieldx;
9908 SCR_FIELDY = new_scr_fieldy;
9910 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9921 new_sxsize != SXSIZE ||
9922 new_sysize != SYSIZE ||
9923 new_dxsize != DXSIZE ||
9924 new_dysize != DYSIZE ||
9925 new_vxsize != VXSIZE ||
9926 new_vysize != VYSIZE ||
9927 new_exsize != EXSIZE ||
9928 new_eysize != EYSIZE ||
9929 new_real_sx != REAL_SX ||
9930 new_real_sy != REAL_SY ||
9931 new_full_sxsize != FULL_SXSIZE ||
9932 new_full_sysize != FULL_SYSIZE ||
9933 new_tilesize_var != TILESIZE_VAR
9936 // ------------------------------------------------------------------------
9937 // determine next fading area for changed viewport definitions
9938 // ------------------------------------------------------------------------
9940 // start with current playfield area (default fading area)
9943 FADE_SXSIZE = FULL_SXSIZE;
9944 FADE_SYSIZE = FULL_SYSIZE;
9946 // add new playfield area if position or size has changed
9947 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9948 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9950 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9951 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9954 // add current and new door 1 area if position or size has changed
9955 if (new_dx != DX || new_dy != DY ||
9956 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9958 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9959 DX, DY, DXSIZE, DYSIZE);
9960 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9961 new_dx, new_dy, new_dxsize, new_dysize);
9964 // add current and new door 2 area if position or size has changed
9965 if (new_vx != VX || new_vy != VY ||
9966 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9968 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9969 VX, VY, VXSIZE, VYSIZE);
9970 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9971 new_vx, new_vy, new_vxsize, new_vysize);
9974 // ------------------------------------------------------------------------
9975 // handle changed tile size
9976 // ------------------------------------------------------------------------
9978 if (new_tilesize_var != TILESIZE_VAR)
9980 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9982 // changing tile size invalidates scroll values of engine snapshots
9983 FreeEngineSnapshotSingle();
9985 // changing tile size requires update of graphic mapping for EM engine
9986 init_em_graphics = TRUE;
9997 SXSIZE = new_sxsize;
9998 SYSIZE = new_sysize;
9999 DXSIZE = new_dxsize;
10000 DYSIZE = new_dysize;
10001 VXSIZE = new_vxsize;
10002 VYSIZE = new_vysize;
10003 EXSIZE = new_exsize;
10004 EYSIZE = new_eysize;
10005 REAL_SX = new_real_sx;
10006 REAL_SY = new_real_sy;
10007 FULL_SXSIZE = new_full_sxsize;
10008 FULL_SYSIZE = new_full_sysize;
10009 TILESIZE_VAR = new_tilesize_var;
10011 init_gfx_buffers = TRUE;
10012 init_gadgets_and_anims = TRUE;
10014 // Debug("tools:viewport", "viewports: init_gfx_buffers");
10015 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
10018 if (init_gfx_buffers)
10020 // Debug("tools:viewport", "init_gfx_buffers");
10022 SCR_FIELDX = new_scr_fieldx_buffers;
10023 SCR_FIELDY = new_scr_fieldy_buffers;
10027 SCR_FIELDX = new_scr_fieldx;
10028 SCR_FIELDY = new_scr_fieldy;
10030 SetDrawDeactivationMask(REDRAW_NONE);
10031 SetDrawBackgroundMask(REDRAW_FIELD);
10034 if (init_video_buffer)
10036 // Debug("tools:viewport", "init_video_buffer");
10038 FreeAllImageTextures(); // needs old renderer to free the textures
10040 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
10041 InitImageTextures();
10044 if (init_gadgets_and_anims)
10046 // Debug("tools:viewport", "init_gadgets_and_anims");
10049 InitGlobalAnimations();
10052 if (init_em_graphics)
10054 InitGraphicInfo_EM();
10058 void OpenURL(char *url)
10060 #if SDL_VERSION_ATLEAST(2,0,14)
10063 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
10064 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
10065 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
10069 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
10071 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
10075 // ============================================================================
10077 // ============================================================================
10079 #if defined(PLATFORM_WINDOWS)
10080 /* FILETIME of Jan 1 1970 00:00:00. */
10081 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
10084 * timezone information is stored outside the kernel so tzp isn't used anymore.
10086 * Note: this function is not for Win32 high precision timing purpose. See
10089 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
10091 FILETIME file_time;
10092 SYSTEMTIME system_time;
10093 ULARGE_INTEGER ularge;
10095 GetSystemTime(&system_time);
10096 SystemTimeToFileTime(&system_time, &file_time);
10097 ularge.LowPart = file_time.dwLowDateTime;
10098 ularge.HighPart = file_time.dwHighDateTime;
10100 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10101 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10107 static char *test_init_uuid_random_function_simple(void)
10109 static char seed_text[100];
10110 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10112 sprintf(seed_text, "%d", seed);
10117 static char *test_init_uuid_random_function_better(void)
10119 static char seed_text[100];
10120 struct timeval current_time;
10122 gettimeofday(¤t_time, NULL);
10124 prng_seed_bytes(¤t_time, sizeof(current_time));
10126 sprintf(seed_text, "%ld.%ld",
10127 (long)current_time.tv_sec,
10128 (long)current_time.tv_usec);
10133 #if defined(PLATFORM_WINDOWS)
10134 static char *test_init_uuid_random_function_better_windows(void)
10136 static char seed_text[100];
10137 struct timeval current_time;
10139 gettimeofday_windows(¤t_time, NULL);
10141 prng_seed_bytes(¤t_time, sizeof(current_time));
10143 sprintf(seed_text, "%ld.%ld",
10144 (long)current_time.tv_sec,
10145 (long)current_time.tv_usec);
10151 static unsigned int test_uuid_random_function_simple(int max)
10153 return GetSimpleRandom(max);
10156 static unsigned int test_uuid_random_function_better(int max)
10158 return (max > 0 ? prng_get_uint() % max : 0);
10161 #if defined(PLATFORM_WINDOWS)
10162 #define NUM_UUID_TESTS 3
10164 #define NUM_UUID_TESTS 2
10167 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10169 struct hashtable *hash_seeds =
10170 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10171 struct hashtable *hash_uuids =
10172 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10173 static char message[100];
10176 char *random_name = (nr == 0 ? "simple" : "better");
10177 char *random_type = (always_seed ? "always" : "only once");
10178 char *(*init_random_function)(void) =
10180 test_init_uuid_random_function_simple :
10181 test_init_uuid_random_function_better);
10182 unsigned int (*random_function)(int) =
10184 test_uuid_random_function_simple :
10185 test_uuid_random_function_better);
10188 #if defined(PLATFORM_WINDOWS)
10191 random_name = "windows";
10192 init_random_function = test_init_uuid_random_function_better_windows;
10198 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10199 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10201 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10202 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10203 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10205 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10209 // always initialize random number generator at least once
10210 init_random_function();
10212 unsigned int time_start = SDL_GetTicks();
10214 for (i = 0; i < num_uuids; i++)
10218 char *seed = getStringCopy(init_random_function());
10220 hashtable_remove(hash_seeds, seed);
10221 hashtable_insert(hash_seeds, seed, "1");
10224 char *uuid = getStringCopy(getUUIDExt(random_function));
10226 hashtable_remove(hash_uuids, uuid);
10227 hashtable_insert(hash_uuids, uuid, "1");
10230 int num_unique_seeds = hashtable_count(hash_seeds);
10231 int num_unique_uuids = hashtable_count(hash_uuids);
10233 unsigned int time_needed = SDL_GetTicks() - time_start;
10235 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10237 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10240 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10242 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10243 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10245 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10247 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10249 Request(message, REQ_CONFIRM);
10251 hashtable_destroy(hash_seeds, 0);
10252 hashtable_destroy(hash_uuids, 0);
10255 void TestGeneratingUUIDs(void)
10257 int num_uuids = 1000000;
10260 for (i = 0; i < NUM_UUID_TESTS; i++)
10261 for (j = 0; j < 2; j++)
10262 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10264 CloseAllAndExit(0);