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 &&
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, unsigned int req_state)
3147 DrawBuffer *drawto_last = drawto;
3148 char *text_final = text;
3149 char *text_door_style = NULL;
3150 int graphic = IMG_BACKGROUND_REQUEST;
3151 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3152 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3153 int font_nr = FONT_REQUEST;
3154 int font_width = getFontWidth(font_nr);
3155 int font_height = getFontHeight(font_nr);
3156 int border_size = request.border_size;
3157 int line_spacing = request.line_spacing;
3158 int line_height = font_height + line_spacing;
3159 int max_text_width = request.width - 2 * border_size;
3160 int max_text_height = request.height - 2 * border_size;
3161 int line_length = max_text_width / font_width;
3162 int max_lines = max_text_height / line_height;
3163 int text_width = line_length * font_width;
3164 int width = request.width;
3165 int height = request.height;
3166 int tile_size = MAX(request.step_offset, 1);
3167 int x_steps = width / tile_size;
3168 int y_steps = height / tile_size;
3169 int sx_offset = border_size;
3170 int sy_offset = border_size;
3174 if (request.centered)
3175 sx_offset = (request.width - text_width) / 2;
3177 if (request.wrap_single_words && !request.autowrap)
3179 char *src_text_ptr, *dst_text_ptr;
3181 text_door_style = checked_malloc(2 * strlen(text) + 1);
3183 src_text_ptr = text;
3184 dst_text_ptr = text_door_style;
3186 while (*src_text_ptr)
3188 if (*src_text_ptr == ' ' ||
3189 *src_text_ptr == '?' ||
3190 *src_text_ptr == '!')
3191 *dst_text_ptr++ = '\n';
3193 if (*src_text_ptr != ' ')
3194 *dst_text_ptr++ = *src_text_ptr;
3199 *dst_text_ptr = '\0';
3201 text_final = text_door_style;
3204 setRequestPosition(&sx, &sy, FALSE);
3206 // draw complete envelope request to temporary bitmap
3207 drawto = bitmap_db_store_1;
3209 ClearRectangle(drawto, sx, sy, width, height);
3211 for (y = 0; y < y_steps; y++)
3212 for (x = 0; x < x_steps; x++)
3213 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3214 x, y, x_steps, y_steps,
3215 tile_size, tile_size);
3217 // force DOOR font inside door area
3218 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3220 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3221 line_length, -1, max_lines, line_spacing, mask_mode,
3222 request.autowrap, request.centered, FALSE);
3226 if (req_state & REQ_ASK)
3228 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3229 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3230 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3231 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3233 else if (req_state & REQ_CONFIRM)
3235 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3236 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3238 else if (req_state & REQ_PLAYER)
3240 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3241 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3242 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3243 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3246 // restore pointer to drawing buffer
3247 drawto = drawto_last;
3249 // prepare complete envelope request from temporary bitmap
3250 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy, width, height);
3252 if (text_door_style)
3253 free(text_door_style);
3256 static void AnimateEnvelopeRequest(int anim_mode, int action)
3258 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
3260 int delay_value_normal = request.step_delay;
3261 int delay_value_fast = delay_value_normal / 2;
3262 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3263 boolean no_delay = (tape.warp_forward);
3264 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3265 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3266 DelayCounter anim_delay = { anim_delay_value };
3268 int tile_size = MAX(request.step_offset, 1);
3269 int max_xsize = request.width / tile_size;
3270 int max_ysize = request.height / tile_size;
3271 int max_xsize_inner = max_xsize - 2;
3272 int max_ysize_inner = max_ysize - 2;
3274 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3275 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3276 int xend = max_xsize_inner;
3277 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3278 int xstep = (xstart < xend ? 1 : 0);
3279 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3281 int end = MAX(xend - xstart, yend - ystart);
3284 if (setup.quick_doors)
3291 for (i = start; i <= end; i++)
3293 int last_frame = end; // last frame of this "for" loop
3294 int x = xstart + i * xstep;
3295 int y = ystart + i * ystep;
3296 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3297 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3298 int xsize_size_left = (xsize - 1) * tile_size;
3299 int ysize_size_top = (ysize - 1) * tile_size;
3300 int max_xsize_pos = (max_xsize - 1) * tile_size;
3301 int max_ysize_pos = (max_ysize - 1) * tile_size;
3302 int width = xsize * tile_size;
3303 int height = ysize * tile_size;
3308 if (game_just_ended)
3309 HandleGameActions();
3311 setRequestPosition(&src_x, &src_y, FALSE);
3312 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3314 for (yy = 0; yy < 2; yy++)
3316 for (xx = 0; xx < 2; xx++)
3318 int src_xx = src_x + xx * max_xsize_pos;
3319 int src_yy = src_y + yy * max_ysize_pos;
3320 int dst_xx = dst_x + xx * xsize_size_left;
3321 int dst_yy = dst_y + yy * ysize_size_top;
3322 int xx_size = (xx ? tile_size : xsize_size_left);
3323 int yy_size = (yy ? tile_size : ysize_size_top);
3325 // draw partial (animated) envelope request to temporary bitmap
3326 BlitBitmap(bitmap_db_store_1, bitmap_db_store_2,
3327 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3331 // prepare partial (animated) envelope request from temporary bitmap
3332 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, dst_x, dst_y,
3335 redraw_mask |= REDRAW_FIELD;
3339 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3342 ClearAutoRepeatKeyEvents();
3345 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3347 int graphic = IMG_BACKGROUND_REQUEST;
3348 int sound_opening = SND_REQUEST_OPENING;
3349 int sound_closing = SND_REQUEST_CLOSING;
3350 int anim_mode_1 = request.anim_mode; // (higher priority)
3351 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3352 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3353 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3354 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3356 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3358 if (action == ACTION_OPENING)
3360 DrawEnvelopeRequest(text, req_state);
3362 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3364 if (anim_mode == ANIM_DEFAULT)
3365 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3367 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3371 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3373 if (anim_mode != ANIM_NONE)
3374 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3376 if (anim_mode == ANIM_DEFAULT)
3377 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3380 game.envelope_active = FALSE;
3383 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3385 if (IS_MM_WALL(element))
3387 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3393 int graphic = el2preimg(element);
3395 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3396 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3401 void DrawLevel(int draw_background_mask)
3405 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3406 SetDrawBackgroundMask(draw_background_mask);
3410 for (x = BX1; x <= BX2; x++)
3411 for (y = BY1; y <= BY2; y++)
3412 DrawScreenField(x, y);
3414 redraw_mask |= REDRAW_FIELD;
3417 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3422 for (x = 0; x < size_x; x++)
3423 for (y = 0; y < size_y; y++)
3424 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3426 redraw_mask |= REDRAW_FIELD;
3429 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3433 for (x = 0; x < size_x; x++)
3434 for (y = 0; y < size_y; y++)
3435 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3437 redraw_mask |= REDRAW_FIELD;
3440 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3442 boolean show_level_border = (BorderElement != EL_EMPTY);
3443 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3444 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3445 int tile_size = preview.tile_size;
3446 int preview_width = preview.xsize * tile_size;
3447 int preview_height = preview.ysize * tile_size;
3448 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3449 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3450 int real_preview_width = real_preview_xsize * tile_size;
3451 int real_preview_height = real_preview_ysize * tile_size;
3452 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3453 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3456 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3459 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3461 dst_x += (preview_width - real_preview_width) / 2;
3462 dst_y += (preview_height - real_preview_height) / 2;
3464 for (x = 0; x < real_preview_xsize; x++)
3466 for (y = 0; y < real_preview_ysize; y++)
3468 int lx = from_x + x + (show_level_border ? -1 : 0);
3469 int ly = from_y + y + (show_level_border ? -1 : 0);
3470 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3471 getBorderElement(lx, ly));
3473 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3474 element, tile_size);
3478 redraw_mask |= REDRAW_FIELD;
3481 #define MICROLABEL_EMPTY 0
3482 #define MICROLABEL_LEVEL_NAME 1
3483 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3484 #define MICROLABEL_LEVEL_AUTHOR 3
3485 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3486 #define MICROLABEL_IMPORTED_FROM 5
3487 #define MICROLABEL_IMPORTED_BY_HEAD 6
3488 #define MICROLABEL_IMPORTED_BY 7
3490 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3492 int max_text_width = SXSIZE;
3493 int font_width = getFontWidth(font_nr);
3495 if (pos->align == ALIGN_CENTER)
3496 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3497 else if (pos->align == ALIGN_RIGHT)
3498 max_text_width = pos->x;
3500 max_text_width = SXSIZE - pos->x;
3502 return max_text_width / font_width;
3505 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3507 char label_text[MAX_OUTPUT_LINESIZE + 1];
3508 int max_len_label_text;
3509 int font_nr = pos->font;
3512 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3515 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3516 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3517 mode == MICROLABEL_IMPORTED_BY_HEAD)
3518 font_nr = pos->font_alt;
3520 max_len_label_text = getMaxTextLength(pos, font_nr);
3522 if (pos->size != -1)
3523 max_len_label_text = pos->size;
3525 for (i = 0; i < max_len_label_text; i++)
3526 label_text[i] = ' ';
3527 label_text[max_len_label_text] = '\0';
3529 if (strlen(label_text) > 0)
3530 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3533 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3534 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3535 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3536 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3537 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3538 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3539 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3540 max_len_label_text);
3541 label_text[max_len_label_text] = '\0';
3543 if (strlen(label_text) > 0)
3544 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3546 redraw_mask |= REDRAW_FIELD;
3549 static void DrawPreviewLevelLabel(int mode)
3551 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3554 static void DrawPreviewLevelInfo(int mode)
3556 if (mode == MICROLABEL_LEVEL_NAME)
3557 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3558 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3559 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3562 static void DrawPreviewLevelExt(boolean restart)
3564 static DelayCounter scroll_delay = { 0 };
3565 static DelayCounter label_delay = { 0 };
3566 static int from_x, from_y, scroll_direction;
3567 static int label_state, label_counter;
3568 boolean show_level_border = (BorderElement != EL_EMPTY);
3569 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3570 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3572 scroll_delay.value = preview.step_delay;
3573 label_delay.value = MICROLEVEL_LABEL_DELAY;
3580 if (preview.anim_mode == ANIM_CENTERED)
3582 if (level_xsize > preview.xsize)
3583 from_x = (level_xsize - preview.xsize) / 2;
3584 if (level_ysize > preview.ysize)
3585 from_y = (level_ysize - preview.ysize) / 2;
3588 from_x += preview.xoffset;
3589 from_y += preview.yoffset;
3591 scroll_direction = MV_RIGHT;
3595 DrawPreviewLevelPlayfield(from_x, from_y);
3596 DrawPreviewLevelLabel(label_state);
3598 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3599 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3601 // initialize delay counters
3602 ResetDelayCounter(&scroll_delay);
3603 ResetDelayCounter(&label_delay);
3605 if (leveldir_current->name)
3607 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3608 char label_text[MAX_OUTPUT_LINESIZE + 1];
3609 int font_nr = pos->font;
3610 int max_len_label_text = getMaxTextLength(pos, font_nr);
3612 if (pos->size != -1)
3613 max_len_label_text = pos->size;
3615 strncpy(label_text, leveldir_current->name, max_len_label_text);
3616 label_text[max_len_label_text] = '\0';
3618 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3619 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3625 // scroll preview level, if needed
3626 if (preview.anim_mode != ANIM_NONE &&
3627 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3628 DelayReached(&scroll_delay))
3630 switch (scroll_direction)
3635 from_x -= preview.step_offset;
3636 from_x = (from_x < 0 ? 0 : from_x);
3639 scroll_direction = MV_UP;
3643 if (from_x < level_xsize - preview.xsize)
3645 from_x += preview.step_offset;
3646 from_x = (from_x > level_xsize - preview.xsize ?
3647 level_xsize - preview.xsize : from_x);
3650 scroll_direction = MV_DOWN;
3656 from_y -= preview.step_offset;
3657 from_y = (from_y < 0 ? 0 : from_y);
3660 scroll_direction = MV_RIGHT;
3664 if (from_y < level_ysize - preview.ysize)
3666 from_y += preview.step_offset;
3667 from_y = (from_y > level_ysize - preview.ysize ?
3668 level_ysize - preview.ysize : from_y);
3671 scroll_direction = MV_LEFT;
3678 DrawPreviewLevelPlayfield(from_x, from_y);
3681 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3682 // redraw micro level label, if needed
3683 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3684 !strEqual(level.author, ANONYMOUS_NAME) &&
3685 !strEqual(level.author, leveldir_current->name) &&
3686 DelayReached(&label_delay))
3688 int max_label_counter = 23;
3690 if (leveldir_current->imported_from != NULL &&
3691 strlen(leveldir_current->imported_from) > 0)
3692 max_label_counter += 14;
3693 if (leveldir_current->imported_by != NULL &&
3694 strlen(leveldir_current->imported_by) > 0)
3695 max_label_counter += 14;
3697 label_counter = (label_counter + 1) % max_label_counter;
3698 label_state = (label_counter >= 0 && label_counter <= 7 ?
3699 MICROLABEL_LEVEL_NAME :
3700 label_counter >= 9 && label_counter <= 12 ?
3701 MICROLABEL_LEVEL_AUTHOR_HEAD :
3702 label_counter >= 14 && label_counter <= 21 ?
3703 MICROLABEL_LEVEL_AUTHOR :
3704 label_counter >= 23 && label_counter <= 26 ?
3705 MICROLABEL_IMPORTED_FROM_HEAD :
3706 label_counter >= 28 && label_counter <= 35 ?
3707 MICROLABEL_IMPORTED_FROM :
3708 label_counter >= 37 && label_counter <= 40 ?
3709 MICROLABEL_IMPORTED_BY_HEAD :
3710 label_counter >= 42 && label_counter <= 49 ?
3711 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3713 if (leveldir_current->imported_from == NULL &&
3714 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3715 label_state == MICROLABEL_IMPORTED_FROM))
3716 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3717 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3719 DrawPreviewLevelLabel(label_state);
3723 void DrawPreviewPlayers(void)
3725 if (game_status != GAME_MODE_MAIN)
3728 // do not draw preview players if level preview redefined, but players aren't
3729 if (preview.redefined && !menu.main.preview_players.redefined)
3732 boolean player_found[MAX_PLAYERS];
3733 int num_players = 0;
3736 for (i = 0; i < MAX_PLAYERS; i++)
3737 player_found[i] = FALSE;
3739 // check which players can be found in the level (simple approach)
3740 for (x = 0; x < lev_fieldx; x++)
3742 for (y = 0; y < lev_fieldy; y++)
3744 int element = level.field[x][y];
3746 if (IS_PLAYER_ELEMENT(element))
3748 int player_nr = GET_PLAYER_NR(element);
3750 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3752 if (!player_found[player_nr])
3755 player_found[player_nr] = TRUE;
3760 struct TextPosInfo *pos = &menu.main.preview_players;
3761 int tile_size = pos->tile_size;
3762 int border_size = pos->border_size;
3763 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3764 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3765 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3766 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3767 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3768 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3769 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3770 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3771 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3772 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3773 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3774 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3776 // clear area in which the players will be drawn
3777 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3778 max_players_width, max_players_height);
3780 if (!network.enabled && !setup.team_mode)
3783 // only draw players if level is suited for team mode
3784 if (num_players < 2)
3787 // draw all players that were found in the level
3788 for (i = 0; i < MAX_PLAYERS; i++)
3790 if (player_found[i])
3792 int graphic = el2img(EL_PLAYER_1 + i);
3794 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3796 xpos += player_xoffset;
3797 ypos += player_yoffset;
3802 void DrawPreviewLevelInitial(void)
3804 DrawPreviewLevelExt(TRUE);
3805 DrawPreviewPlayers();
3808 void DrawPreviewLevelAnimation(void)
3810 DrawPreviewLevelExt(FALSE);
3813 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3814 int border_size, int font_nr)
3816 int graphic = el2img(EL_PLAYER_1 + player_nr);
3817 int font_height = getFontHeight(font_nr);
3818 int player_height = MAX(tile_size, font_height);
3819 int xoffset_text = tile_size + border_size;
3820 int yoffset_text = (player_height - font_height) / 2;
3821 int yoffset_graphic = (player_height - tile_size) / 2;
3822 char *player_name = getNetworkPlayerName(player_nr + 1);
3824 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3826 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3829 static void DrawNetworkPlayersExt(boolean force)
3831 if (game_status != GAME_MODE_MAIN)
3834 if (!network.connected && !force)
3837 // do not draw network players if level preview redefined, but players aren't
3838 if (preview.redefined && !menu.main.network_players.redefined)
3841 int num_players = 0;
3844 for (i = 0; i < MAX_PLAYERS; i++)
3845 if (stored_player[i].connected_network)
3848 struct TextPosInfo *pos = &menu.main.network_players;
3849 int tile_size = pos->tile_size;
3850 int border_size = pos->border_size;
3851 int xoffset_text = tile_size + border_size;
3852 int font_nr = pos->font;
3853 int font_width = getFontWidth(font_nr);
3854 int font_height = getFontHeight(font_nr);
3855 int player_height = MAX(tile_size, font_height);
3856 int player_yoffset = player_height + border_size;
3857 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3858 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3859 int all_players_height = num_players * player_yoffset - border_size;
3860 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3861 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3862 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3864 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3865 max_players_width, max_players_height);
3867 // first draw local network player ...
3868 for (i = 0; i < MAX_PLAYERS; i++)
3870 if (stored_player[i].connected_network &&
3871 stored_player[i].connected_locally)
3873 char *player_name = getNetworkPlayerName(i + 1);
3874 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3875 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3877 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3879 ypos += player_yoffset;
3883 // ... then draw all other network players
3884 for (i = 0; i < MAX_PLAYERS; i++)
3886 if (stored_player[i].connected_network &&
3887 !stored_player[i].connected_locally)
3889 char *player_name = getNetworkPlayerName(i + 1);
3890 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3891 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3893 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3895 ypos += player_yoffset;
3900 void DrawNetworkPlayers(void)
3902 DrawNetworkPlayersExt(FALSE);
3905 void ClearNetworkPlayers(void)
3907 DrawNetworkPlayersExt(TRUE);
3910 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3911 int graphic, int lx, int ly,
3914 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3916 if (mask_mode == USE_MASKING)
3917 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3919 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3922 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3923 int graphic, int sync_frame, int mask_mode)
3925 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3927 if (mask_mode == USE_MASKING)
3928 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3930 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3933 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3934 int graphic, int sync_frame, int tilesize,
3937 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3939 if (mask_mode == USE_MASKING)
3940 DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
3942 DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
3945 static void DrawGraphicAnimation(int x, int y, int graphic)
3947 int lx = LEVELX(x), ly = LEVELY(y);
3948 int mask_mode = NO_MASKING;
3950 if (!IN_SCR_FIELD(x, y))
3953 if (game.use_masked_elements)
3955 if (Tile[lx][ly] != EL_EMPTY)
3957 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3959 mask_mode = USE_MASKING;
3963 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3964 graphic, lx, ly, mask_mode);
3966 MarkTileDirty(x, y);
3969 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3971 int lx = LEVELX(x), ly = LEVELY(y);
3972 int mask_mode = NO_MASKING;
3974 if (!IN_SCR_FIELD(x, y))
3977 if (game.use_masked_elements)
3979 if (Tile[lx][ly] != EL_EMPTY)
3981 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3983 mask_mode = USE_MASKING;
3987 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3988 graphic, lx, ly, mask_mode);
3990 MarkTileDirty(x, y);
3993 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3995 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3998 void DrawLevelElementAnimation(int x, int y, int element)
4000 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4002 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4005 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
4007 int sx = SCREENX(x), sy = SCREENY(y);
4009 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4012 if (Tile[x][y] == EL_EMPTY)
4013 graphic = el2img(GfxElementEmpty[x][y]);
4015 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4018 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4021 DrawGraphicAnimation(sx, sy, graphic);
4024 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4025 DrawLevelFieldCrumbled(x, y);
4027 if (GFX_CRUMBLED(Tile[x][y]))
4028 DrawLevelFieldCrumbled(x, y);
4032 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4034 int sx = SCREENX(x), sy = SCREENY(y);
4037 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4040 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4042 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4045 DrawGraphicAnimation(sx, sy, graphic);
4047 if (GFX_CRUMBLED(element))
4048 DrawLevelFieldCrumbled(x, y);
4051 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4053 if (player->use_murphy)
4055 // this works only because currently only one player can be "murphy" ...
4056 static int last_horizontal_dir = MV_LEFT;
4057 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4059 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4060 last_horizontal_dir = move_dir;
4062 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4064 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4066 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4072 return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4075 static boolean equalGraphics(int graphic1, int graphic2)
4077 struct GraphicInfo *g1 = &graphic_info[graphic1];
4078 struct GraphicInfo *g2 = &graphic_info[graphic2];
4080 return (g1->bitmap == g2->bitmap &&
4081 g1->src_x == g2->src_x &&
4082 g1->src_y == g2->src_y &&
4083 g1->anim_frames == g2->anim_frames &&
4084 g1->anim_delay == g2->anim_delay &&
4085 g1->anim_mode == g2->anim_mode);
4088 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4092 DRAW_PLAYER_STAGE_INIT = 0,
4093 DRAW_PLAYER_STAGE_LAST_FIELD,
4094 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4095 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4096 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4097 DRAW_PLAYER_STAGE_PLAYER,
4099 DRAW_PLAYER_STAGE_PLAYER,
4100 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4102 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4103 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4105 NUM_DRAW_PLAYER_STAGES
4108 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4110 static int static_last_player_graphic[MAX_PLAYERS];
4111 static int static_last_player_frame[MAX_PLAYERS];
4112 static boolean static_player_is_opaque[MAX_PLAYERS];
4113 static boolean draw_player[MAX_PLAYERS];
4114 int pnr = player->index_nr;
4116 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4118 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4119 static_last_player_frame[pnr] = player->Frame;
4120 static_player_is_opaque[pnr] = FALSE;
4122 draw_player[pnr] = TRUE;
4125 if (!draw_player[pnr])
4129 if (!IN_LEV_FIELD(player->jx, player->jy))
4131 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4132 Debug("draw:DrawPlayerExt", "This should never happen!");
4134 draw_player[pnr] = FALSE;
4140 int last_player_graphic = static_last_player_graphic[pnr];
4141 int last_player_frame = static_last_player_frame[pnr];
4142 boolean player_is_opaque = static_player_is_opaque[pnr];
4144 int jx = player->jx;
4145 int jy = player->jy;
4146 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4147 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4148 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4149 int last_jx = (player->is_moving ? jx - dx : jx);
4150 int last_jy = (player->is_moving ? jy - dy : jy);
4151 int next_jx = jx + dx;
4152 int next_jy = jy + dy;
4153 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4154 int sx = SCREENX(jx);
4155 int sy = SCREENY(jy);
4156 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4157 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4158 int element = Tile[jx][jy];
4159 int last_element = Tile[last_jx][last_jy];
4160 int action = (player->is_pushing ? ACTION_PUSHING :
4161 player->is_digging ? ACTION_DIGGING :
4162 player->is_collecting ? ACTION_COLLECTING :
4163 player->is_moving ? ACTION_MOVING :
4164 player->is_snapping ? ACTION_SNAPPING :
4165 player->is_dropping ? ACTION_DROPPING :
4166 player->is_waiting ? player->action_waiting :
4169 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4171 // ------------------------------------------------------------------------
4172 // initialize drawing the player
4173 // ------------------------------------------------------------------------
4175 draw_player[pnr] = FALSE;
4177 // GfxElement[][] is set to the element the player is digging or collecting;
4178 // remove also for off-screen player if the player is not moving anymore
4179 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4180 GfxElement[jx][jy] = EL_UNDEFINED;
4182 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4185 if (element == EL_EXPLOSION)
4188 InitPlayerGfxAnimation(player, action, move_dir);
4190 draw_player[pnr] = TRUE;
4192 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4194 // ------------------------------------------------------------------------
4195 // draw things in the field the player is leaving, if needed
4196 // ------------------------------------------------------------------------
4198 if (!IN_SCR_FIELD(sx, sy))
4199 draw_player[pnr] = FALSE;
4201 if (!player->is_moving)
4204 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4206 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4208 if (last_element == EL_DYNAMITE_ACTIVE ||
4209 last_element == EL_EM_DYNAMITE_ACTIVE ||
4210 last_element == EL_SP_DISK_RED_ACTIVE)
4211 DrawDynamite(last_jx, last_jy);
4213 DrawLevelFieldThruMask(last_jx, last_jy);
4215 else if (last_element == EL_DYNAMITE_ACTIVE ||
4216 last_element == EL_EM_DYNAMITE_ACTIVE ||
4217 last_element == EL_SP_DISK_RED_ACTIVE)
4218 DrawDynamite(last_jx, last_jy);
4220 DrawLevelField(last_jx, last_jy);
4222 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4224 // ------------------------------------------------------------------------
4225 // draw things behind the player, if needed
4226 // ------------------------------------------------------------------------
4230 DrawLevelElement(jx, jy, Back[jx][jy]);
4235 if (IS_ACTIVE_BOMB(element))
4237 DrawLevelElement(jx, jy, EL_EMPTY);
4242 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4244 int old_element = GfxElement[jx][jy];
4245 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4246 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4248 if (GFX_CRUMBLED(old_element))
4249 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4251 DrawScreenGraphic(sx, sy, old_graphic, frame);
4253 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4254 static_player_is_opaque[pnr] = TRUE;
4258 GfxElement[jx][jy] = EL_UNDEFINED;
4260 // make sure that pushed elements are drawn with correct frame rate
4261 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4263 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4264 GfxFrame[jx][jy] = player->StepFrame;
4266 DrawLevelField(jx, jy);
4269 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4271 // ------------------------------------------------------------------------
4272 // draw things the player is pushing, if needed
4273 // ------------------------------------------------------------------------
4275 if (!player->is_pushing || !player->is_moving)
4278 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4281 int gfx_frame = GfxFrame[jx][jy];
4283 if (!IS_MOVING(jx, jy)) // push movement already finished
4285 element = Tile[next_jx][next_jy];
4286 gfx_frame = GfxFrame[next_jx][next_jy];
4289 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4290 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4291 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4293 // draw background element under pushed element (like the Sokoban field)
4294 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4296 // this allows transparent pushing animation over non-black background
4299 DrawLevelElement(jx, jy, Back[jx][jy]);
4301 DrawLevelElement(jx, jy, EL_EMPTY);
4304 if (Back[next_jx][next_jy])
4305 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4307 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4309 int px = SCREENX(jx), py = SCREENY(jy);
4310 int pxx = (TILEX - ABS(sxx)) * dx;
4311 int pyy = (TILEY - ABS(syy)) * dy;
4314 // do not draw (EM style) pushing animation when pushing is finished
4315 // (two-tile animations usually do not contain start and end frame)
4316 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4317 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4319 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4321 // masked drawing is needed for EMC style (double) movement graphics
4322 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4323 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4326 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4328 // ------------------------------------------------------------------------
4329 // draw player himself
4330 // ------------------------------------------------------------------------
4332 int graphic = getPlayerGraphic(player, move_dir);
4334 // in the case of changed player action or direction, prevent the current
4335 // animation frame from being restarted for identical animations
4336 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4337 player->Frame = last_player_frame;
4339 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4341 if (player_is_opaque)
4342 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4344 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4346 if (SHIELD_ON(player))
4348 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4349 IMG_SHIELD_NORMAL_ACTIVE);
4350 frame = getGraphicAnimationFrame(graphic, -1);
4352 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4355 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4357 // ------------------------------------------------------------------------
4358 // draw things in front of player (active dynamite or dynabombs)
4359 // ------------------------------------------------------------------------
4361 if (IS_ACTIVE_BOMB(element))
4363 int graphic = el2img(element);
4364 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4366 if (game.emulation == EMU_SUPAPLEX)
4367 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4369 DrawGraphicThruMask(sx, sy, graphic, frame);
4372 if (player_is_moving && last_element == EL_EXPLOSION)
4374 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4375 GfxElement[last_jx][last_jy] : EL_EMPTY);
4376 int graphic = el_act2img(element, ACTION_EXPLODING);
4377 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4378 int phase = ExplodePhase[last_jx][last_jy] - 1;
4379 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4382 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4385 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4387 // ------------------------------------------------------------------------
4388 // draw elements the player is just walking/passing through/under
4389 // ------------------------------------------------------------------------
4391 if (player_is_moving)
4393 // handle the field the player is leaving ...
4394 if (IS_ACCESSIBLE_INSIDE(last_element))
4395 DrawLevelField(last_jx, last_jy);
4396 else if (IS_ACCESSIBLE_UNDER(last_element))
4397 DrawLevelFieldThruMask(last_jx, last_jy);
4400 // do not redraw accessible elements if the player is just pushing them
4401 if (!player_is_moving || !player->is_pushing)
4403 // ... and the field the player is entering
4404 if (IS_ACCESSIBLE_INSIDE(element))
4405 DrawLevelField(jx, jy);
4406 else if (IS_ACCESSIBLE_UNDER(element))
4407 DrawLevelFieldThruMask(jx, jy);
4410 MarkTileDirty(sx, sy);
4414 void DrawPlayer(struct PlayerInfo *player)
4418 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4419 DrawPlayerExt(player, i);
4422 void DrawAllPlayers(void)
4426 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4427 for (j = 0; j < MAX_PLAYERS; j++)
4428 if (stored_player[j].active)
4429 DrawPlayerExt(&stored_player[j], i);
4432 void DrawPlayerField(int x, int y)
4434 if (!IS_PLAYER(x, y))
4437 DrawPlayer(PLAYERINFO(x, y));
4440 // ----------------------------------------------------------------------------
4442 void WaitForEventToContinue(void)
4444 boolean first_wait = TRUE;
4445 boolean still_wait = TRUE;
4447 if (program.headless)
4450 // simulate releasing mouse button over last gadget, if still pressed
4452 HandleGadgets(-1, -1, 0);
4454 button_status = MB_RELEASED;
4457 ClearPlayerAction();
4463 if (NextValidEvent(&event))
4467 case EVENT_BUTTONPRESS:
4468 case EVENT_FINGERPRESS:
4472 case EVENT_BUTTONRELEASE:
4473 case EVENT_FINGERRELEASE:
4474 still_wait = first_wait;
4477 case EVENT_KEYPRESS:
4478 case SDL_CONTROLLERBUTTONDOWN:
4479 case SDL_JOYBUTTONDOWN:
4484 HandleOtherEvents(&event);
4488 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4493 if (!PendingEvent())
4498 #define MAX_REQUEST_LINES 13
4499 #define MAX_REQUEST_LINE_FONT1_LEN 7
4500 #define MAX_REQUEST_LINE_FONT2_LEN 10
4502 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4504 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4506 int draw_buffer_last = GetDrawtoField();
4507 int width = request.width;
4508 int height = request.height;
4512 setRequestPosition(&sx, &sy, FALSE);
4514 button_status = MB_RELEASED;
4516 request_gadget_id = -1;
4521 if (game_just_ended)
4523 SetDrawtoField(draw_buffer_game);
4525 HandleGameActions();
4527 SetDrawtoField(DRAW_TO_BACKBUFFER);
4534 while (NextValidEvent(&event))
4538 case EVENT_BUTTONPRESS:
4539 case EVENT_BUTTONRELEASE:
4540 case EVENT_MOTIONNOTIFY:
4542 DrawBuffer *drawto_last = drawto;
4545 if (event.type == EVENT_MOTIONNOTIFY)
4550 motion_status = TRUE;
4551 mx = ((MotionEvent *) &event)->x;
4552 my = ((MotionEvent *) &event)->y;
4556 motion_status = FALSE;
4557 mx = ((ButtonEvent *) &event)->x;
4558 my = ((ButtonEvent *) &event)->y;
4559 if (event.type == EVENT_BUTTONPRESS)
4560 button_status = ((ButtonEvent *) &event)->button;
4562 button_status = MB_RELEASED;
4565 if (global.use_envelope_request)
4567 // draw changed button states to temporary bitmap
4568 drawto = bitmap_db_store_1;
4571 // this sets 'request_gadget_id'
4572 HandleGadgets(mx, my, button_status);
4574 if (global.use_envelope_request)
4576 // restore pointer to drawing buffer
4577 drawto = drawto_last;
4579 // prepare complete envelope request from temporary bitmap
4580 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy,
4584 switch (request_gadget_id)
4586 case TOOL_CTRL_ID_YES:
4587 case TOOL_CTRL_ID_TOUCH_YES:
4590 case TOOL_CTRL_ID_NO:
4591 case TOOL_CTRL_ID_TOUCH_NO:
4594 case TOOL_CTRL_ID_CONFIRM:
4595 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4596 result = TRUE | FALSE;
4599 case TOOL_CTRL_ID_PLAYER_1:
4602 case TOOL_CTRL_ID_PLAYER_2:
4605 case TOOL_CTRL_ID_PLAYER_3:
4608 case TOOL_CTRL_ID_PLAYER_4:
4613 // only check clickable animations if no request gadget clicked
4614 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4621 case SDL_WINDOWEVENT:
4622 HandleWindowEvent((WindowEvent *) &event);
4625 case SDL_APP_WILLENTERBACKGROUND:
4626 case SDL_APP_DIDENTERBACKGROUND:
4627 case SDL_APP_WILLENTERFOREGROUND:
4628 case SDL_APP_DIDENTERFOREGROUND:
4629 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4632 case EVENT_KEYPRESS:
4634 Key key = GetEventKey((KeyEvent *)&event);
4639 if (req_state & REQ_CONFIRM)
4648 #if defined(KSYM_Rewind)
4649 case KSYM_Rewind: // for Amazon Fire TV remote
4658 #if defined(KSYM_FastForward)
4659 case KSYM_FastForward: // for Amazon Fire TV remote
4665 HandleKeysDebug(key, KEY_PRESSED);
4669 if (req_state & REQ_PLAYER)
4671 int old_player_nr = setup.network_player_nr;
4674 result = old_player_nr + 1;
4679 result = old_player_nr + 1;
4710 case EVENT_FINGERRELEASE:
4711 case EVENT_KEYRELEASE:
4712 ClearPlayerAction();
4715 case SDL_CONTROLLERBUTTONDOWN:
4716 switch (event.cbutton.button)
4718 case SDL_CONTROLLER_BUTTON_A:
4719 case SDL_CONTROLLER_BUTTON_X:
4720 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4721 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4725 case SDL_CONTROLLER_BUTTON_B:
4726 case SDL_CONTROLLER_BUTTON_Y:
4727 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4728 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4729 case SDL_CONTROLLER_BUTTON_BACK:
4734 if (req_state & REQ_PLAYER)
4736 int old_player_nr = setup.network_player_nr;
4739 result = old_player_nr + 1;
4741 switch (event.cbutton.button)
4743 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4744 case SDL_CONTROLLER_BUTTON_Y:
4748 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4749 case SDL_CONTROLLER_BUTTON_B:
4753 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4754 case SDL_CONTROLLER_BUTTON_A:
4758 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4759 case SDL_CONTROLLER_BUTTON_X:
4770 case SDL_CONTROLLERBUTTONUP:
4771 HandleJoystickEvent(&event);
4772 ClearPlayerAction();
4776 HandleOtherEvents(&event);
4781 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4783 int joy = AnyJoystick();
4785 if (joy & JOY_BUTTON_1)
4787 else if (joy & JOY_BUTTON_2)
4790 else if (AnyJoystick())
4792 int joy = AnyJoystick();
4794 if (req_state & REQ_PLAYER)
4798 else if (joy & JOY_RIGHT)
4800 else if (joy & JOY_DOWN)
4802 else if (joy & JOY_LEFT)
4810 SetDrawtoField(draw_buffer_last);
4815 static boolean RequestDoor(char *text, unsigned int req_state)
4817 int draw_buffer_last = GetDrawtoField();
4818 unsigned int old_door_state;
4819 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4820 int font_nr = FONT_TEXT_2;
4825 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4827 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4828 font_nr = FONT_TEXT_1;
4831 if (game_status == GAME_MODE_PLAYING)
4832 BlitScreenToBitmap(backbuffer);
4834 // disable deactivated drawing when quick-loading level tape recording
4835 if (tape.playing && tape.deactivate_display)
4836 TapeDeactivateDisplayOff(TRUE);
4838 SetMouseCursor(CURSOR_DEFAULT);
4840 // pause network game while waiting for request to answer
4841 if (network.enabled &&
4842 game_status == GAME_MODE_PLAYING &&
4843 !game.all_players_gone &&
4844 req_state & REQUEST_WAIT_FOR_INPUT)
4845 SendToServer_PausePlaying();
4847 old_door_state = GetDoorState();
4849 // simulate releasing mouse button over last gadget, if still pressed
4851 HandleGadgets(-1, -1, 0);
4855 // draw released gadget before proceeding
4858 if (old_door_state & DOOR_OPEN_1)
4860 CloseDoor(DOOR_CLOSE_1);
4862 // save old door content
4863 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4864 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4867 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4868 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4870 // clear door drawing field
4871 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4873 // force DOOR font inside door area
4874 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4876 // write text for request
4877 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4879 char text_line[max_request_line_len + 1];
4885 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4887 tc = *(text_ptr + tx);
4888 // if (!tc || tc == ' ')
4889 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4893 if ((tc == '?' || tc == '!') && tl == 0)
4903 strncpy(text_line, text_ptr, tl);
4906 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4907 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4908 text_line, font_nr);
4910 text_ptr += tl + (tc == ' ' ? 1 : 0);
4911 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4916 if (req_state & REQ_ASK)
4918 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4919 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4920 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4921 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4923 else if (req_state & REQ_CONFIRM)
4925 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4926 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4928 else if (req_state & REQ_PLAYER)
4930 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4931 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4932 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4933 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4936 // copy request gadgets to door backbuffer
4937 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4939 OpenDoor(DOOR_OPEN_1);
4941 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4943 if (game_status == GAME_MODE_PLAYING)
4945 SetPanelBackground();
4946 SetDrawBackgroundMask(REDRAW_DOOR_1);
4950 SetDrawBackgroundMask(REDRAW_FIELD);
4956 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4958 // ---------- handle request buttons ----------
4959 result = RequestHandleEvents(req_state, draw_buffer_last);
4963 if (!(req_state & REQ_STAY_OPEN))
4965 CloseDoor(DOOR_CLOSE_1);
4967 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4968 (req_state & REQ_REOPEN))
4969 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4974 if (game_status == GAME_MODE_PLAYING)
4976 SetPanelBackground();
4977 SetDrawBackgroundMask(REDRAW_DOOR_1);
4981 SetDrawBackgroundMask(REDRAW_FIELD);
4984 // continue network game after request
4985 if (network.enabled &&
4986 game_status == GAME_MODE_PLAYING &&
4987 !game.all_players_gone &&
4988 req_state & REQUEST_WAIT_FOR_INPUT)
4989 SendToServer_ContinuePlaying();
4991 // restore deactivated drawing when quick-loading level tape recording
4992 if (tape.playing && tape.deactivate_display)
4993 TapeDeactivateDisplayOn();
4998 static boolean RequestEnvelope(char *text, unsigned int req_state)
5000 int draw_buffer_last = GetDrawtoField();
5003 if (game_status == GAME_MODE_PLAYING)
5004 BlitScreenToBitmap(backbuffer);
5006 // disable deactivated drawing when quick-loading level tape recording
5007 if (tape.playing && tape.deactivate_display)
5008 TapeDeactivateDisplayOff(TRUE);
5010 SetMouseCursor(CURSOR_DEFAULT);
5012 // pause network game while waiting for request to answer
5013 if (network.enabled &&
5014 game_status == GAME_MODE_PLAYING &&
5015 !game.all_players_gone &&
5016 req_state & REQUEST_WAIT_FOR_INPUT)
5017 SendToServer_PausePlaying();
5019 // simulate releasing mouse button over last gadget, if still pressed
5021 HandleGadgets(-1, -1, 0);
5025 // (replace with setting corresponding request background)
5026 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5027 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5029 // clear door drawing field
5030 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5032 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5034 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5036 if (game_status == GAME_MODE_PLAYING)
5038 SetPanelBackground();
5039 SetDrawBackgroundMask(REDRAW_DOOR_1);
5043 SetDrawBackgroundMask(REDRAW_FIELD);
5049 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5051 // ---------- handle request buttons ----------
5052 result = RequestHandleEvents(req_state, draw_buffer_last);
5056 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5060 if (game_status == GAME_MODE_PLAYING)
5062 SetPanelBackground();
5063 SetDrawBackgroundMask(REDRAW_DOOR_1);
5067 SetDrawBackgroundMask(REDRAW_FIELD);
5070 // continue network game after request
5071 if (network.enabled &&
5072 game_status == GAME_MODE_PLAYING &&
5073 !game.all_players_gone &&
5074 req_state & REQUEST_WAIT_FOR_INPUT)
5075 SendToServer_ContinuePlaying();
5077 // restore deactivated drawing when quick-loading level tape recording
5078 if (tape.playing && tape.deactivate_display)
5079 TapeDeactivateDisplayOn();
5084 boolean Request(char *text, unsigned int req_state)
5086 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
5088 boolean overlay_enabled = GetOverlayEnabled();
5091 // when showing request dialog after game ended, deactivate game panel
5092 if (game_just_ended)
5093 game.panel.active = FALSE;
5095 game.request_active = TRUE;
5097 SetOverlayEnabled(FALSE);
5099 if (global.use_envelope_request)
5100 result = RequestEnvelope(text, req_state);
5102 result = RequestDoor(text, req_state);
5104 SetOverlayEnabled(overlay_enabled);
5106 game.request_active = FALSE;
5111 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5113 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5114 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5117 if (dpo1->sort_priority != dpo2->sort_priority)
5118 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5120 compare_result = dpo1->nr - dpo2->nr;
5122 return compare_result;
5125 void InitGraphicCompatibilityInfo_Doors(void)
5131 struct DoorInfo *door;
5135 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5136 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5138 { -1, -1, -1, NULL }
5140 struct Rect door_rect_list[] =
5142 { DX, DY, DXSIZE, DYSIZE },
5143 { VX, VY, VXSIZE, VYSIZE }
5147 for (i = 0; doors[i].door_token != -1; i++)
5149 int door_token = doors[i].door_token;
5150 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5151 int part_1 = doors[i].part_1;
5152 int part_8 = doors[i].part_8;
5153 int part_2 = part_1 + 1;
5154 int part_3 = part_1 + 2;
5155 struct DoorInfo *door = doors[i].door;
5156 struct Rect *door_rect = &door_rect_list[door_index];
5157 boolean door_gfx_redefined = FALSE;
5159 // check if any door part graphic definitions have been redefined
5161 for (j = 0; door_part_controls[j].door_token != -1; j++)
5163 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5164 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5166 if (dpc->door_token == door_token && fi->redefined)
5167 door_gfx_redefined = TRUE;
5170 // check for old-style door graphic/animation modifications
5172 if (!door_gfx_redefined)
5174 if (door->anim_mode & ANIM_STATIC_PANEL)
5176 door->panel.step_xoffset = 0;
5177 door->panel.step_yoffset = 0;
5180 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5182 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5183 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5184 int num_door_steps, num_panel_steps;
5186 // remove door part graphics other than the two default wings
5188 for (j = 0; door_part_controls[j].door_token != -1; j++)
5190 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5191 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5193 if (dpc->graphic >= part_3 &&
5194 dpc->graphic <= part_8)
5198 // set graphics and screen positions of the default wings
5200 g_part_1->width = door_rect->width;
5201 g_part_1->height = door_rect->height;
5202 g_part_2->width = door_rect->width;
5203 g_part_2->height = door_rect->height;
5204 g_part_2->src_x = door_rect->width;
5205 g_part_2->src_y = g_part_1->src_y;
5207 door->part_2.x = door->part_1.x;
5208 door->part_2.y = door->part_1.y;
5210 if (door->width != -1)
5212 g_part_1->width = door->width;
5213 g_part_2->width = door->width;
5215 // special treatment for graphics and screen position of right wing
5216 g_part_2->src_x += door_rect->width - door->width;
5217 door->part_2.x += door_rect->width - door->width;
5220 if (door->height != -1)
5222 g_part_1->height = door->height;
5223 g_part_2->height = door->height;
5225 // special treatment for graphics and screen position of bottom wing
5226 g_part_2->src_y += door_rect->height - door->height;
5227 door->part_2.y += door_rect->height - door->height;
5230 // set animation delays for the default wings and panels
5232 door->part_1.step_delay = door->step_delay;
5233 door->part_2.step_delay = door->step_delay;
5234 door->panel.step_delay = door->step_delay;
5236 // set animation draw order for the default wings
5238 door->part_1.sort_priority = 2; // draw left wing over ...
5239 door->part_2.sort_priority = 1; // ... right wing
5241 // set animation draw offset for the default wings
5243 if (door->anim_mode & ANIM_HORIZONTAL)
5245 door->part_1.step_xoffset = door->step_offset;
5246 door->part_1.step_yoffset = 0;
5247 door->part_2.step_xoffset = door->step_offset * -1;
5248 door->part_2.step_yoffset = 0;
5250 num_door_steps = g_part_1->width / door->step_offset;
5252 else // ANIM_VERTICAL
5254 door->part_1.step_xoffset = 0;
5255 door->part_1.step_yoffset = door->step_offset;
5256 door->part_2.step_xoffset = 0;
5257 door->part_2.step_yoffset = door->step_offset * -1;
5259 num_door_steps = g_part_1->height / door->step_offset;
5262 // set animation draw offset for the default panels
5264 if (door->step_offset > 1)
5266 num_panel_steps = 2 * door_rect->height / door->step_offset;
5267 door->panel.start_step = num_panel_steps - num_door_steps;
5268 door->panel.start_step_closing = door->panel.start_step;
5272 num_panel_steps = door_rect->height / door->step_offset;
5273 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5274 door->panel.start_step_closing = door->panel.start_step;
5275 door->panel.step_delay *= 2;
5282 void InitDoors(void)
5286 for (i = 0; door_part_controls[i].door_token != -1; i++)
5288 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5289 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5291 // initialize "start_step_opening" and "start_step_closing", if needed
5292 if (dpc->pos->start_step_opening == 0 &&
5293 dpc->pos->start_step_closing == 0)
5295 // dpc->pos->start_step_opening = dpc->pos->start_step;
5296 dpc->pos->start_step_closing = dpc->pos->start_step;
5299 // fill structure for door part draw order (sorted below)
5301 dpo->sort_priority = dpc->pos->sort_priority;
5304 // sort door part controls according to sort_priority and graphic number
5305 qsort(door_part_order, MAX_DOOR_PARTS,
5306 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5309 unsigned int OpenDoor(unsigned int door_state)
5311 if (door_state & DOOR_COPY_BACK)
5313 if (door_state & DOOR_OPEN_1)
5314 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5315 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5317 if (door_state & DOOR_OPEN_2)
5318 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5319 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5321 door_state &= ~DOOR_COPY_BACK;
5324 return MoveDoor(door_state);
5327 unsigned int CloseDoor(unsigned int door_state)
5329 unsigned int old_door_state = GetDoorState();
5331 if (!(door_state & DOOR_NO_COPY_BACK))
5333 if (old_door_state & DOOR_OPEN_1)
5334 BlitBitmap(backbuffer, bitmap_db_door_1,
5335 DX, DY, DXSIZE, DYSIZE, 0, 0);
5337 if (old_door_state & DOOR_OPEN_2)
5338 BlitBitmap(backbuffer, bitmap_db_door_2,
5339 VX, VY, VXSIZE, VYSIZE, 0, 0);
5341 door_state &= ~DOOR_NO_COPY_BACK;
5344 return MoveDoor(door_state);
5347 unsigned int GetDoorState(void)
5349 return MoveDoor(DOOR_GET_STATE);
5352 unsigned int SetDoorState(unsigned int door_state)
5354 return MoveDoor(door_state | DOOR_SET_STATE);
5357 static int euclid(int a, int b)
5359 return (b ? euclid(b, a % b) : a);
5362 unsigned int MoveDoor(unsigned int door_state)
5364 struct Rect door_rect_list[] =
5366 { DX, DY, DXSIZE, DYSIZE },
5367 { VX, VY, VXSIZE, VYSIZE }
5369 static int door1 = DOOR_CLOSE_1;
5370 static int door2 = DOOR_CLOSE_2;
5371 DelayCounter door_delay = { 0 };
5374 if (door_state == DOOR_GET_STATE)
5375 return (door1 | door2);
5377 if (door_state & DOOR_SET_STATE)
5379 if (door_state & DOOR_ACTION_1)
5380 door1 = door_state & DOOR_ACTION_1;
5381 if (door_state & DOOR_ACTION_2)
5382 door2 = door_state & DOOR_ACTION_2;
5384 return (door1 | door2);
5387 if (!(door_state & DOOR_FORCE_REDRAW))
5389 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5390 door_state &= ~DOOR_OPEN_1;
5391 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5392 door_state &= ~DOOR_CLOSE_1;
5393 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5394 door_state &= ~DOOR_OPEN_2;
5395 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5396 door_state &= ~DOOR_CLOSE_2;
5399 if (global.autoplay_leveldir)
5401 door_state |= DOOR_NO_DELAY;
5402 door_state &= ~DOOR_CLOSE_ALL;
5405 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5406 door_state |= DOOR_NO_DELAY;
5408 if (door_state & DOOR_ACTION)
5410 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
5412 boolean door_panel_drawn[NUM_DOORS];
5413 boolean panel_has_doors[NUM_DOORS];
5414 boolean door_part_skip[MAX_DOOR_PARTS];
5415 boolean door_part_done[MAX_DOOR_PARTS];
5416 boolean door_part_done_all;
5417 int num_steps[MAX_DOOR_PARTS];
5418 int max_move_delay = 0; // delay for complete animations of all doors
5419 int max_step_delay = 0; // delay (ms) between two animation frames
5420 int num_move_steps = 0; // number of animation steps for all doors
5421 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5422 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5426 for (i = 0; i < NUM_DOORS; i++)
5427 panel_has_doors[i] = FALSE;
5429 for (i = 0; i < MAX_DOOR_PARTS; i++)
5431 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5432 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5433 int door_token = dpc->door_token;
5435 door_part_done[i] = FALSE;
5436 door_part_skip[i] = (!(door_state & door_token) ||
5440 for (i = 0; i < MAX_DOOR_PARTS; i++)
5442 int nr = door_part_order[i].nr;
5443 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5444 struct DoorPartPosInfo *pos = dpc->pos;
5445 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5446 int door_token = dpc->door_token;
5447 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5448 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5449 int step_xoffset = ABS(pos->step_xoffset);
5450 int step_yoffset = ABS(pos->step_yoffset);
5451 int step_delay = pos->step_delay;
5452 int current_door_state = door_state & door_token;
5453 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5454 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5455 boolean part_opening = (is_panel ? door_closing : door_opening);
5456 int start_step = (part_opening ? pos->start_step_opening :
5457 pos->start_step_closing);
5458 float move_xsize = (step_xoffset ? g->width : 0);
5459 float move_ysize = (step_yoffset ? g->height : 0);
5460 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5461 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5462 int move_steps = (move_xsteps && move_ysteps ?
5463 MIN(move_xsteps, move_ysteps) :
5464 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5465 int move_delay = move_steps * step_delay;
5467 if (door_part_skip[nr])
5470 max_move_delay = MAX(max_move_delay, move_delay);
5471 max_step_delay = (max_step_delay == 0 ? step_delay :
5472 euclid(max_step_delay, step_delay));
5473 num_steps[nr] = move_steps;
5477 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5479 panel_has_doors[door_index] = TRUE;
5483 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5485 num_move_steps = max_move_delay / max_step_delay;
5486 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5488 door_delay.value = max_step_delay;
5490 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5492 start = num_move_steps - 1;
5496 // opening door sound has priority over simultaneously closing door
5497 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5499 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5501 if (door_state & DOOR_OPEN_1)
5502 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5503 if (door_state & DOOR_OPEN_2)
5504 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5506 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5508 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5510 if (door_state & DOOR_CLOSE_1)
5511 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5512 if (door_state & DOOR_CLOSE_2)
5513 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5517 for (k = start; k < num_move_steps; k++)
5519 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5521 door_part_done_all = TRUE;
5523 for (i = 0; i < NUM_DOORS; i++)
5524 door_panel_drawn[i] = FALSE;
5526 for (i = 0; i < MAX_DOOR_PARTS; i++)
5528 int nr = door_part_order[i].nr;
5529 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5530 struct DoorPartPosInfo *pos = dpc->pos;
5531 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5532 int door_token = dpc->door_token;
5533 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5534 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5535 boolean is_panel_and_door_has_closed = FALSE;
5536 struct Rect *door_rect = &door_rect_list[door_index];
5537 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5539 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5540 int current_door_state = door_state & door_token;
5541 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5542 boolean door_closing = !door_opening;
5543 boolean part_opening = (is_panel ? door_closing : door_opening);
5544 boolean part_closing = !part_opening;
5545 int start_step = (part_opening ? pos->start_step_opening :
5546 pos->start_step_closing);
5547 int step_delay = pos->step_delay;
5548 int step_factor = step_delay / max_step_delay;
5549 int k1 = (step_factor ? k / step_factor + 1 : k);
5550 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5551 int kk = MAX(0, k2);
5554 int src_x, src_y, src_xx, src_yy;
5555 int dst_x, dst_y, dst_xx, dst_yy;
5558 if (door_part_skip[nr])
5561 if (!(door_state & door_token))
5569 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5570 int kk_door = MAX(0, k2_door);
5571 int sync_frame = kk_door * door_delay.value;
5572 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5574 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5575 &g_src_x, &g_src_y);
5580 if (!door_panel_drawn[door_index])
5582 ClearRectangle(drawto, door_rect->x, door_rect->y,
5583 door_rect->width, door_rect->height);
5585 door_panel_drawn[door_index] = TRUE;
5588 // draw opening or closing door parts
5590 if (pos->step_xoffset < 0) // door part on right side
5593 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5596 if (dst_xx + width > door_rect->width)
5597 width = door_rect->width - dst_xx;
5599 else // door part on left side
5602 dst_xx = pos->x - kk * pos->step_xoffset;
5606 src_xx = ABS(dst_xx);
5610 width = g->width - src_xx;
5612 if (width > door_rect->width)
5613 width = door_rect->width;
5615 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5618 if (pos->step_yoffset < 0) // door part on bottom side
5621 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5624 if (dst_yy + height > door_rect->height)
5625 height = door_rect->height - dst_yy;
5627 else // door part on top side
5630 dst_yy = pos->y - kk * pos->step_yoffset;
5634 src_yy = ABS(dst_yy);
5638 height = g->height - src_yy;
5641 src_x = g_src_x + src_xx;
5642 src_y = g_src_y + src_yy;
5644 dst_x = door_rect->x + dst_xx;
5645 dst_y = door_rect->y + dst_yy;
5647 is_panel_and_door_has_closed =
5650 panel_has_doors[door_index] &&
5651 k >= num_move_steps_doors_only - 1);
5653 if (width >= 0 && width <= g->width &&
5654 height >= 0 && height <= g->height &&
5655 !is_panel_and_door_has_closed)
5657 if (is_panel || !pos->draw_masked)
5658 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5661 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5665 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5667 if ((part_opening && (width < 0 || height < 0)) ||
5668 (part_closing && (width >= g->width && height >= g->height)))
5669 door_part_done[nr] = TRUE;
5671 // continue door part animations, but not panel after door has closed
5672 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5673 door_part_done_all = FALSE;
5676 if (!(door_state & DOOR_NO_DELAY))
5678 if (game_just_ended)
5679 HandleGameActions();
5683 SkipUntilDelayReached(&door_delay, &k, last_frame);
5685 // prevent OS (Windows) from complaining about program not responding
5689 if (door_part_done_all)
5693 if (!(door_state & DOOR_NO_DELAY))
5695 // wait for specified door action post delay
5696 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5697 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5698 else if (door_state & DOOR_ACTION_1)
5699 door_delay.value = door_1.post_delay;
5700 else if (door_state & DOOR_ACTION_2)
5701 door_delay.value = door_2.post_delay;
5703 while (!DelayReached(&door_delay))
5705 if (game_just_ended)
5706 HandleGameActions();
5713 if (door_state & DOOR_ACTION_1)
5714 door1 = door_state & DOOR_ACTION_1;
5715 if (door_state & DOOR_ACTION_2)
5716 door2 = door_state & DOOR_ACTION_2;
5718 // draw masked border over door area
5719 DrawMaskedBorder(REDRAW_DOOR_1);
5720 DrawMaskedBorder(REDRAW_DOOR_2);
5722 ClearAutoRepeatKeyEvents();
5724 return (door1 | door2);
5727 static boolean useSpecialEditorDoor(void)
5729 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5730 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5732 // do not draw special editor door if editor border defined or redefined
5733 if (graphic_info[graphic].bitmap != NULL || redefined)
5736 // do not draw special editor door if global border defined to be empty
5737 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5740 // do not draw special editor door if viewport definitions do not match
5744 EY + EYSIZE != VY + VYSIZE)
5750 void DrawSpecialEditorDoor(void)
5752 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5753 int top_border_width = gfx1->width;
5754 int top_border_height = gfx1->height;
5755 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5756 int ex = EX - outer_border;
5757 int ey = EY - outer_border;
5758 int vy = VY - outer_border;
5759 int exsize = EXSIZE + 2 * outer_border;
5761 if (!useSpecialEditorDoor())
5764 // draw bigger level editor toolbox window
5765 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5766 top_border_width, top_border_height, ex, ey - top_border_height);
5767 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5768 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5770 redraw_mask |= REDRAW_ALL;
5773 void UndrawSpecialEditorDoor(void)
5775 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5776 int top_border_width = gfx1->width;
5777 int top_border_height = gfx1->height;
5778 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5779 int ex = EX - outer_border;
5780 int ey = EY - outer_border;
5781 int ey_top = ey - top_border_height;
5782 int exsize = EXSIZE + 2 * outer_border;
5783 int eysize = EYSIZE + 2 * outer_border;
5785 if (!useSpecialEditorDoor())
5788 // draw normal tape recorder window
5789 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5791 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5792 ex, ey_top, top_border_width, top_border_height,
5794 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5795 ex, ey, exsize, eysize, ex, ey);
5799 // if screen background is set to "[NONE]", clear editor toolbox window
5800 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5801 ClearRectangle(drawto, ex, ey, exsize, eysize);
5804 redraw_mask |= REDRAW_ALL;
5808 // ---------- new tool button stuff -------------------------------------------
5813 struct TextPosInfo *pos;
5815 boolean is_touch_button;
5817 } toolbutton_info[NUM_TOOL_BUTTONS] =
5820 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5821 TOOL_CTRL_ID_YES, FALSE, "yes"
5824 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5825 TOOL_CTRL_ID_NO, FALSE, "no"
5828 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5829 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5832 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5833 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5836 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5837 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5840 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5841 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5844 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5845 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5848 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5849 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5852 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5853 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5856 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5857 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5861 void CreateToolButtons(void)
5865 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5867 int graphic = toolbutton_info[i].graphic;
5868 struct GraphicInfo *gfx = &graphic_info[graphic];
5869 struct TextPosInfo *pos = toolbutton_info[i].pos;
5870 struct GadgetInfo *gi;
5871 Bitmap *deco_bitmap = None;
5872 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5873 unsigned int event_mask = GD_EVENT_RELEASED;
5874 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5875 int base_x = (is_touch_button ? 0 : DX);
5876 int base_y = (is_touch_button ? 0 : DY);
5877 int gd_x = gfx->src_x;
5878 int gd_y = gfx->src_y;
5879 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5880 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5885 // do not use touch buttons if overlay touch buttons are disabled
5886 if (is_touch_button && !setup.touch.overlay_buttons)
5889 if (global.use_envelope_request && !is_touch_button)
5891 setRequestPosition(&base_x, &base_y, TRUE);
5893 // check if request buttons are outside of envelope and fix, if needed
5894 if (x < 0 || x + gfx->width > request.width ||
5895 y < 0 || y + gfx->height > request.height)
5897 if (id == TOOL_CTRL_ID_YES)
5900 y = request.height - 2 * request.border_size - gfx->height;
5902 else if (id == TOOL_CTRL_ID_NO)
5904 x = request.width - 2 * request.border_size - gfx->width;
5905 y = request.height - 2 * request.border_size - gfx->height;
5907 else if (id == TOOL_CTRL_ID_CONFIRM)
5909 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5910 y = request.height - 2 * request.border_size - gfx->height;
5912 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5914 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5916 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5917 y = request.height - 2 * request.border_size - gfx->height * 2;
5919 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5920 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5925 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5928 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5930 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5931 pos->size, &deco_bitmap, &deco_x, &deco_y);
5932 deco_xpos = (gfx->width - pos->size) / 2;
5933 deco_ypos = (gfx->height - pos->size) / 2;
5936 gi = CreateGadget(GDI_CUSTOM_ID, id,
5937 GDI_IMAGE_ID, graphic,
5938 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5941 GDI_WIDTH, gfx->width,
5942 GDI_HEIGHT, gfx->height,
5943 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5944 GDI_STATE, GD_BUTTON_UNPRESSED,
5945 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5946 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5947 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5948 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5949 GDI_DECORATION_SIZE, pos->size, pos->size,
5950 GDI_DECORATION_SHIFTING, 1, 1,
5951 GDI_DIRECT_DRAW, FALSE,
5952 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5953 GDI_EVENT_MASK, event_mask,
5954 GDI_CALLBACK_ACTION, HandleToolButtons,
5958 Fail("cannot create gadget");
5960 tool_gadget[id] = gi;
5964 void FreeToolButtons(void)
5968 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5969 FreeGadget(tool_gadget[i]);
5972 static void UnmapToolButtons(void)
5976 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5977 UnmapGadget(tool_gadget[i]);
5980 static void HandleToolButtons(struct GadgetInfo *gi)
5982 request_gadget_id = gi->custom_id;
5985 static struct Mapping_EM_to_RND_object
5988 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5989 boolean is_backside; // backside of moving element
5995 em_object_mapping_list[GAME_TILE_MAX + 1] =
5998 Zborder, FALSE, FALSE,
6002 Zplayer, FALSE, FALSE,
6011 Ztank, FALSE, FALSE,
6015 Zeater, FALSE, FALSE,
6019 Zdynamite, FALSE, FALSE,
6023 Zboom, FALSE, FALSE,
6028 Xchain, FALSE, FALSE,
6029 EL_DEFAULT, ACTION_EXPLODING, -1
6032 Xboom_bug, FALSE, FALSE,
6033 EL_BUG, ACTION_EXPLODING, -1
6036 Xboom_tank, FALSE, FALSE,
6037 EL_SPACESHIP, ACTION_EXPLODING, -1
6040 Xboom_android, FALSE, FALSE,
6041 EL_EMC_ANDROID, ACTION_OTHER, -1
6044 Xboom_1, FALSE, FALSE,
6045 EL_DEFAULT, ACTION_EXPLODING, -1
6048 Xboom_2, FALSE, FALSE,
6049 EL_DEFAULT, ACTION_EXPLODING, -1
6053 Xblank, TRUE, FALSE,
6058 Xsplash_e, FALSE, FALSE,
6059 EL_ACID_SPLASH_RIGHT, -1, -1
6062 Xsplash_w, FALSE, FALSE,
6063 EL_ACID_SPLASH_LEFT, -1, -1
6067 Xplant, TRUE, FALSE,
6068 EL_EMC_PLANT, -1, -1
6071 Yplant, FALSE, FALSE,
6072 EL_EMC_PLANT, -1, -1
6076 Xacid_1, TRUE, FALSE,
6080 Xacid_2, FALSE, FALSE,
6084 Xacid_3, FALSE, FALSE,
6088 Xacid_4, FALSE, FALSE,
6092 Xacid_5, FALSE, FALSE,
6096 Xacid_6, FALSE, FALSE,
6100 Xacid_7, FALSE, FALSE,
6104 Xacid_8, FALSE, FALSE,
6109 Xfake_acid_1, TRUE, FALSE,
6110 EL_EMC_FAKE_ACID, -1, -1
6113 Xfake_acid_2, FALSE, FALSE,
6114 EL_EMC_FAKE_ACID, -1, -1
6117 Xfake_acid_3, FALSE, FALSE,
6118 EL_EMC_FAKE_ACID, -1, -1
6121 Xfake_acid_4, FALSE, FALSE,
6122 EL_EMC_FAKE_ACID, -1, -1
6125 Xfake_acid_5, FALSE, FALSE,
6126 EL_EMC_FAKE_ACID, -1, -1
6129 Xfake_acid_6, FALSE, FALSE,
6130 EL_EMC_FAKE_ACID, -1, -1
6133 Xfake_acid_7, FALSE, FALSE,
6134 EL_EMC_FAKE_ACID, -1, -1
6137 Xfake_acid_8, FALSE, FALSE,
6138 EL_EMC_FAKE_ACID, -1, -1
6142 Xfake_acid_1_player, FALSE, FALSE,
6143 EL_EMC_FAKE_ACID, -1, -1
6146 Xfake_acid_2_player, FALSE, FALSE,
6147 EL_EMC_FAKE_ACID, -1, -1
6150 Xfake_acid_3_player, FALSE, FALSE,
6151 EL_EMC_FAKE_ACID, -1, -1
6154 Xfake_acid_4_player, FALSE, FALSE,
6155 EL_EMC_FAKE_ACID, -1, -1
6158 Xfake_acid_5_player, FALSE, FALSE,
6159 EL_EMC_FAKE_ACID, -1, -1
6162 Xfake_acid_6_player, FALSE, FALSE,
6163 EL_EMC_FAKE_ACID, -1, -1
6166 Xfake_acid_7_player, FALSE, FALSE,
6167 EL_EMC_FAKE_ACID, -1, -1
6170 Xfake_acid_8_player, FALSE, FALSE,
6171 EL_EMC_FAKE_ACID, -1, -1
6175 Xgrass, TRUE, FALSE,
6176 EL_EMC_GRASS, -1, -1
6179 Ygrass_nB, FALSE, FALSE,
6180 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6183 Ygrass_eB, FALSE, FALSE,
6184 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6187 Ygrass_sB, FALSE, FALSE,
6188 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6191 Ygrass_wB, FALSE, FALSE,
6192 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6200 Ydirt_nB, FALSE, FALSE,
6201 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6204 Ydirt_eB, FALSE, FALSE,
6205 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6208 Ydirt_sB, FALSE, FALSE,
6209 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6212 Ydirt_wB, FALSE, FALSE,
6213 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6217 Xandroid, TRUE, FALSE,
6218 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6221 Xandroid_1_n, FALSE, FALSE,
6222 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6225 Xandroid_2_n, FALSE, FALSE,
6226 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6229 Xandroid_1_e, FALSE, FALSE,
6230 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6233 Xandroid_2_e, FALSE, FALSE,
6234 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6237 Xandroid_1_w, FALSE, FALSE,
6238 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6241 Xandroid_2_w, FALSE, FALSE,
6242 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6245 Xandroid_1_s, FALSE, FALSE,
6246 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6249 Xandroid_2_s, FALSE, FALSE,
6250 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6253 Yandroid_n, FALSE, FALSE,
6254 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6257 Yandroid_nB, FALSE, TRUE,
6258 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6261 Yandroid_ne, FALSE, FALSE,
6262 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6265 Yandroid_neB, FALSE, TRUE,
6266 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6269 Yandroid_e, FALSE, FALSE,
6270 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6273 Yandroid_eB, FALSE, TRUE,
6274 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6277 Yandroid_se, FALSE, FALSE,
6278 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6281 Yandroid_seB, FALSE, TRUE,
6282 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6285 Yandroid_s, FALSE, FALSE,
6286 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6289 Yandroid_sB, FALSE, TRUE,
6290 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6293 Yandroid_sw, FALSE, FALSE,
6294 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6297 Yandroid_swB, FALSE, TRUE,
6298 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6301 Yandroid_w, FALSE, FALSE,
6302 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6305 Yandroid_wB, FALSE, TRUE,
6306 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6309 Yandroid_nw, FALSE, FALSE,
6310 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6313 Yandroid_nwB, FALSE, TRUE,
6314 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6318 Xeater_n, TRUE, FALSE,
6319 EL_YAMYAM_UP, -1, -1
6322 Xeater_e, TRUE, FALSE,
6323 EL_YAMYAM_RIGHT, -1, -1
6326 Xeater_w, TRUE, FALSE,
6327 EL_YAMYAM_LEFT, -1, -1
6330 Xeater_s, TRUE, FALSE,
6331 EL_YAMYAM_DOWN, -1, -1
6334 Yeater_n, FALSE, FALSE,
6335 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6338 Yeater_nB, FALSE, TRUE,
6339 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6342 Yeater_e, FALSE, FALSE,
6343 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6346 Yeater_eB, FALSE, TRUE,
6347 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6350 Yeater_s, FALSE, FALSE,
6351 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6354 Yeater_sB, FALSE, TRUE,
6355 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6358 Yeater_w, FALSE, FALSE,
6359 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6362 Yeater_wB, FALSE, TRUE,
6363 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6366 Yeater_stone, FALSE, FALSE,
6367 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6370 Yeater_spring, FALSE, FALSE,
6371 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6375 Xalien, TRUE, FALSE,
6379 Xalien_pause, FALSE, FALSE,
6383 Yalien_n, FALSE, FALSE,
6384 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6387 Yalien_nB, FALSE, TRUE,
6388 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6391 Yalien_e, FALSE, FALSE,
6392 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6395 Yalien_eB, FALSE, TRUE,
6396 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6399 Yalien_s, FALSE, FALSE,
6400 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6403 Yalien_sB, FALSE, TRUE,
6404 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6407 Yalien_w, FALSE, FALSE,
6408 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6411 Yalien_wB, FALSE, TRUE,
6412 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6415 Yalien_stone, FALSE, FALSE,
6416 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6419 Yalien_spring, FALSE, FALSE,
6420 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6424 Xbug_1_n, TRUE, FALSE,
6428 Xbug_1_e, TRUE, FALSE,
6429 EL_BUG_RIGHT, -1, -1
6432 Xbug_1_s, TRUE, FALSE,
6436 Xbug_1_w, TRUE, FALSE,
6440 Xbug_2_n, FALSE, FALSE,
6444 Xbug_2_e, FALSE, FALSE,
6445 EL_BUG_RIGHT, -1, -1
6448 Xbug_2_s, FALSE, FALSE,
6452 Xbug_2_w, FALSE, FALSE,
6456 Ybug_n, FALSE, FALSE,
6457 EL_BUG, ACTION_MOVING, MV_BIT_UP
6460 Ybug_nB, FALSE, TRUE,
6461 EL_BUG, ACTION_MOVING, MV_BIT_UP
6464 Ybug_e, FALSE, FALSE,
6465 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6468 Ybug_eB, FALSE, TRUE,
6469 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6472 Ybug_s, FALSE, FALSE,
6473 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6476 Ybug_sB, FALSE, TRUE,
6477 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6480 Ybug_w, FALSE, FALSE,
6481 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6484 Ybug_wB, FALSE, TRUE,
6485 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6488 Ybug_w_n, FALSE, FALSE,
6489 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6492 Ybug_n_e, FALSE, FALSE,
6493 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6496 Ybug_e_s, FALSE, FALSE,
6497 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6500 Ybug_s_w, FALSE, FALSE,
6501 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6504 Ybug_e_n, FALSE, FALSE,
6505 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6508 Ybug_s_e, FALSE, FALSE,
6509 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6512 Ybug_w_s, FALSE, FALSE,
6513 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6516 Ybug_n_w, FALSE, FALSE,
6517 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6520 Ybug_stone, FALSE, FALSE,
6521 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6524 Ybug_spring, FALSE, FALSE,
6525 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6529 Xtank_1_n, TRUE, FALSE,
6530 EL_SPACESHIP_UP, -1, -1
6533 Xtank_1_e, TRUE, FALSE,
6534 EL_SPACESHIP_RIGHT, -1, -1
6537 Xtank_1_s, TRUE, FALSE,
6538 EL_SPACESHIP_DOWN, -1, -1
6541 Xtank_1_w, TRUE, FALSE,
6542 EL_SPACESHIP_LEFT, -1, -1
6545 Xtank_2_n, FALSE, FALSE,
6546 EL_SPACESHIP_UP, -1, -1
6549 Xtank_2_e, FALSE, FALSE,
6550 EL_SPACESHIP_RIGHT, -1, -1
6553 Xtank_2_s, FALSE, FALSE,
6554 EL_SPACESHIP_DOWN, -1, -1
6557 Xtank_2_w, FALSE, FALSE,
6558 EL_SPACESHIP_LEFT, -1, -1
6561 Ytank_n, FALSE, FALSE,
6562 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6565 Ytank_nB, FALSE, TRUE,
6566 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6569 Ytank_e, FALSE, FALSE,
6570 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6573 Ytank_eB, FALSE, TRUE,
6574 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6577 Ytank_s, FALSE, FALSE,
6578 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6581 Ytank_sB, FALSE, TRUE,
6582 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6585 Ytank_w, FALSE, FALSE,
6586 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6589 Ytank_wB, FALSE, TRUE,
6590 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6593 Ytank_w_n, FALSE, FALSE,
6594 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6597 Ytank_n_e, FALSE, FALSE,
6598 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6601 Ytank_e_s, FALSE, FALSE,
6602 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6605 Ytank_s_w, FALSE, FALSE,
6606 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6609 Ytank_e_n, FALSE, FALSE,
6610 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6613 Ytank_s_e, FALSE, FALSE,
6614 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6617 Ytank_w_s, FALSE, FALSE,
6618 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6621 Ytank_n_w, FALSE, FALSE,
6622 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6625 Ytank_stone, FALSE, FALSE,
6626 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6629 Ytank_spring, FALSE, FALSE,
6630 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6634 Xemerald, TRUE, FALSE,
6638 Xemerald_pause, FALSE, FALSE,
6642 Xemerald_fall, FALSE, FALSE,
6646 Xemerald_shine, FALSE, FALSE,
6647 EL_EMERALD, ACTION_TWINKLING, -1
6650 Yemerald_s, FALSE, FALSE,
6651 EL_EMERALD, ACTION_FALLING, -1
6654 Yemerald_sB, FALSE, TRUE,
6655 EL_EMERALD, ACTION_FALLING, -1
6658 Yemerald_e, FALSE, FALSE,
6659 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6662 Yemerald_eB, FALSE, TRUE,
6663 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6666 Yemerald_w, FALSE, FALSE,
6667 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6670 Yemerald_wB, FALSE, TRUE,
6671 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6674 Yemerald_blank, FALSE, FALSE,
6675 EL_EMERALD, ACTION_COLLECTING, -1
6679 Xdiamond, TRUE, FALSE,
6683 Xdiamond_pause, FALSE, FALSE,
6687 Xdiamond_fall, FALSE, FALSE,
6691 Xdiamond_shine, FALSE, FALSE,
6692 EL_DIAMOND, ACTION_TWINKLING, -1
6695 Ydiamond_s, FALSE, FALSE,
6696 EL_DIAMOND, ACTION_FALLING, -1
6699 Ydiamond_sB, FALSE, TRUE,
6700 EL_DIAMOND, ACTION_FALLING, -1
6703 Ydiamond_e, FALSE, FALSE,
6704 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6707 Ydiamond_eB, FALSE, TRUE,
6708 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6711 Ydiamond_w, FALSE, FALSE,
6712 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6715 Ydiamond_wB, FALSE, TRUE,
6716 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6719 Ydiamond_blank, FALSE, FALSE,
6720 EL_DIAMOND, ACTION_COLLECTING, -1
6723 Ydiamond_stone, FALSE, FALSE,
6724 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6728 Xstone, TRUE, FALSE,
6732 Xstone_pause, FALSE, FALSE,
6736 Xstone_fall, FALSE, FALSE,
6740 Ystone_s, FALSE, FALSE,
6741 EL_ROCK, ACTION_FALLING, -1
6744 Ystone_sB, FALSE, TRUE,
6745 EL_ROCK, ACTION_FALLING, -1
6748 Ystone_e, FALSE, FALSE,
6749 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6752 Ystone_eB, FALSE, TRUE,
6753 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6756 Ystone_w, FALSE, FALSE,
6757 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6760 Ystone_wB, FALSE, TRUE,
6761 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6769 Xbomb_pause, FALSE, FALSE,
6773 Xbomb_fall, FALSE, FALSE,
6777 Ybomb_s, FALSE, FALSE,
6778 EL_BOMB, ACTION_FALLING, -1
6781 Ybomb_sB, FALSE, TRUE,
6782 EL_BOMB, ACTION_FALLING, -1
6785 Ybomb_e, FALSE, FALSE,
6786 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6789 Ybomb_eB, FALSE, TRUE,
6790 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6793 Ybomb_w, FALSE, FALSE,
6794 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6797 Ybomb_wB, FALSE, TRUE,
6798 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6801 Ybomb_blank, FALSE, FALSE,
6802 EL_BOMB, ACTION_ACTIVATING, -1
6810 Xnut_pause, FALSE, FALSE,
6814 Xnut_fall, FALSE, FALSE,
6818 Ynut_s, FALSE, FALSE,
6819 EL_NUT, ACTION_FALLING, -1
6822 Ynut_sB, FALSE, TRUE,
6823 EL_NUT, ACTION_FALLING, -1
6826 Ynut_e, FALSE, FALSE,
6827 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6830 Ynut_eB, FALSE, TRUE,
6831 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6834 Ynut_w, FALSE, FALSE,
6835 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6838 Ynut_wB, FALSE, TRUE,
6839 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6842 Ynut_stone, FALSE, FALSE,
6843 EL_NUT, ACTION_BREAKING, -1
6847 Xspring, TRUE, FALSE,
6851 Xspring_pause, FALSE, FALSE,
6855 Xspring_e, TRUE, FALSE,
6856 EL_SPRING_RIGHT, -1, -1
6859 Xspring_w, TRUE, FALSE,
6860 EL_SPRING_LEFT, -1, -1
6863 Xspring_fall, FALSE, FALSE,
6867 Yspring_s, FALSE, FALSE,
6868 EL_SPRING, ACTION_FALLING, -1
6871 Yspring_sB, FALSE, TRUE,
6872 EL_SPRING, ACTION_FALLING, -1
6875 Yspring_e, FALSE, FALSE,
6876 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6879 Yspring_eB, FALSE, TRUE,
6880 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6883 Yspring_w, FALSE, FALSE,
6884 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6887 Yspring_wB, FALSE, TRUE,
6888 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6891 Yspring_alien_e, FALSE, FALSE,
6892 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6895 Yspring_alien_eB, FALSE, TRUE,
6896 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6899 Yspring_alien_w, FALSE, FALSE,
6900 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6903 Yspring_alien_wB, FALSE, TRUE,
6904 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6908 Xpush_emerald_e, FALSE, FALSE,
6909 EL_EMERALD, -1, MV_BIT_RIGHT
6912 Xpush_emerald_w, FALSE, FALSE,
6913 EL_EMERALD, -1, MV_BIT_LEFT
6916 Xpush_diamond_e, FALSE, FALSE,
6917 EL_DIAMOND, -1, MV_BIT_RIGHT
6920 Xpush_diamond_w, FALSE, FALSE,
6921 EL_DIAMOND, -1, MV_BIT_LEFT
6924 Xpush_stone_e, FALSE, FALSE,
6925 EL_ROCK, -1, MV_BIT_RIGHT
6928 Xpush_stone_w, FALSE, FALSE,
6929 EL_ROCK, -1, MV_BIT_LEFT
6932 Xpush_bomb_e, FALSE, FALSE,
6933 EL_BOMB, -1, MV_BIT_RIGHT
6936 Xpush_bomb_w, FALSE, FALSE,
6937 EL_BOMB, -1, MV_BIT_LEFT
6940 Xpush_nut_e, FALSE, FALSE,
6941 EL_NUT, -1, MV_BIT_RIGHT
6944 Xpush_nut_w, FALSE, FALSE,
6945 EL_NUT, -1, MV_BIT_LEFT
6948 Xpush_spring_e, FALSE, FALSE,
6949 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6952 Xpush_spring_w, FALSE, FALSE,
6953 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6957 Xdynamite, TRUE, FALSE,
6958 EL_EM_DYNAMITE, -1, -1
6961 Ydynamite_blank, FALSE, FALSE,
6962 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6965 Xdynamite_1, TRUE, FALSE,
6966 EL_EM_DYNAMITE_ACTIVE, -1, -1
6969 Xdynamite_2, FALSE, FALSE,
6970 EL_EM_DYNAMITE_ACTIVE, -1, -1
6973 Xdynamite_3, FALSE, FALSE,
6974 EL_EM_DYNAMITE_ACTIVE, -1, -1
6977 Xdynamite_4, FALSE, FALSE,
6978 EL_EM_DYNAMITE_ACTIVE, -1, -1
6982 Xkey_1, TRUE, FALSE,
6986 Xkey_2, TRUE, FALSE,
6990 Xkey_3, TRUE, FALSE,
6994 Xkey_4, TRUE, FALSE,
6998 Xkey_5, TRUE, FALSE,
6999 EL_EMC_KEY_5, -1, -1
7002 Xkey_6, TRUE, FALSE,
7003 EL_EMC_KEY_6, -1, -1
7006 Xkey_7, TRUE, FALSE,
7007 EL_EMC_KEY_7, -1, -1
7010 Xkey_8, TRUE, FALSE,
7011 EL_EMC_KEY_8, -1, -1
7015 Xdoor_1, TRUE, FALSE,
7016 EL_EM_GATE_1, -1, -1
7019 Xdoor_2, TRUE, FALSE,
7020 EL_EM_GATE_2, -1, -1
7023 Xdoor_3, TRUE, FALSE,
7024 EL_EM_GATE_3, -1, -1
7027 Xdoor_4, TRUE, FALSE,
7028 EL_EM_GATE_4, -1, -1
7031 Xdoor_5, TRUE, FALSE,
7032 EL_EMC_GATE_5, -1, -1
7035 Xdoor_6, TRUE, FALSE,
7036 EL_EMC_GATE_6, -1, -1
7039 Xdoor_7, TRUE, FALSE,
7040 EL_EMC_GATE_7, -1, -1
7043 Xdoor_8, TRUE, FALSE,
7044 EL_EMC_GATE_8, -1, -1
7048 Xfake_door_1, TRUE, FALSE,
7049 EL_EM_GATE_1_GRAY, -1, -1
7052 Xfake_door_2, TRUE, FALSE,
7053 EL_EM_GATE_2_GRAY, -1, -1
7056 Xfake_door_3, TRUE, FALSE,
7057 EL_EM_GATE_3_GRAY, -1, -1
7060 Xfake_door_4, TRUE, FALSE,
7061 EL_EM_GATE_4_GRAY, -1, -1
7064 Xfake_door_5, TRUE, FALSE,
7065 EL_EMC_GATE_5_GRAY, -1, -1
7068 Xfake_door_6, TRUE, FALSE,
7069 EL_EMC_GATE_6_GRAY, -1, -1
7072 Xfake_door_7, TRUE, FALSE,
7073 EL_EMC_GATE_7_GRAY, -1, -1
7076 Xfake_door_8, TRUE, FALSE,
7077 EL_EMC_GATE_8_GRAY, -1, -1
7081 Xballoon, TRUE, FALSE,
7085 Yballoon_n, FALSE, FALSE,
7086 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7089 Yballoon_nB, FALSE, TRUE,
7090 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7093 Yballoon_e, FALSE, FALSE,
7094 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7097 Yballoon_eB, FALSE, TRUE,
7098 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7101 Yballoon_s, FALSE, FALSE,
7102 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7105 Yballoon_sB, FALSE, TRUE,
7106 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7109 Yballoon_w, FALSE, FALSE,
7110 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7113 Yballoon_wB, FALSE, TRUE,
7114 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7118 Xball_1, TRUE, FALSE,
7119 EL_EMC_MAGIC_BALL, -1, -1
7122 Yball_1, FALSE, FALSE,
7123 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7126 Xball_2, FALSE, FALSE,
7127 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7130 Yball_2, FALSE, FALSE,
7131 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7134 Yball_blank, FALSE, FALSE,
7135 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7139 Xamoeba_1, TRUE, FALSE,
7140 EL_AMOEBA_DRY, ACTION_OTHER, -1
7143 Xamoeba_2, FALSE, FALSE,
7144 EL_AMOEBA_DRY, ACTION_OTHER, -1
7147 Xamoeba_3, FALSE, FALSE,
7148 EL_AMOEBA_DRY, ACTION_OTHER, -1
7151 Xamoeba_4, FALSE, FALSE,
7152 EL_AMOEBA_DRY, ACTION_OTHER, -1
7155 Xamoeba_5, TRUE, FALSE,
7156 EL_AMOEBA_WET, ACTION_OTHER, -1
7159 Xamoeba_6, FALSE, FALSE,
7160 EL_AMOEBA_WET, ACTION_OTHER, -1
7163 Xamoeba_7, FALSE, FALSE,
7164 EL_AMOEBA_WET, ACTION_OTHER, -1
7167 Xamoeba_8, FALSE, FALSE,
7168 EL_AMOEBA_WET, ACTION_OTHER, -1
7173 EL_AMOEBA_DROP, ACTION_GROWING, -1
7176 Xdrip_fall, FALSE, FALSE,
7177 EL_AMOEBA_DROP, -1, -1
7180 Xdrip_stretch, FALSE, FALSE,
7181 EL_AMOEBA_DROP, ACTION_FALLING, -1
7184 Xdrip_stretchB, FALSE, TRUE,
7185 EL_AMOEBA_DROP, ACTION_FALLING, -1
7188 Ydrip_1_s, FALSE, FALSE,
7189 EL_AMOEBA_DROP, ACTION_FALLING, -1
7192 Ydrip_1_sB, FALSE, TRUE,
7193 EL_AMOEBA_DROP, ACTION_FALLING, -1
7196 Ydrip_2_s, FALSE, FALSE,
7197 EL_AMOEBA_DROP, ACTION_FALLING, -1
7200 Ydrip_2_sB, FALSE, TRUE,
7201 EL_AMOEBA_DROP, ACTION_FALLING, -1
7205 Xwonderwall, TRUE, FALSE,
7206 EL_MAGIC_WALL, -1, -1
7209 Ywonderwall, FALSE, FALSE,
7210 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7214 Xwheel, TRUE, FALSE,
7215 EL_ROBOT_WHEEL, -1, -1
7218 Ywheel, FALSE, FALSE,
7219 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7223 Xswitch, TRUE, FALSE,
7224 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7227 Yswitch, FALSE, FALSE,
7228 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7232 Xbumper, TRUE, FALSE,
7233 EL_EMC_SPRING_BUMPER, -1, -1
7236 Ybumper, FALSE, FALSE,
7237 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7241 Xacid_nw, TRUE, FALSE,
7242 EL_ACID_POOL_TOPLEFT, -1, -1
7245 Xacid_ne, TRUE, FALSE,
7246 EL_ACID_POOL_TOPRIGHT, -1, -1
7249 Xacid_sw, TRUE, FALSE,
7250 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7253 Xacid_s, TRUE, FALSE,
7254 EL_ACID_POOL_BOTTOM, -1, -1
7257 Xacid_se, TRUE, FALSE,
7258 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7262 Xfake_blank, TRUE, FALSE,
7263 EL_INVISIBLE_WALL, -1, -1
7266 Yfake_blank, FALSE, FALSE,
7267 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7271 Xfake_grass, TRUE, FALSE,
7272 EL_EMC_FAKE_GRASS, -1, -1
7275 Yfake_grass, FALSE, FALSE,
7276 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7280 Xfake_amoeba, TRUE, FALSE,
7281 EL_EMC_DRIPPER, -1, -1
7284 Yfake_amoeba, FALSE, FALSE,
7285 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7289 Xlenses, TRUE, FALSE,
7290 EL_EMC_LENSES, -1, -1
7294 Xmagnify, TRUE, FALSE,
7295 EL_EMC_MAGNIFIER, -1, -1
7300 EL_QUICKSAND_EMPTY, -1, -1
7303 Xsand_stone, TRUE, FALSE,
7304 EL_QUICKSAND_FULL, -1, -1
7307 Xsand_stonein_1, FALSE, TRUE,
7308 EL_ROCK, ACTION_FILLING, -1
7311 Xsand_stonein_2, FALSE, TRUE,
7312 EL_ROCK, ACTION_FILLING, -1
7315 Xsand_stonein_3, FALSE, TRUE,
7316 EL_ROCK, ACTION_FILLING, -1
7319 Xsand_stonein_4, FALSE, TRUE,
7320 EL_ROCK, ACTION_FILLING, -1
7323 Xsand_sandstone_1, FALSE, FALSE,
7324 EL_QUICKSAND_FILLING, -1, -1
7327 Xsand_sandstone_2, FALSE, FALSE,
7328 EL_QUICKSAND_FILLING, -1, -1
7331 Xsand_sandstone_3, FALSE, FALSE,
7332 EL_QUICKSAND_FILLING, -1, -1
7335 Xsand_sandstone_4, FALSE, FALSE,
7336 EL_QUICKSAND_FILLING, -1, -1
7339 Xsand_stonesand_1, FALSE, FALSE,
7340 EL_QUICKSAND_EMPTYING, -1, -1
7343 Xsand_stonesand_2, FALSE, FALSE,
7344 EL_QUICKSAND_EMPTYING, -1, -1
7347 Xsand_stonesand_3, FALSE, FALSE,
7348 EL_QUICKSAND_EMPTYING, -1, -1
7351 Xsand_stonesand_4, FALSE, FALSE,
7352 EL_QUICKSAND_EMPTYING, -1, -1
7355 Xsand_stoneout_1, FALSE, FALSE,
7356 EL_ROCK, ACTION_EMPTYING, -1
7359 Xsand_stoneout_2, FALSE, FALSE,
7360 EL_ROCK, ACTION_EMPTYING, -1
7363 Xsand_stonesand_quickout_1, FALSE, FALSE,
7364 EL_QUICKSAND_EMPTYING, -1, -1
7367 Xsand_stonesand_quickout_2, FALSE, FALSE,
7368 EL_QUICKSAND_EMPTYING, -1, -1
7372 Xslide_ns, TRUE, FALSE,
7373 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7376 Yslide_ns_blank, FALSE, FALSE,
7377 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7380 Xslide_ew, TRUE, FALSE,
7381 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7384 Yslide_ew_blank, FALSE, FALSE,
7385 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7389 Xwind_n, TRUE, FALSE,
7390 EL_BALLOON_SWITCH_UP, -1, -1
7393 Xwind_e, TRUE, FALSE,
7394 EL_BALLOON_SWITCH_RIGHT, -1, -1
7397 Xwind_s, TRUE, FALSE,
7398 EL_BALLOON_SWITCH_DOWN, -1, -1
7401 Xwind_w, TRUE, FALSE,
7402 EL_BALLOON_SWITCH_LEFT, -1, -1
7405 Xwind_any, TRUE, FALSE,
7406 EL_BALLOON_SWITCH_ANY, -1, -1
7409 Xwind_stop, TRUE, FALSE,
7410 EL_BALLOON_SWITCH_NONE, -1, -1
7415 EL_EM_EXIT_CLOSED, -1, -1
7418 Xexit_1, TRUE, FALSE,
7419 EL_EM_EXIT_OPEN, -1, -1
7422 Xexit_2, FALSE, FALSE,
7423 EL_EM_EXIT_OPEN, -1, -1
7426 Xexit_3, FALSE, FALSE,
7427 EL_EM_EXIT_OPEN, -1, -1
7431 Xpause, FALSE, FALSE,
7436 Xwall_1, TRUE, FALSE,
7440 Xwall_2, TRUE, FALSE,
7441 EL_EMC_WALL_14, -1, -1
7444 Xwall_3, TRUE, FALSE,
7445 EL_EMC_WALL_15, -1, -1
7448 Xwall_4, TRUE, FALSE,
7449 EL_EMC_WALL_16, -1, -1
7453 Xroundwall_1, TRUE, FALSE,
7454 EL_WALL_SLIPPERY, -1, -1
7457 Xroundwall_2, TRUE, FALSE,
7458 EL_EMC_WALL_SLIPPERY_2, -1, -1
7461 Xroundwall_3, TRUE, FALSE,
7462 EL_EMC_WALL_SLIPPERY_3, -1, -1
7465 Xroundwall_4, TRUE, FALSE,
7466 EL_EMC_WALL_SLIPPERY_4, -1, -1
7470 Xsteel_1, TRUE, FALSE,
7471 EL_STEELWALL, -1, -1
7474 Xsteel_2, TRUE, FALSE,
7475 EL_EMC_STEELWALL_2, -1, -1
7478 Xsteel_3, TRUE, FALSE,
7479 EL_EMC_STEELWALL_3, -1, -1
7482 Xsteel_4, TRUE, FALSE,
7483 EL_EMC_STEELWALL_4, -1, -1
7487 Xdecor_1, TRUE, FALSE,
7488 EL_EMC_WALL_8, -1, -1
7491 Xdecor_2, TRUE, FALSE,
7492 EL_EMC_WALL_6, -1, -1
7495 Xdecor_3, TRUE, FALSE,
7496 EL_EMC_WALL_4, -1, -1
7499 Xdecor_4, TRUE, FALSE,
7500 EL_EMC_WALL_7, -1, -1
7503 Xdecor_5, TRUE, FALSE,
7504 EL_EMC_WALL_5, -1, -1
7507 Xdecor_6, TRUE, FALSE,
7508 EL_EMC_WALL_9, -1, -1
7511 Xdecor_7, TRUE, FALSE,
7512 EL_EMC_WALL_10, -1, -1
7515 Xdecor_8, TRUE, FALSE,
7516 EL_EMC_WALL_1, -1, -1
7519 Xdecor_9, TRUE, FALSE,
7520 EL_EMC_WALL_2, -1, -1
7523 Xdecor_10, TRUE, FALSE,
7524 EL_EMC_WALL_3, -1, -1
7527 Xdecor_11, TRUE, FALSE,
7528 EL_EMC_WALL_11, -1, -1
7531 Xdecor_12, TRUE, FALSE,
7532 EL_EMC_WALL_12, -1, -1
7536 Xalpha_0, TRUE, FALSE,
7537 EL_CHAR('0'), -1, -1
7540 Xalpha_1, TRUE, FALSE,
7541 EL_CHAR('1'), -1, -1
7544 Xalpha_2, TRUE, FALSE,
7545 EL_CHAR('2'), -1, -1
7548 Xalpha_3, TRUE, FALSE,
7549 EL_CHAR('3'), -1, -1
7552 Xalpha_4, TRUE, FALSE,
7553 EL_CHAR('4'), -1, -1
7556 Xalpha_5, TRUE, FALSE,
7557 EL_CHAR('5'), -1, -1
7560 Xalpha_6, TRUE, FALSE,
7561 EL_CHAR('6'), -1, -1
7564 Xalpha_7, TRUE, FALSE,
7565 EL_CHAR('7'), -1, -1
7568 Xalpha_8, TRUE, FALSE,
7569 EL_CHAR('8'), -1, -1
7572 Xalpha_9, TRUE, FALSE,
7573 EL_CHAR('9'), -1, -1
7576 Xalpha_excla, TRUE, FALSE,
7577 EL_CHAR('!'), -1, -1
7580 Xalpha_apost, TRUE, FALSE,
7581 EL_CHAR('\''), -1, -1
7584 Xalpha_comma, TRUE, FALSE,
7585 EL_CHAR(','), -1, -1
7588 Xalpha_minus, TRUE, FALSE,
7589 EL_CHAR('-'), -1, -1
7592 Xalpha_perio, TRUE, FALSE,
7593 EL_CHAR('.'), -1, -1
7596 Xalpha_colon, TRUE, FALSE,
7597 EL_CHAR(':'), -1, -1
7600 Xalpha_quest, TRUE, FALSE,
7601 EL_CHAR('?'), -1, -1
7604 Xalpha_a, TRUE, FALSE,
7605 EL_CHAR('A'), -1, -1
7608 Xalpha_b, TRUE, FALSE,
7609 EL_CHAR('B'), -1, -1
7612 Xalpha_c, TRUE, FALSE,
7613 EL_CHAR('C'), -1, -1
7616 Xalpha_d, TRUE, FALSE,
7617 EL_CHAR('D'), -1, -1
7620 Xalpha_e, TRUE, FALSE,
7621 EL_CHAR('E'), -1, -1
7624 Xalpha_f, TRUE, FALSE,
7625 EL_CHAR('F'), -1, -1
7628 Xalpha_g, TRUE, FALSE,
7629 EL_CHAR('G'), -1, -1
7632 Xalpha_h, TRUE, FALSE,
7633 EL_CHAR('H'), -1, -1
7636 Xalpha_i, TRUE, FALSE,
7637 EL_CHAR('I'), -1, -1
7640 Xalpha_j, TRUE, FALSE,
7641 EL_CHAR('J'), -1, -1
7644 Xalpha_k, TRUE, FALSE,
7645 EL_CHAR('K'), -1, -1
7648 Xalpha_l, TRUE, FALSE,
7649 EL_CHAR('L'), -1, -1
7652 Xalpha_m, TRUE, FALSE,
7653 EL_CHAR('M'), -1, -1
7656 Xalpha_n, TRUE, FALSE,
7657 EL_CHAR('N'), -1, -1
7660 Xalpha_o, TRUE, FALSE,
7661 EL_CHAR('O'), -1, -1
7664 Xalpha_p, TRUE, FALSE,
7665 EL_CHAR('P'), -1, -1
7668 Xalpha_q, TRUE, FALSE,
7669 EL_CHAR('Q'), -1, -1
7672 Xalpha_r, TRUE, FALSE,
7673 EL_CHAR('R'), -1, -1
7676 Xalpha_s, TRUE, FALSE,
7677 EL_CHAR('S'), -1, -1
7680 Xalpha_t, TRUE, FALSE,
7681 EL_CHAR('T'), -1, -1
7684 Xalpha_u, TRUE, FALSE,
7685 EL_CHAR('U'), -1, -1
7688 Xalpha_v, TRUE, FALSE,
7689 EL_CHAR('V'), -1, -1
7692 Xalpha_w, TRUE, FALSE,
7693 EL_CHAR('W'), -1, -1
7696 Xalpha_x, TRUE, FALSE,
7697 EL_CHAR('X'), -1, -1
7700 Xalpha_y, TRUE, FALSE,
7701 EL_CHAR('Y'), -1, -1
7704 Xalpha_z, TRUE, FALSE,
7705 EL_CHAR('Z'), -1, -1
7708 Xalpha_arrow_e, TRUE, FALSE,
7709 EL_CHAR('>'), -1, -1
7712 Xalpha_arrow_w, TRUE, FALSE,
7713 EL_CHAR('<'), -1, -1
7716 Xalpha_copyr, TRUE, FALSE,
7717 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7721 Ykey_1_blank, FALSE, FALSE,
7722 EL_EM_KEY_1, ACTION_COLLECTING, -1
7725 Ykey_2_blank, FALSE, FALSE,
7726 EL_EM_KEY_2, ACTION_COLLECTING, -1
7729 Ykey_3_blank, FALSE, FALSE,
7730 EL_EM_KEY_3, ACTION_COLLECTING, -1
7733 Ykey_4_blank, FALSE, FALSE,
7734 EL_EM_KEY_4, ACTION_COLLECTING, -1
7737 Ykey_5_blank, FALSE, FALSE,
7738 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7741 Ykey_6_blank, FALSE, FALSE,
7742 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7745 Ykey_7_blank, FALSE, FALSE,
7746 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7749 Ykey_8_blank, FALSE, FALSE,
7750 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7753 Ylenses_blank, FALSE, FALSE,
7754 EL_EMC_LENSES, ACTION_COLLECTING, -1
7757 Ymagnify_blank, FALSE, FALSE,
7758 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7761 Ygrass_blank, FALSE, FALSE,
7762 EL_EMC_GRASS, ACTION_SNAPPING, -1
7765 Ydirt_blank, FALSE, FALSE,
7766 EL_SAND, ACTION_SNAPPING, -1
7775 static struct Mapping_EM_to_RND_player
7784 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7788 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7792 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7796 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7800 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7804 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7808 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7812 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7816 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7820 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7824 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7828 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7832 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7836 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7840 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7844 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7848 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7852 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7856 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7860 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7864 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7868 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7872 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7876 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7880 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7884 EL_PLAYER_1, ACTION_DEFAULT, -1,
7888 EL_PLAYER_2, ACTION_DEFAULT, -1,
7892 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7896 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7900 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7904 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7908 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7912 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7916 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7920 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7924 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7928 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7932 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7936 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7940 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7944 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7948 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7952 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7956 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7960 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7964 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7968 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7972 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7976 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7980 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7984 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7988 EL_PLAYER_3, ACTION_DEFAULT, -1,
7992 EL_PLAYER_4, ACTION_DEFAULT, -1,
8001 int map_element_RND_to_EM_cave(int element_rnd)
8003 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
8004 static boolean mapping_initialized = FALSE;
8006 if (!mapping_initialized)
8010 // return "Xalpha_quest" for all undefined elements in mapping array
8011 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8012 mapping_RND_to_EM[i] = Xalpha_quest;
8014 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8015 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
8016 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
8017 em_object_mapping_list[i].element_em;
8019 mapping_initialized = TRUE;
8022 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
8024 Warn("invalid RND level element %d", element_rnd);
8029 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
8032 int map_element_EM_to_RND_cave(int element_em_cave)
8034 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8035 static boolean mapping_initialized = FALSE;
8037 if (!mapping_initialized)
8041 // return "EL_UNKNOWN" for all undefined elements in mapping array
8042 for (i = 0; i < GAME_TILE_MAX; i++)
8043 mapping_EM_to_RND[i] = EL_UNKNOWN;
8045 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8046 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8047 em_object_mapping_list[i].element_rnd;
8049 mapping_initialized = TRUE;
8052 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8054 Warn("invalid EM cave element %d", element_em_cave);
8059 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8062 int map_element_EM_to_RND_game(int element_em_game)
8064 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8065 static boolean mapping_initialized = FALSE;
8067 if (!mapping_initialized)
8071 // return "EL_UNKNOWN" for all undefined elements in mapping array
8072 for (i = 0; i < GAME_TILE_MAX; i++)
8073 mapping_EM_to_RND[i] = EL_UNKNOWN;
8075 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8076 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8077 em_object_mapping_list[i].element_rnd;
8079 mapping_initialized = TRUE;
8082 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8084 Warn("invalid EM game element %d", element_em_game);
8089 return mapping_EM_to_RND[element_em_game];
8092 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8094 struct LevelInfo_EM *level_em = level->native_em_level;
8095 struct CAVE *cav = level_em->cav;
8098 for (i = 0; i < GAME_TILE_MAX; i++)
8099 cav->android_array[i] = Cblank;
8101 for (i = 0; i < level->num_android_clone_elements; i++)
8103 int element_rnd = level->android_clone_element[i];
8104 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8106 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8107 if (em_object_mapping_list[j].element_rnd == element_rnd)
8108 cav->android_array[em_object_mapping_list[j].element_em] =
8113 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8115 struct LevelInfo_EM *level_em = level->native_em_level;
8116 struct CAVE *cav = level_em->cav;
8119 level->num_android_clone_elements = 0;
8121 for (i = 0; i < GAME_TILE_MAX; i++)
8123 int element_em_cave = cav->android_array[i];
8125 boolean element_found = FALSE;
8127 if (element_em_cave == Cblank)
8130 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8132 for (j = 0; j < level->num_android_clone_elements; j++)
8133 if (level->android_clone_element[j] == element_rnd)
8134 element_found = TRUE;
8138 level->android_clone_element[level->num_android_clone_elements++] =
8141 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8146 if (level->num_android_clone_elements == 0)
8148 level->num_android_clone_elements = 1;
8149 level->android_clone_element[0] = EL_EMPTY;
8153 int map_direction_RND_to_EM(int direction)
8155 return (direction == MV_UP ? 0 :
8156 direction == MV_RIGHT ? 1 :
8157 direction == MV_DOWN ? 2 :
8158 direction == MV_LEFT ? 3 :
8162 int map_direction_EM_to_RND(int direction)
8164 return (direction == 0 ? MV_UP :
8165 direction == 1 ? MV_RIGHT :
8166 direction == 2 ? MV_DOWN :
8167 direction == 3 ? MV_LEFT :
8171 int map_element_RND_to_SP(int element_rnd)
8173 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8175 if (element_rnd >= EL_SP_START &&
8176 element_rnd <= EL_SP_END)
8177 element_sp = element_rnd - EL_SP_START;
8178 else if (element_rnd == EL_EMPTY_SPACE)
8180 else if (element_rnd == EL_INVISIBLE_WALL)
8186 int map_element_SP_to_RND(int element_sp)
8188 int element_rnd = EL_UNKNOWN;
8190 if (element_sp >= 0x00 &&
8192 element_rnd = EL_SP_START + element_sp;
8193 else if (element_sp == 0x28)
8194 element_rnd = EL_INVISIBLE_WALL;
8199 int map_action_SP_to_RND(int action_sp)
8203 case actActive: return ACTION_ACTIVE;
8204 case actImpact: return ACTION_IMPACT;
8205 case actExploding: return ACTION_EXPLODING;
8206 case actDigging: return ACTION_DIGGING;
8207 case actSnapping: return ACTION_SNAPPING;
8208 case actCollecting: return ACTION_COLLECTING;
8209 case actPassing: return ACTION_PASSING;
8210 case actPushing: return ACTION_PUSHING;
8211 case actDropping: return ACTION_DROPPING;
8213 default: return ACTION_DEFAULT;
8217 int map_element_RND_to_MM(int element_rnd)
8219 return (element_rnd >= EL_MM_START_1 &&
8220 element_rnd <= EL_MM_END_1 ?
8221 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8223 element_rnd >= EL_MM_START_2 &&
8224 element_rnd <= EL_MM_END_2 ?
8225 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8227 element_rnd >= EL_MM_START_3 &&
8228 element_rnd <= EL_MM_END_3 ?
8229 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
8231 element_rnd >= EL_CHAR_START &&
8232 element_rnd <= EL_CHAR_END ?
8233 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8235 element_rnd >= EL_MM_RUNTIME_START &&
8236 element_rnd <= EL_MM_RUNTIME_END ?
8237 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8239 EL_MM_EMPTY_NATIVE);
8242 int map_element_MM_to_RND(int element_mm)
8244 return (element_mm == EL_MM_EMPTY_NATIVE ||
8245 element_mm == EL_DF_EMPTY_NATIVE ?
8248 element_mm >= EL_MM_START_1_NATIVE &&
8249 element_mm <= EL_MM_END_1_NATIVE ?
8250 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8252 element_mm >= EL_MM_START_2_NATIVE &&
8253 element_mm <= EL_MM_END_2_NATIVE ?
8254 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8256 element_mm >= EL_MM_START_3_NATIVE &&
8257 element_mm <= EL_MM_END_3_NATIVE ?
8258 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
8260 element_mm >= EL_MM_CHAR_START_NATIVE &&
8261 element_mm <= EL_MM_CHAR_END_NATIVE ?
8262 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8264 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8265 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8266 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8271 int map_action_MM_to_RND(int action_mm)
8273 // all MM actions are defined to exactly match their RND counterparts
8277 int map_sound_MM_to_RND(int sound_mm)
8281 case SND_MM_GAME_LEVELTIME_CHARGING:
8282 return SND_GAME_LEVELTIME_CHARGING;
8284 case SND_MM_GAME_HEALTH_CHARGING:
8285 return SND_GAME_HEALTH_CHARGING;
8288 return SND_UNDEFINED;
8292 int map_mm_wall_element(int element)
8294 return (element >= EL_MM_STEEL_WALL_START &&
8295 element <= EL_MM_STEEL_WALL_END ?
8298 element >= EL_MM_WOODEN_WALL_START &&
8299 element <= EL_MM_WOODEN_WALL_END ?
8302 element >= EL_MM_ICE_WALL_START &&
8303 element <= EL_MM_ICE_WALL_END ?
8306 element >= EL_MM_AMOEBA_WALL_START &&
8307 element <= EL_MM_AMOEBA_WALL_END ?
8310 element >= EL_DF_STEEL_WALL_START &&
8311 element <= EL_DF_STEEL_WALL_END ?
8314 element >= EL_DF_WOODEN_WALL_START &&
8315 element <= EL_DF_WOODEN_WALL_END ?
8321 int map_mm_wall_element_editor(int element)
8325 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8326 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8327 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8328 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8329 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8330 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8332 default: return element;
8336 int get_next_element(int element)
8340 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8341 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8342 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8343 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8344 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8345 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8346 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8347 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8348 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8349 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8350 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8352 default: return element;
8356 int el2img_mm(int element_mm)
8358 return el2img(map_element_MM_to_RND(element_mm));
8361 int el_act2img_mm(int element_mm, int action)
8363 return el_act2img(map_element_MM_to_RND(element_mm), action);
8366 int el_act_dir2img(int element, int action, int direction)
8368 element = GFX_ELEMENT(element);
8369 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8371 // direction_graphic[][] == graphic[] for undefined direction graphics
8372 return element_info[element].direction_graphic[action][direction];
8375 static int el_act_dir2crm(int element, int action, int direction)
8377 element = GFX_ELEMENT(element);
8378 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8380 // direction_graphic[][] == graphic[] for undefined direction graphics
8381 return element_info[element].direction_crumbled[action][direction];
8384 int el_act2img(int element, int action)
8386 element = GFX_ELEMENT(element);
8388 return element_info[element].graphic[action];
8391 int el_act2crm(int element, int action)
8393 element = GFX_ELEMENT(element);
8395 return element_info[element].crumbled[action];
8398 int el_dir2img(int element, int direction)
8400 element = GFX_ELEMENT(element);
8402 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8405 int el2baseimg(int element)
8407 return element_info[element].graphic[ACTION_DEFAULT];
8410 int el2img(int element)
8412 element = GFX_ELEMENT(element);
8414 return element_info[element].graphic[ACTION_DEFAULT];
8417 int el2edimg(int element)
8419 element = GFX_ELEMENT(element);
8421 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8424 int el2preimg(int element)
8426 element = GFX_ELEMENT(element);
8428 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8431 int el2panelimg(int element)
8433 element = GFX_ELEMENT(element);
8435 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8438 int font2baseimg(int font_nr)
8440 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8443 int getBeltNrFromBeltElement(int element)
8445 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8446 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8447 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8450 int getBeltNrFromBeltActiveElement(int element)
8452 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8453 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8454 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8457 int getBeltNrFromBeltSwitchElement(int element)
8459 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8460 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8461 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8464 int getBeltDirNrFromBeltElement(int element)
8466 static int belt_base_element[4] =
8468 EL_CONVEYOR_BELT_1_LEFT,
8469 EL_CONVEYOR_BELT_2_LEFT,
8470 EL_CONVEYOR_BELT_3_LEFT,
8471 EL_CONVEYOR_BELT_4_LEFT
8474 int belt_nr = getBeltNrFromBeltElement(element);
8475 int belt_dir_nr = element - belt_base_element[belt_nr];
8477 return (belt_dir_nr % 3);
8480 int getBeltDirNrFromBeltSwitchElement(int element)
8482 static int belt_base_element[4] =
8484 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8485 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8486 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8487 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8490 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8491 int belt_dir_nr = element - belt_base_element[belt_nr];
8493 return (belt_dir_nr % 3);
8496 int getBeltDirFromBeltElement(int element)
8498 static int belt_move_dir[3] =
8505 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8507 return belt_move_dir[belt_dir_nr];
8510 int getBeltDirFromBeltSwitchElement(int element)
8512 static int belt_move_dir[3] =
8519 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8521 return belt_move_dir[belt_dir_nr];
8524 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8526 static int belt_base_element[4] =
8528 EL_CONVEYOR_BELT_1_LEFT,
8529 EL_CONVEYOR_BELT_2_LEFT,
8530 EL_CONVEYOR_BELT_3_LEFT,
8531 EL_CONVEYOR_BELT_4_LEFT
8534 return belt_base_element[belt_nr] + belt_dir_nr;
8537 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8539 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8541 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8544 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8546 static int belt_base_element[4] =
8548 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8549 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8550 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8551 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8554 return belt_base_element[belt_nr] + belt_dir_nr;
8557 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8559 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8561 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8564 boolean swapTiles_EM(boolean is_pre_emc_cave)
8566 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8569 boolean getTeamMode_EM(void)
8571 return game.team_mode || network_playing;
8574 boolean isActivePlayer_EM(int player_nr)
8576 return stored_player[player_nr].active;
8579 unsigned int InitRND(int seed)
8581 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8582 return InitEngineRandom_EM(seed);
8583 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8584 return InitEngineRandom_SP(seed);
8585 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8586 return InitEngineRandom_MM(seed);
8588 return InitEngineRandom_RND(seed);
8591 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8592 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8594 static int get_effective_element_EM(int tile, int frame_em)
8596 int element = object_mapping[tile].element_rnd;
8597 int action = object_mapping[tile].action;
8598 boolean is_backside = object_mapping[tile].is_backside;
8599 boolean action_removing = (action == ACTION_DIGGING ||
8600 action == ACTION_SNAPPING ||
8601 action == ACTION_COLLECTING);
8609 return (frame_em > 5 ? EL_EMPTY : element);
8615 else // frame_em == 7
8626 case Ydiamond_stone:
8630 case Xdrip_stretchB:
8646 case Ymagnify_blank:
8649 case Xsand_stonein_1:
8650 case Xsand_stonein_2:
8651 case Xsand_stonein_3:
8652 case Xsand_stonein_4:
8656 return (is_backside || action_removing ? EL_EMPTY : element);
8661 static boolean check_linear_animation_EM(int tile)
8665 case Xsand_stonesand_1:
8666 case Xsand_stonesand_quickout_1:
8667 case Xsand_sandstone_1:
8668 case Xsand_stonein_1:
8669 case Xsand_stoneout_1:
8697 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8698 boolean has_crumbled_graphics,
8699 int crumbled, int sync_frame)
8701 // if element can be crumbled, but certain action graphics are just empty
8702 // space (like instantly snapping sand to empty space in 1 frame), do not
8703 // treat these empty space graphics as crumbled graphics in EMC engine
8704 if (crumbled == IMG_EMPTY_SPACE)
8705 has_crumbled_graphics = FALSE;
8707 if (has_crumbled_graphics)
8709 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8710 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8711 g_crumbled->anim_delay,
8712 g_crumbled->anim_mode,
8713 g_crumbled->anim_start_frame,
8716 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8717 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8719 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8720 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8722 g_em->has_crumbled_graphics = TRUE;
8726 g_em->crumbled_bitmap = NULL;
8727 g_em->crumbled_src_x = 0;
8728 g_em->crumbled_src_y = 0;
8729 g_em->crumbled_border_size = 0;
8730 g_em->crumbled_tile_size = 0;
8732 g_em->has_crumbled_graphics = FALSE;
8737 void ResetGfxAnimation_EM(int x, int y, int tile)
8743 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8744 int tile, int frame_em, int x, int y)
8746 int action = object_mapping[tile].action;
8747 int direction = object_mapping[tile].direction;
8748 int effective_element = get_effective_element_EM(tile, frame_em);
8749 int graphic = (direction == MV_NONE ?
8750 el_act2img(effective_element, action) :
8751 el_act_dir2img(effective_element, action, direction));
8752 struct GraphicInfo *g = &graphic_info[graphic];
8754 boolean action_removing = (action == ACTION_DIGGING ||
8755 action == ACTION_SNAPPING ||
8756 action == ACTION_COLLECTING);
8757 boolean action_moving = (action == ACTION_FALLING ||
8758 action == ACTION_MOVING ||
8759 action == ACTION_PUSHING ||
8760 action == ACTION_EATING ||
8761 action == ACTION_FILLING ||
8762 action == ACTION_EMPTYING);
8763 boolean action_falling = (action == ACTION_FALLING ||
8764 action == ACTION_FILLING ||
8765 action == ACTION_EMPTYING);
8767 // special case: graphic uses "2nd movement tile" and has defined
8768 // 7 frames for movement animation (or less) => use default graphic
8769 // for last (8th) frame which ends the movement animation
8770 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8772 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8773 graphic = (direction == MV_NONE ?
8774 el_act2img(effective_element, action) :
8775 el_act_dir2img(effective_element, action, direction));
8777 g = &graphic_info[graphic];
8780 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8784 else if (action_moving)
8786 boolean is_backside = object_mapping[tile].is_backside;
8790 int direction = object_mapping[tile].direction;
8791 int move_dir = (action_falling ? MV_DOWN : direction);
8796 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8797 if (g->double_movement && frame_em == 0)
8801 if (move_dir == MV_LEFT)
8802 GfxFrame[x - 1][y] = GfxFrame[x][y];
8803 else if (move_dir == MV_RIGHT)
8804 GfxFrame[x + 1][y] = GfxFrame[x][y];
8805 else if (move_dir == MV_UP)
8806 GfxFrame[x][y - 1] = GfxFrame[x][y];
8807 else if (move_dir == MV_DOWN)
8808 GfxFrame[x][y + 1] = GfxFrame[x][y];
8815 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8816 if (tile == Xsand_stonesand_quickout_1 ||
8817 tile == Xsand_stonesand_quickout_2)
8821 if (graphic_info[graphic].anim_global_sync)
8822 sync_frame = FrameCounter;
8823 else if (graphic_info[graphic].anim_global_anim_sync)
8824 sync_frame = getGlobalAnimSyncFrame();
8825 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8826 sync_frame = GfxFrame[x][y];
8828 sync_frame = 0; // playfield border (pseudo steel)
8830 SetRandomAnimationValue(x, y);
8832 int frame = getAnimationFrame(g->anim_frames,
8835 g->anim_start_frame,
8838 g_em->unique_identifier =
8839 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8842 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8843 int tile, int frame_em, int x, int y)
8845 int action = object_mapping[tile].action;
8846 int direction = object_mapping[tile].direction;
8847 boolean is_backside = object_mapping[tile].is_backside;
8848 int effective_element = get_effective_element_EM(tile, frame_em);
8849 int effective_action = action;
8850 int graphic = (direction == MV_NONE ?
8851 el_act2img(effective_element, effective_action) :
8852 el_act_dir2img(effective_element, effective_action,
8854 int crumbled = (direction == MV_NONE ?
8855 el_act2crm(effective_element, effective_action) :
8856 el_act_dir2crm(effective_element, effective_action,
8858 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8859 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8860 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8861 struct GraphicInfo *g = &graphic_info[graphic];
8864 // special case: graphic uses "2nd movement tile" and has defined
8865 // 7 frames for movement animation (or less) => use default graphic
8866 // for last (8th) frame which ends the movement animation
8867 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8869 effective_action = ACTION_DEFAULT;
8870 graphic = (direction == MV_NONE ?
8871 el_act2img(effective_element, effective_action) :
8872 el_act_dir2img(effective_element, effective_action,
8874 crumbled = (direction == MV_NONE ?
8875 el_act2crm(effective_element, effective_action) :
8876 el_act_dir2crm(effective_element, effective_action,
8879 g = &graphic_info[graphic];
8882 if (graphic_info[graphic].anim_global_sync)
8883 sync_frame = FrameCounter;
8884 else if (graphic_info[graphic].anim_global_anim_sync)
8885 sync_frame = getGlobalAnimSyncFrame();
8886 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8887 sync_frame = GfxFrame[x][y];
8889 sync_frame = 0; // playfield border (pseudo steel)
8891 SetRandomAnimationValue(x, y);
8893 int frame = getAnimationFrame(g->anim_frames,
8896 g->anim_start_frame,
8899 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8900 g->double_movement && is_backside);
8902 // (updating the "crumbled" graphic definitions is probably not really needed,
8903 // as animations for crumbled graphics can't be longer than one EMC cycle)
8904 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8908 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8909 int player_nr, int anim, int frame_em)
8911 int element = player_mapping[player_nr][anim].element_rnd;
8912 int action = player_mapping[player_nr][anim].action;
8913 int direction = player_mapping[player_nr][anim].direction;
8914 int graphic = (direction == MV_NONE ?
8915 el_act2img(element, action) :
8916 el_act_dir2img(element, action, direction));
8917 struct GraphicInfo *g = &graphic_info[graphic];
8920 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8922 stored_player[player_nr].StepFrame = frame_em;
8924 sync_frame = stored_player[player_nr].Frame;
8926 int frame = getAnimationFrame(g->anim_frames,
8929 g->anim_start_frame,
8932 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8933 &g_em->src_x, &g_em->src_y, FALSE);
8936 void InitGraphicInfo_EM(void)
8940 // always start with reliable default values
8941 for (i = 0; i < GAME_TILE_MAX; i++)
8943 object_mapping[i].element_rnd = EL_UNKNOWN;
8944 object_mapping[i].is_backside = FALSE;
8945 object_mapping[i].action = ACTION_DEFAULT;
8946 object_mapping[i].direction = MV_NONE;
8949 // always start with reliable default values
8950 for (p = 0; p < MAX_PLAYERS; p++)
8952 for (i = 0; i < PLY_MAX; i++)
8954 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8955 player_mapping[p][i].action = ACTION_DEFAULT;
8956 player_mapping[p][i].direction = MV_NONE;
8960 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8962 int e = em_object_mapping_list[i].element_em;
8964 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8965 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8967 if (em_object_mapping_list[i].action != -1)
8968 object_mapping[e].action = em_object_mapping_list[i].action;
8970 if (em_object_mapping_list[i].direction != -1)
8971 object_mapping[e].direction =
8972 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8975 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8977 int a = em_player_mapping_list[i].action_em;
8978 int p = em_player_mapping_list[i].player_nr;
8980 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8982 if (em_player_mapping_list[i].action != -1)
8983 player_mapping[p][a].action = em_player_mapping_list[i].action;
8985 if (em_player_mapping_list[i].direction != -1)
8986 player_mapping[p][a].direction =
8987 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8990 for (i = 0; i < GAME_TILE_MAX; i++)
8992 int element = object_mapping[i].element_rnd;
8993 int action = object_mapping[i].action;
8994 int direction = object_mapping[i].direction;
8995 boolean is_backside = object_mapping[i].is_backside;
8996 boolean action_exploding = ((action == ACTION_EXPLODING ||
8997 action == ACTION_SMASHED_BY_ROCK ||
8998 action == ACTION_SMASHED_BY_SPRING) &&
8999 element != EL_DIAMOND);
9000 boolean action_active = (action == ACTION_ACTIVE);
9001 boolean action_other = (action == ACTION_OTHER);
9003 for (j = 0; j < 8; j++)
9005 int effective_element = get_effective_element_EM(i, j);
9006 int effective_action = (j < 7 ? action :
9007 i == Xdrip_stretch ? action :
9008 i == Xdrip_stretchB ? action :
9009 i == Ydrip_1_s ? action :
9010 i == Ydrip_1_sB ? action :
9011 i == Yball_1 ? action :
9012 i == Xball_2 ? action :
9013 i == Yball_2 ? action :
9014 i == Yball_blank ? action :
9015 i == Ykey_1_blank ? action :
9016 i == Ykey_2_blank ? action :
9017 i == Ykey_3_blank ? action :
9018 i == Ykey_4_blank ? action :
9019 i == Ykey_5_blank ? action :
9020 i == Ykey_6_blank ? action :
9021 i == Ykey_7_blank ? action :
9022 i == Ykey_8_blank ? action :
9023 i == Ylenses_blank ? action :
9024 i == Ymagnify_blank ? action :
9025 i == Ygrass_blank ? action :
9026 i == Ydirt_blank ? action :
9027 i == Xsand_stonein_1 ? action :
9028 i == Xsand_stonein_2 ? action :
9029 i == Xsand_stonein_3 ? action :
9030 i == Xsand_stonein_4 ? action :
9031 i == Xsand_stoneout_1 ? action :
9032 i == Xsand_stoneout_2 ? action :
9033 i == Xboom_android ? ACTION_EXPLODING :
9034 action_exploding ? ACTION_EXPLODING :
9035 action_active ? action :
9036 action_other ? action :
9038 int graphic = (el_act_dir2img(effective_element, effective_action,
9040 int crumbled = (el_act_dir2crm(effective_element, effective_action,
9042 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
9043 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
9044 boolean has_action_graphics = (graphic != base_graphic);
9045 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9046 struct GraphicInfo *g = &graphic_info[graphic];
9047 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9050 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9051 boolean special_animation = (action != ACTION_DEFAULT &&
9052 g->anim_frames == 3 &&
9053 g->anim_delay == 2 &&
9054 g->anim_mode & ANIM_LINEAR);
9055 int sync_frame = (i == Xdrip_stretch ? 7 :
9056 i == Xdrip_stretchB ? 7 :
9057 i == Ydrip_2_s ? j + 8 :
9058 i == Ydrip_2_sB ? j + 8 :
9067 i == Xfake_acid_1 ? 0 :
9068 i == Xfake_acid_2 ? 10 :
9069 i == Xfake_acid_3 ? 20 :
9070 i == Xfake_acid_4 ? 30 :
9071 i == Xfake_acid_5 ? 40 :
9072 i == Xfake_acid_6 ? 50 :
9073 i == Xfake_acid_7 ? 60 :
9074 i == Xfake_acid_8 ? 70 :
9075 i == Xfake_acid_1_player ? 0 :
9076 i == Xfake_acid_2_player ? 10 :
9077 i == Xfake_acid_3_player ? 20 :
9078 i == Xfake_acid_4_player ? 30 :
9079 i == Xfake_acid_5_player ? 40 :
9080 i == Xfake_acid_6_player ? 50 :
9081 i == Xfake_acid_7_player ? 60 :
9082 i == Xfake_acid_8_player ? 70 :
9084 i == Yball_2 ? j + 8 :
9085 i == Yball_blank ? j + 1 :
9086 i == Ykey_1_blank ? j + 1 :
9087 i == Ykey_2_blank ? j + 1 :
9088 i == Ykey_3_blank ? j + 1 :
9089 i == Ykey_4_blank ? j + 1 :
9090 i == Ykey_5_blank ? j + 1 :
9091 i == Ykey_6_blank ? j + 1 :
9092 i == Ykey_7_blank ? j + 1 :
9093 i == Ykey_8_blank ? j + 1 :
9094 i == Ylenses_blank ? j + 1 :
9095 i == Ymagnify_blank ? j + 1 :
9096 i == Ygrass_blank ? j + 1 :
9097 i == Ydirt_blank ? j + 1 :
9098 i == Xamoeba_1 ? 0 :
9099 i == Xamoeba_2 ? 1 :
9100 i == Xamoeba_3 ? 2 :
9101 i == Xamoeba_4 ? 3 :
9102 i == Xamoeba_5 ? 0 :
9103 i == Xamoeba_6 ? 1 :
9104 i == Xamoeba_7 ? 2 :
9105 i == Xamoeba_8 ? 3 :
9106 i == Xexit_2 ? j + 8 :
9107 i == Xexit_3 ? j + 16 :
9108 i == Xdynamite_1 ? 0 :
9109 i == Xdynamite_2 ? 8 :
9110 i == Xdynamite_3 ? 16 :
9111 i == Xdynamite_4 ? 24 :
9112 i == Xsand_stonein_1 ? j + 1 :
9113 i == Xsand_stonein_2 ? j + 9 :
9114 i == Xsand_stonein_3 ? j + 17 :
9115 i == Xsand_stonein_4 ? j + 25 :
9116 i == Xsand_stoneout_1 && j == 0 ? 0 :
9117 i == Xsand_stoneout_1 && j == 1 ? 0 :
9118 i == Xsand_stoneout_1 && j == 2 ? 1 :
9119 i == Xsand_stoneout_1 && j == 3 ? 2 :
9120 i == Xsand_stoneout_1 && j == 4 ? 2 :
9121 i == Xsand_stoneout_1 && j == 5 ? 3 :
9122 i == Xsand_stoneout_1 && j == 6 ? 4 :
9123 i == Xsand_stoneout_1 && j == 7 ? 4 :
9124 i == Xsand_stoneout_2 && j == 0 ? 5 :
9125 i == Xsand_stoneout_2 && j == 1 ? 6 :
9126 i == Xsand_stoneout_2 && j == 2 ? 7 :
9127 i == Xsand_stoneout_2 && j == 3 ? 8 :
9128 i == Xsand_stoneout_2 && j == 4 ? 9 :
9129 i == Xsand_stoneout_2 && j == 5 ? 11 :
9130 i == Xsand_stoneout_2 && j == 6 ? 13 :
9131 i == Xsand_stoneout_2 && j == 7 ? 15 :
9132 i == Xboom_bug && j == 1 ? 2 :
9133 i == Xboom_bug && j == 2 ? 2 :
9134 i == Xboom_bug && j == 3 ? 4 :
9135 i == Xboom_bug && j == 4 ? 4 :
9136 i == Xboom_bug && j == 5 ? 2 :
9137 i == Xboom_bug && j == 6 ? 2 :
9138 i == Xboom_bug && j == 7 ? 0 :
9139 i == Xboom_tank && j == 1 ? 2 :
9140 i == Xboom_tank && j == 2 ? 2 :
9141 i == Xboom_tank && j == 3 ? 4 :
9142 i == Xboom_tank && j == 4 ? 4 :
9143 i == Xboom_tank && j == 5 ? 2 :
9144 i == Xboom_tank && j == 6 ? 2 :
9145 i == Xboom_tank && j == 7 ? 0 :
9146 i == Xboom_android && j == 7 ? 6 :
9147 i == Xboom_1 && j == 1 ? 2 :
9148 i == Xboom_1 && j == 2 ? 2 :
9149 i == Xboom_1 && j == 3 ? 4 :
9150 i == Xboom_1 && j == 4 ? 4 :
9151 i == Xboom_1 && j == 5 ? 6 :
9152 i == Xboom_1 && j == 6 ? 6 :
9153 i == Xboom_1 && j == 7 ? 8 :
9154 i == Xboom_2 && j == 0 ? 8 :
9155 i == Xboom_2 && j == 1 ? 8 :
9156 i == Xboom_2 && j == 2 ? 10 :
9157 i == Xboom_2 && j == 3 ? 10 :
9158 i == Xboom_2 && j == 4 ? 10 :
9159 i == Xboom_2 && j == 5 ? 12 :
9160 i == Xboom_2 && j == 6 ? 12 :
9161 i == Xboom_2 && j == 7 ? 12 :
9162 special_animation && j == 4 ? 3 :
9163 effective_action != action ? 0 :
9165 int frame = getAnimationFrame(g->anim_frames,
9168 g->anim_start_frame,
9171 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9172 g->double_movement && is_backside);
9174 g_em->bitmap = src_bitmap;
9175 g_em->src_x = src_x;
9176 g_em->src_y = src_y;
9177 g_em->src_offset_x = 0;
9178 g_em->src_offset_y = 0;
9179 g_em->dst_offset_x = 0;
9180 g_em->dst_offset_y = 0;
9181 g_em->width = TILEX;
9182 g_em->height = TILEY;
9184 g_em->preserve_background = FALSE;
9186 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9189 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9190 effective_action == ACTION_MOVING ||
9191 effective_action == ACTION_PUSHING ||
9192 effective_action == ACTION_EATING)) ||
9193 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9194 effective_action == ACTION_EMPTYING)))
9197 (effective_action == ACTION_FALLING ||
9198 effective_action == ACTION_FILLING ||
9199 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9200 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9201 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9202 int num_steps = (i == Ydrip_1_s ? 16 :
9203 i == Ydrip_1_sB ? 16 :
9204 i == Ydrip_2_s ? 16 :
9205 i == Ydrip_2_sB ? 16 :
9206 i == Xsand_stonein_1 ? 32 :
9207 i == Xsand_stonein_2 ? 32 :
9208 i == Xsand_stonein_3 ? 32 :
9209 i == Xsand_stonein_4 ? 32 :
9210 i == Xsand_stoneout_1 ? 16 :
9211 i == Xsand_stoneout_2 ? 16 : 8);
9212 int cx = ABS(dx) * (TILEX / num_steps);
9213 int cy = ABS(dy) * (TILEY / num_steps);
9214 int step_frame = (i == Ydrip_2_s ? j + 8 :
9215 i == Ydrip_2_sB ? j + 8 :
9216 i == Xsand_stonein_2 ? j + 8 :
9217 i == Xsand_stonein_3 ? j + 16 :
9218 i == Xsand_stonein_4 ? j + 24 :
9219 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9220 int step = (is_backside ? step_frame : num_steps - step_frame);
9222 if (is_backside) // tile where movement starts
9224 if (dx < 0 || dy < 0)
9226 g_em->src_offset_x = cx * step;
9227 g_em->src_offset_y = cy * step;
9231 g_em->dst_offset_x = cx * step;
9232 g_em->dst_offset_y = cy * step;
9235 else // tile where movement ends
9237 if (dx < 0 || dy < 0)
9239 g_em->dst_offset_x = cx * step;
9240 g_em->dst_offset_y = cy * step;
9244 g_em->src_offset_x = cx * step;
9245 g_em->src_offset_y = cy * step;
9249 g_em->width = TILEX - cx * step;
9250 g_em->height = TILEY - cy * step;
9253 // create unique graphic identifier to decide if tile must be redrawn
9254 /* bit 31 - 16 (16 bit): EM style graphic
9255 bit 15 - 12 ( 4 bit): EM style frame
9256 bit 11 - 6 ( 6 bit): graphic width
9257 bit 5 - 0 ( 6 bit): graphic height */
9258 g_em->unique_identifier =
9259 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9263 for (i = 0; i < GAME_TILE_MAX; i++)
9265 for (j = 0; j < 8; j++)
9267 int element = object_mapping[i].element_rnd;
9268 int action = object_mapping[i].action;
9269 int direction = object_mapping[i].direction;
9270 boolean is_backside = object_mapping[i].is_backside;
9271 int graphic_action = el_act_dir2img(element, action, direction);
9272 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9274 if ((action == ACTION_SMASHED_BY_ROCK ||
9275 action == ACTION_SMASHED_BY_SPRING ||
9276 action == ACTION_EATING) &&
9277 graphic_action == graphic_default)
9279 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9280 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9281 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9282 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9285 // no separate animation for "smashed by rock" -- use rock instead
9286 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9287 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9289 g_em->bitmap = g_xx->bitmap;
9290 g_em->src_x = g_xx->src_x;
9291 g_em->src_y = g_xx->src_y;
9292 g_em->src_offset_x = g_xx->src_offset_x;
9293 g_em->src_offset_y = g_xx->src_offset_y;
9294 g_em->dst_offset_x = g_xx->dst_offset_x;
9295 g_em->dst_offset_y = g_xx->dst_offset_y;
9296 g_em->width = g_xx->width;
9297 g_em->height = g_xx->height;
9298 g_em->unique_identifier = g_xx->unique_identifier;
9301 g_em->preserve_background = TRUE;
9306 for (p = 0; p < MAX_PLAYERS; p++)
9308 for (i = 0; i < PLY_MAX; i++)
9310 int element = player_mapping[p][i].element_rnd;
9311 int action = player_mapping[p][i].action;
9312 int direction = player_mapping[p][i].direction;
9314 for (j = 0; j < 8; j++)
9316 int effective_element = element;
9317 int effective_action = action;
9318 int graphic = (direction == MV_NONE ?
9319 el_act2img(effective_element, effective_action) :
9320 el_act_dir2img(effective_element, effective_action,
9322 struct GraphicInfo *g = &graphic_info[graphic];
9323 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9327 int frame = getAnimationFrame(g->anim_frames,
9330 g->anim_start_frame,
9333 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9335 g_em->bitmap = src_bitmap;
9336 g_em->src_x = src_x;
9337 g_em->src_y = src_y;
9338 g_em->src_offset_x = 0;
9339 g_em->src_offset_y = 0;
9340 g_em->dst_offset_x = 0;
9341 g_em->dst_offset_y = 0;
9342 g_em->width = TILEX;
9343 g_em->height = TILEY;
9349 static void CheckSaveEngineSnapshot_EM(int frame,
9350 boolean any_player_moving,
9351 boolean any_player_snapping,
9352 boolean any_player_dropping)
9354 if (frame == 7 && !any_player_dropping)
9356 if (!local_player->was_waiting)
9358 if (!CheckSaveEngineSnapshotToList())
9361 local_player->was_waiting = TRUE;
9364 else if (any_player_moving || any_player_snapping || any_player_dropping)
9366 local_player->was_waiting = FALSE;
9370 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9371 boolean murphy_is_dropping)
9373 if (murphy_is_waiting)
9375 if (!local_player->was_waiting)
9377 if (!CheckSaveEngineSnapshotToList())
9380 local_player->was_waiting = TRUE;
9385 local_player->was_waiting = FALSE;
9389 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9390 boolean button_released)
9392 if (button_released)
9394 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9395 CheckSaveEngineSnapshotToList();
9397 else if (element_clicked)
9399 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9400 CheckSaveEngineSnapshotToList();
9402 game.snapshot.changed_action = TRUE;
9406 boolean CheckSingleStepMode_EM(int frame,
9407 boolean any_player_moving,
9408 boolean any_player_snapping,
9409 boolean any_player_dropping)
9411 if (tape.single_step && tape.recording && !tape.pausing)
9412 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9413 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9415 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9416 any_player_snapping, any_player_dropping);
9418 return tape.pausing;
9421 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9422 boolean murphy_is_dropping)
9424 boolean murphy_starts_dropping = FALSE;
9427 for (i = 0; i < MAX_PLAYERS; i++)
9428 if (stored_player[i].force_dropping)
9429 murphy_starts_dropping = TRUE;
9431 if (tape.single_step && tape.recording && !tape.pausing)
9432 if (murphy_is_waiting && !murphy_starts_dropping)
9433 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9435 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9438 void CheckSingleStepMode_MM(boolean element_clicked,
9439 boolean button_released)
9441 if (tape.single_step && tape.recording && !tape.pausing)
9442 if (button_released)
9443 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9445 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9448 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9449 int graphic, int sync_frame)
9451 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9453 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9456 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9458 return (IS_NEXT_FRAME(sync_frame, graphic));
9461 int getGraphicInfo_Delay(int graphic)
9463 return graphic_info[graphic].anim_delay;
9466 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9468 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9471 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9477 void PlayMenuSoundExt(int sound)
9479 if (sound == SND_UNDEFINED)
9482 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9483 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9486 if (IS_LOOP_SOUND(sound))
9487 PlaySoundLoop(sound);
9492 void PlayMenuSound(void)
9494 PlayMenuSoundExt(menu.sound[game_status]);
9497 void PlayMenuSoundStereo(int sound, int stereo_position)
9499 if (sound == SND_UNDEFINED)
9502 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9503 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9506 if (IS_LOOP_SOUND(sound))
9507 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9509 PlaySoundStereo(sound, stereo_position);
9512 void PlayMenuSoundIfLoopExt(int sound)
9514 if (sound == SND_UNDEFINED)
9517 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9518 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9521 if (IS_LOOP_SOUND(sound))
9522 PlaySoundLoop(sound);
9525 void PlayMenuSoundIfLoop(void)
9527 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9530 void PlayMenuMusicExt(int music)
9532 if (music == MUS_UNDEFINED)
9535 if (!setup.sound_music)
9538 if (IS_LOOP_MUSIC(music))
9539 PlayMusicLoop(music);
9544 void PlayMenuMusic(void)
9546 char *curr_music = getCurrentlyPlayingMusicFilename();
9547 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9549 if (!strEqual(curr_music, next_music))
9550 PlayMenuMusicExt(menu.music[game_status]);
9553 void PlayMenuSoundsAndMusic(void)
9559 static void FadeMenuSounds(void)
9564 static void FadeMenuMusic(void)
9566 char *curr_music = getCurrentlyPlayingMusicFilename();
9567 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9569 if (!strEqual(curr_music, next_music))
9573 void FadeMenuSoundsAndMusic(void)
9579 void PlaySoundActivating(void)
9582 PlaySound(SND_MENU_ITEM_ACTIVATING);
9586 void PlaySoundSelecting(void)
9589 PlaySound(SND_MENU_ITEM_SELECTING);
9593 void ToggleFullscreenIfNeeded(void)
9595 // if setup and video fullscreen state are already matching, nothing do do
9596 if (setup.fullscreen == video.fullscreen_enabled ||
9597 !video.fullscreen_available)
9600 SDLSetWindowFullscreen(setup.fullscreen);
9602 // set setup value according to successfully changed fullscreen mode
9603 setup.fullscreen = video.fullscreen_enabled;
9606 void ChangeWindowScalingIfNeeded(void)
9608 // if setup and video window scaling are already matching, nothing do do
9609 if (setup.window_scaling_percent == video.window_scaling_percent ||
9610 video.fullscreen_enabled)
9613 SDLSetWindowScaling(setup.window_scaling_percent);
9615 // set setup value according to successfully changed window scaling
9616 setup.window_scaling_percent = video.window_scaling_percent;
9619 void ChangeVsyncModeIfNeeded(void)
9621 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9622 int video_vsync_mode = video.vsync_mode;
9624 // if setup and video vsync mode are already matching, nothing do do
9625 if (setup_vsync_mode == video_vsync_mode)
9628 // if renderer is using OpenGL, vsync mode can directly be changed
9629 SDLSetScreenVsyncMode(setup.vsync_mode);
9631 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9632 if (video.vsync_mode == video_vsync_mode)
9634 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9636 // save backbuffer content which gets lost when re-creating screen
9637 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9639 // force re-creating screen and renderer to set new vsync mode
9640 video.fullscreen_enabled = !setup.fullscreen;
9642 // when creating new renderer, destroy textures linked to old renderer
9643 FreeAllImageTextures(); // needs old renderer to free the textures
9645 // re-create screen and renderer (including change of vsync mode)
9646 ChangeVideoModeIfNeeded(setup.fullscreen);
9648 // set setup value according to successfully changed fullscreen mode
9649 setup.fullscreen = video.fullscreen_enabled;
9651 // restore backbuffer content from temporary backbuffer backup bitmap
9652 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9653 FreeBitmap(tmp_backbuffer);
9655 // update visible window/screen
9656 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9658 // when changing vsync mode, re-create textures for new renderer
9659 InitImageTextures();
9662 // set setup value according to successfully changed vsync mode
9663 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9666 static void JoinRectangles(int *x, int *y, int *width, int *height,
9667 int x2, int y2, int width2, int height2)
9669 // do not join with "off-screen" rectangle
9670 if (x2 == -1 || y2 == -1)
9675 *width = MAX(*width, width2);
9676 *height = MAX(*height, height2);
9679 void SetAnimStatus(int anim_status_new)
9681 if (anim_status_new == GAME_MODE_MAIN)
9682 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9683 else if (anim_status_new == GAME_MODE_NAMES)
9684 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9685 else if (anim_status_new == GAME_MODE_SCORES)
9686 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9688 global.anim_status_next = anim_status_new;
9690 // directly set screen modes that are entered without fading
9691 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9692 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9693 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9694 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9695 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9696 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9697 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9698 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9699 global.anim_status = global.anim_status_next;
9702 void SetGameStatus(int game_status_new)
9704 if (game_status_new != game_status)
9705 game_status_last_screen = game_status;
9707 game_status = game_status_new;
9709 SetAnimStatus(game_status_new);
9712 void SetFontStatus(int game_status_new)
9714 static int last_game_status = -1;
9716 if (game_status_new != -1)
9718 // set game status for font use after storing last game status
9719 last_game_status = game_status;
9720 game_status = game_status_new;
9724 // reset game status after font use from last stored game status
9725 game_status = last_game_status;
9729 void ResetFontStatus(void)
9734 void SetLevelSetInfo(char *identifier, int level_nr)
9736 setString(&levelset.identifier, identifier);
9738 levelset.level_nr = level_nr;
9741 boolean CheckIfAllViewportsHaveChanged(void)
9743 // if game status has not changed, viewports have not changed either
9744 if (game_status == game_status_last)
9747 // check if all viewports have changed with current game status
9749 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9750 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9751 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9752 int new_real_sx = vp_playfield->x;
9753 int new_real_sy = vp_playfield->y;
9754 int new_full_sxsize = vp_playfield->width;
9755 int new_full_sysize = vp_playfield->height;
9756 int new_dx = vp_door_1->x;
9757 int new_dy = vp_door_1->y;
9758 int new_dxsize = vp_door_1->width;
9759 int new_dysize = vp_door_1->height;
9760 int new_vx = vp_door_2->x;
9761 int new_vy = vp_door_2->y;
9762 int new_vxsize = vp_door_2->width;
9763 int new_vysize = vp_door_2->height;
9765 boolean playfield_viewport_has_changed =
9766 (new_real_sx != REAL_SX ||
9767 new_real_sy != REAL_SY ||
9768 new_full_sxsize != FULL_SXSIZE ||
9769 new_full_sysize != FULL_SYSIZE);
9771 boolean door_1_viewport_has_changed =
9774 new_dxsize != DXSIZE ||
9775 new_dysize != DYSIZE);
9777 boolean door_2_viewport_has_changed =
9780 new_vxsize != VXSIZE ||
9781 new_vysize != VYSIZE ||
9782 game_status_last == GAME_MODE_EDITOR);
9784 return (playfield_viewport_has_changed &&
9785 door_1_viewport_has_changed &&
9786 door_2_viewport_has_changed);
9789 boolean CheckFadeAll(void)
9791 return (CheckIfGlobalBorderHasChanged() ||
9792 CheckIfAllViewportsHaveChanged());
9795 void ChangeViewportPropertiesIfNeeded(void)
9797 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9798 FALSE : setup.small_game_graphics);
9799 int gfx_game_mode = getGlobalGameStatus(game_status);
9800 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9802 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9803 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9804 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9805 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9806 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9807 int new_win_xsize = vp_window->width;
9808 int new_win_ysize = vp_window->height;
9809 int border_left = vp_playfield->border_left;
9810 int border_right = vp_playfield->border_right;
9811 int border_top = vp_playfield->border_top;
9812 int border_bottom = vp_playfield->border_bottom;
9813 int new_sx = vp_playfield->x + border_left;
9814 int new_sy = vp_playfield->y + border_top;
9815 int new_sxsize = vp_playfield->width - border_left - border_right;
9816 int new_sysize = vp_playfield->height - border_top - border_bottom;
9817 int new_real_sx = vp_playfield->x;
9818 int new_real_sy = vp_playfield->y;
9819 int new_full_sxsize = vp_playfield->width;
9820 int new_full_sysize = vp_playfield->height;
9821 int new_dx = vp_door_1->x;
9822 int new_dy = vp_door_1->y;
9823 int new_dxsize = vp_door_1->width;
9824 int new_dysize = vp_door_1->height;
9825 int new_vx = vp_door_2->x;
9826 int new_vy = vp_door_2->y;
9827 int new_vxsize = vp_door_2->width;
9828 int new_vysize = vp_door_2->height;
9829 int new_ex = vp_door_3->x;
9830 int new_ey = vp_door_3->y;
9831 int new_exsize = vp_door_3->width;
9832 int new_eysize = vp_door_3->height;
9833 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9834 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9835 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9836 int new_scr_fieldx = new_sxsize / tilesize;
9837 int new_scr_fieldy = new_sysize / tilesize;
9838 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9839 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9840 boolean init_gfx_buffers = FALSE;
9841 boolean init_video_buffer = FALSE;
9842 boolean init_gadgets_and_anims = FALSE;
9843 boolean init_em_graphics = FALSE;
9845 if (new_win_xsize != WIN_XSIZE ||
9846 new_win_ysize != WIN_YSIZE)
9848 WIN_XSIZE = new_win_xsize;
9849 WIN_YSIZE = new_win_ysize;
9851 init_video_buffer = TRUE;
9852 init_gfx_buffers = TRUE;
9853 init_gadgets_and_anims = TRUE;
9855 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9858 if (new_scr_fieldx != SCR_FIELDX ||
9859 new_scr_fieldy != SCR_FIELDY)
9861 // this always toggles between MAIN and GAME when using small tile size
9863 SCR_FIELDX = new_scr_fieldx;
9864 SCR_FIELDY = new_scr_fieldy;
9866 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9877 new_sxsize != SXSIZE ||
9878 new_sysize != SYSIZE ||
9879 new_dxsize != DXSIZE ||
9880 new_dysize != DYSIZE ||
9881 new_vxsize != VXSIZE ||
9882 new_vysize != VYSIZE ||
9883 new_exsize != EXSIZE ||
9884 new_eysize != EYSIZE ||
9885 new_real_sx != REAL_SX ||
9886 new_real_sy != REAL_SY ||
9887 new_full_sxsize != FULL_SXSIZE ||
9888 new_full_sysize != FULL_SYSIZE ||
9889 new_tilesize_var != TILESIZE_VAR
9892 // ------------------------------------------------------------------------
9893 // determine next fading area for changed viewport definitions
9894 // ------------------------------------------------------------------------
9896 // start with current playfield area (default fading area)
9899 FADE_SXSIZE = FULL_SXSIZE;
9900 FADE_SYSIZE = FULL_SYSIZE;
9902 // add new playfield area if position or size has changed
9903 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9904 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9906 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9907 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9910 // add current and new door 1 area if position or size has changed
9911 if (new_dx != DX || new_dy != DY ||
9912 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9914 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9915 DX, DY, DXSIZE, DYSIZE);
9916 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9917 new_dx, new_dy, new_dxsize, new_dysize);
9920 // add current and new door 2 area if position or size has changed
9921 if (new_vx != VX || new_vy != VY ||
9922 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9924 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9925 VX, VY, VXSIZE, VYSIZE);
9926 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9927 new_vx, new_vy, new_vxsize, new_vysize);
9930 // ------------------------------------------------------------------------
9931 // handle changed tile size
9932 // ------------------------------------------------------------------------
9934 if (new_tilesize_var != TILESIZE_VAR)
9936 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9938 // changing tile size invalidates scroll values of engine snapshots
9939 FreeEngineSnapshotSingle();
9941 // changing tile size requires update of graphic mapping for EM engine
9942 init_em_graphics = TRUE;
9953 SXSIZE = new_sxsize;
9954 SYSIZE = new_sysize;
9955 DXSIZE = new_dxsize;
9956 DYSIZE = new_dysize;
9957 VXSIZE = new_vxsize;
9958 VYSIZE = new_vysize;
9959 EXSIZE = new_exsize;
9960 EYSIZE = new_eysize;
9961 REAL_SX = new_real_sx;
9962 REAL_SY = new_real_sy;
9963 FULL_SXSIZE = new_full_sxsize;
9964 FULL_SYSIZE = new_full_sysize;
9965 TILESIZE_VAR = new_tilesize_var;
9967 init_gfx_buffers = TRUE;
9968 init_gadgets_and_anims = TRUE;
9970 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9971 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9974 if (init_gfx_buffers)
9976 // Debug("tools:viewport", "init_gfx_buffers");
9978 SCR_FIELDX = new_scr_fieldx_buffers;
9979 SCR_FIELDY = new_scr_fieldy_buffers;
9983 SCR_FIELDX = new_scr_fieldx;
9984 SCR_FIELDY = new_scr_fieldy;
9986 SetDrawDeactivationMask(REDRAW_NONE);
9987 SetDrawBackgroundMask(REDRAW_FIELD);
9990 if (init_video_buffer)
9992 // Debug("tools:viewport", "init_video_buffer");
9994 FreeAllImageTextures(); // needs old renderer to free the textures
9996 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9997 InitImageTextures();
10000 if (init_gadgets_and_anims)
10002 // Debug("tools:viewport", "init_gadgets_and_anims");
10005 InitGlobalAnimations();
10008 if (init_em_graphics)
10010 InitGraphicInfo_EM();
10014 void OpenURL(char *url)
10016 #if SDL_VERSION_ATLEAST(2,0,14)
10019 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
10020 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
10021 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
10025 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
10027 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
10031 // ============================================================================
10033 // ============================================================================
10035 #if defined(PLATFORM_WINDOWS)
10036 /* FILETIME of Jan 1 1970 00:00:00. */
10037 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
10040 * timezone information is stored outside the kernel so tzp isn't used anymore.
10042 * Note: this function is not for Win32 high precision timing purpose. See
10045 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
10047 FILETIME file_time;
10048 SYSTEMTIME system_time;
10049 ULARGE_INTEGER ularge;
10051 GetSystemTime(&system_time);
10052 SystemTimeToFileTime(&system_time, &file_time);
10053 ularge.LowPart = file_time.dwLowDateTime;
10054 ularge.HighPart = file_time.dwHighDateTime;
10056 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10057 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10063 static char *test_init_uuid_random_function_simple(void)
10065 static char seed_text[100];
10066 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10068 sprintf(seed_text, "%d", seed);
10073 static char *test_init_uuid_random_function_better(void)
10075 static char seed_text[100];
10076 struct timeval current_time;
10078 gettimeofday(¤t_time, NULL);
10080 prng_seed_bytes(¤t_time, sizeof(current_time));
10082 sprintf(seed_text, "%ld.%ld",
10083 (long)current_time.tv_sec,
10084 (long)current_time.tv_usec);
10089 #if defined(PLATFORM_WINDOWS)
10090 static char *test_init_uuid_random_function_better_windows(void)
10092 static char seed_text[100];
10093 struct timeval current_time;
10095 gettimeofday_windows(¤t_time, NULL);
10097 prng_seed_bytes(¤t_time, sizeof(current_time));
10099 sprintf(seed_text, "%ld.%ld",
10100 (long)current_time.tv_sec,
10101 (long)current_time.tv_usec);
10107 static unsigned int test_uuid_random_function_simple(int max)
10109 return GetSimpleRandom(max);
10112 static unsigned int test_uuid_random_function_better(int max)
10114 return (max > 0 ? prng_get_uint() % max : 0);
10117 #if defined(PLATFORM_WINDOWS)
10118 #define NUM_UUID_TESTS 3
10120 #define NUM_UUID_TESTS 2
10123 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10125 struct hashtable *hash_seeds =
10126 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10127 struct hashtable *hash_uuids =
10128 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10129 static char message[100];
10132 char *random_name = (nr == 0 ? "simple" : "better");
10133 char *random_type = (always_seed ? "always" : "only once");
10134 char *(*init_random_function)(void) =
10136 test_init_uuid_random_function_simple :
10137 test_init_uuid_random_function_better);
10138 unsigned int (*random_function)(int) =
10140 test_uuid_random_function_simple :
10141 test_uuid_random_function_better);
10144 #if defined(PLATFORM_WINDOWS)
10147 random_name = "windows";
10148 init_random_function = test_init_uuid_random_function_better_windows;
10154 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10155 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10157 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10158 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10159 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10161 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10165 // always initialize random number generator at least once
10166 init_random_function();
10168 unsigned int time_start = SDL_GetTicks();
10170 for (i = 0; i < num_uuids; i++)
10174 char *seed = getStringCopy(init_random_function());
10176 hashtable_remove(hash_seeds, seed);
10177 hashtable_insert(hash_seeds, seed, "1");
10180 char *uuid = getStringCopy(getUUIDExt(random_function));
10182 hashtable_remove(hash_uuids, uuid);
10183 hashtable_insert(hash_uuids, uuid, "1");
10186 int num_unique_seeds = hashtable_count(hash_seeds);
10187 int num_unique_uuids = hashtable_count(hash_uuids);
10189 unsigned int time_needed = SDL_GetTicks() - time_start;
10191 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10193 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10196 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10198 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10199 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10201 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10203 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10205 Request(message, REQ_CONFIRM);
10207 hashtable_destroy(hash_seeds, 0);
10208 hashtable_destroy(hash_uuids, 0);
10211 void TestGeneratingUUIDs(void)
10213 int num_uuids = 1000000;
10216 for (i = 0; i < NUM_UUID_TESTS; i++)
10217 for (j = 0; j < 2; j++)
10218 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10220 CloseAllAndExit(0);