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_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
3259 int delay_value_normal = request.step_delay;
3260 int delay_value_fast = delay_value_normal / 2;
3261 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3262 boolean no_delay = (tape.warp_forward);
3263 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3264 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3265 DelayCounter anim_delay = { anim_delay_value };
3267 int tile_size = MAX(request.step_offset, 1);
3268 int max_xsize = request.width / tile_size;
3269 int max_ysize = request.height / tile_size;
3270 int max_xsize_inner = max_xsize - 2;
3271 int max_ysize_inner = max_ysize - 2;
3273 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3274 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3275 int xend = max_xsize_inner;
3276 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3277 int xstep = (xstart < xend ? 1 : 0);
3278 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3280 int end = MAX(xend - xstart, yend - ystart);
3283 if (setup.quick_doors)
3290 for (i = start; i <= end; i++)
3292 int last_frame = end; // last frame of this "for" loop
3293 int x = xstart + i * xstep;
3294 int y = ystart + i * ystep;
3295 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3296 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3297 int xsize_size_left = (xsize - 1) * tile_size;
3298 int ysize_size_top = (ysize - 1) * tile_size;
3299 int max_xsize_pos = (max_xsize - 1) * tile_size;
3300 int max_ysize_pos = (max_ysize - 1) * tile_size;
3301 int width = xsize * tile_size;
3302 int height = ysize * tile_size;
3308 HandleGameActions();
3310 setRequestPosition(&src_x, &src_y, FALSE);
3311 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3313 for (yy = 0; yy < 2; yy++)
3315 for (xx = 0; xx < 2; xx++)
3317 int src_xx = src_x + xx * max_xsize_pos;
3318 int src_yy = src_y + yy * max_ysize_pos;
3319 int dst_xx = dst_x + xx * xsize_size_left;
3320 int dst_yy = dst_y + yy * ysize_size_top;
3321 int xx_size = (xx ? tile_size : xsize_size_left);
3322 int yy_size = (yy ? tile_size : ysize_size_top);
3324 // draw partial (animated) envelope request to temporary bitmap
3325 BlitBitmap(bitmap_db_store_1, bitmap_db_store_2,
3326 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3330 // prepare partial (animated) envelope request from temporary bitmap
3331 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, dst_x, dst_y,
3334 redraw_mask |= REDRAW_FIELD;
3338 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3341 ClearAutoRepeatKeyEvents();
3344 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3346 int graphic = IMG_BACKGROUND_REQUEST;
3347 int sound_opening = SND_REQUEST_OPENING;
3348 int sound_closing = SND_REQUEST_CLOSING;
3349 int anim_mode_1 = request.anim_mode; // (higher priority)
3350 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3351 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3352 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3353 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3355 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3357 if (action == ACTION_OPENING)
3359 DrawEnvelopeRequest(text, req_state);
3361 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3363 if (anim_mode == ANIM_DEFAULT)
3364 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3366 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3370 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3372 if (anim_mode != ANIM_NONE)
3373 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3375 if (anim_mode == ANIM_DEFAULT)
3376 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3379 game.envelope_active = FALSE;
3382 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3384 if (IS_MM_WALL(element))
3386 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3392 int graphic = el2preimg(element);
3394 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3395 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3400 void DrawLevel(int draw_background_mask)
3404 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3405 SetDrawBackgroundMask(draw_background_mask);
3409 for (x = BX1; x <= BX2; x++)
3410 for (y = BY1; y <= BY2; y++)
3411 DrawScreenField(x, y);
3413 redraw_mask |= REDRAW_FIELD;
3416 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3421 for (x = 0; x < size_x; x++)
3422 for (y = 0; y < size_y; y++)
3423 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3425 redraw_mask |= REDRAW_FIELD;
3428 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3432 for (x = 0; x < size_x; x++)
3433 for (y = 0; y < size_y; y++)
3434 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3436 redraw_mask |= REDRAW_FIELD;
3439 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3441 boolean show_level_border = (BorderElement != EL_EMPTY);
3442 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3443 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3444 int tile_size = preview.tile_size;
3445 int preview_width = preview.xsize * tile_size;
3446 int preview_height = preview.ysize * tile_size;
3447 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3448 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3449 int real_preview_width = real_preview_xsize * tile_size;
3450 int real_preview_height = real_preview_ysize * tile_size;
3451 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3452 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3455 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3458 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3460 dst_x += (preview_width - real_preview_width) / 2;
3461 dst_y += (preview_height - real_preview_height) / 2;
3463 for (x = 0; x < real_preview_xsize; x++)
3465 for (y = 0; y < real_preview_ysize; y++)
3467 int lx = from_x + x + (show_level_border ? -1 : 0);
3468 int ly = from_y + y + (show_level_border ? -1 : 0);
3469 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3470 getBorderElement(lx, ly));
3472 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3473 element, tile_size);
3477 redraw_mask |= REDRAW_FIELD;
3480 #define MICROLABEL_EMPTY 0
3481 #define MICROLABEL_LEVEL_NAME 1
3482 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3483 #define MICROLABEL_LEVEL_AUTHOR 3
3484 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3485 #define MICROLABEL_IMPORTED_FROM 5
3486 #define MICROLABEL_IMPORTED_BY_HEAD 6
3487 #define MICROLABEL_IMPORTED_BY 7
3489 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3491 int max_text_width = SXSIZE;
3492 int font_width = getFontWidth(font_nr);
3494 if (pos->align == ALIGN_CENTER)
3495 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3496 else if (pos->align == ALIGN_RIGHT)
3497 max_text_width = pos->x;
3499 max_text_width = SXSIZE - pos->x;
3501 return max_text_width / font_width;
3504 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3506 char label_text[MAX_OUTPUT_LINESIZE + 1];
3507 int max_len_label_text;
3508 int font_nr = pos->font;
3511 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3514 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3515 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3516 mode == MICROLABEL_IMPORTED_BY_HEAD)
3517 font_nr = pos->font_alt;
3519 max_len_label_text = getMaxTextLength(pos, font_nr);
3521 if (pos->size != -1)
3522 max_len_label_text = pos->size;
3524 for (i = 0; i < max_len_label_text; i++)
3525 label_text[i] = ' ';
3526 label_text[max_len_label_text] = '\0';
3528 if (strlen(label_text) > 0)
3529 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3532 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3533 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3534 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3535 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3536 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3537 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3538 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3539 max_len_label_text);
3540 label_text[max_len_label_text] = '\0';
3542 if (strlen(label_text) > 0)
3543 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3545 redraw_mask |= REDRAW_FIELD;
3548 static void DrawPreviewLevelLabel(int mode)
3550 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3553 static void DrawPreviewLevelInfo(int mode)
3555 if (mode == MICROLABEL_LEVEL_NAME)
3556 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3557 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3558 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3561 static void DrawPreviewLevelExt(boolean restart)
3563 static DelayCounter scroll_delay = { 0 };
3564 static DelayCounter label_delay = { 0 };
3565 static int from_x, from_y, scroll_direction;
3566 static int label_state, label_counter;
3567 boolean show_level_border = (BorderElement != EL_EMPTY);
3568 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3569 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3571 scroll_delay.value = preview.step_delay;
3572 label_delay.value = MICROLEVEL_LABEL_DELAY;
3579 if (preview.anim_mode == ANIM_CENTERED)
3581 if (level_xsize > preview.xsize)
3582 from_x = (level_xsize - preview.xsize) / 2;
3583 if (level_ysize > preview.ysize)
3584 from_y = (level_ysize - preview.ysize) / 2;
3587 from_x += preview.xoffset;
3588 from_y += preview.yoffset;
3590 scroll_direction = MV_RIGHT;
3594 DrawPreviewLevelPlayfield(from_x, from_y);
3595 DrawPreviewLevelLabel(label_state);
3597 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3598 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3600 // initialize delay counters
3601 ResetDelayCounter(&scroll_delay);
3602 ResetDelayCounter(&label_delay);
3604 if (leveldir_current->name)
3606 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3607 char label_text[MAX_OUTPUT_LINESIZE + 1];
3608 int font_nr = pos->font;
3609 int max_len_label_text = getMaxTextLength(pos, font_nr);
3611 if (pos->size != -1)
3612 max_len_label_text = pos->size;
3614 strncpy(label_text, leveldir_current->name, max_len_label_text);
3615 label_text[max_len_label_text] = '\0';
3617 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3618 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3624 // scroll preview level, if needed
3625 if (preview.anim_mode != ANIM_NONE &&
3626 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3627 DelayReached(&scroll_delay))
3629 switch (scroll_direction)
3634 from_x -= preview.step_offset;
3635 from_x = (from_x < 0 ? 0 : from_x);
3638 scroll_direction = MV_UP;
3642 if (from_x < level_xsize - preview.xsize)
3644 from_x += preview.step_offset;
3645 from_x = (from_x > level_xsize - preview.xsize ?
3646 level_xsize - preview.xsize : from_x);
3649 scroll_direction = MV_DOWN;
3655 from_y -= preview.step_offset;
3656 from_y = (from_y < 0 ? 0 : from_y);
3659 scroll_direction = MV_RIGHT;
3663 if (from_y < level_ysize - preview.ysize)
3665 from_y += preview.step_offset;
3666 from_y = (from_y > level_ysize - preview.ysize ?
3667 level_ysize - preview.ysize : from_y);
3670 scroll_direction = MV_LEFT;
3677 DrawPreviewLevelPlayfield(from_x, from_y);
3680 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3681 // redraw micro level label, if needed
3682 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3683 !strEqual(level.author, ANONYMOUS_NAME) &&
3684 !strEqual(level.author, leveldir_current->name) &&
3685 DelayReached(&label_delay))
3687 int max_label_counter = 23;
3689 if (leveldir_current->imported_from != NULL &&
3690 strlen(leveldir_current->imported_from) > 0)
3691 max_label_counter += 14;
3692 if (leveldir_current->imported_by != NULL &&
3693 strlen(leveldir_current->imported_by) > 0)
3694 max_label_counter += 14;
3696 label_counter = (label_counter + 1) % max_label_counter;
3697 label_state = (label_counter >= 0 && label_counter <= 7 ?
3698 MICROLABEL_LEVEL_NAME :
3699 label_counter >= 9 && label_counter <= 12 ?
3700 MICROLABEL_LEVEL_AUTHOR_HEAD :
3701 label_counter >= 14 && label_counter <= 21 ?
3702 MICROLABEL_LEVEL_AUTHOR :
3703 label_counter >= 23 && label_counter <= 26 ?
3704 MICROLABEL_IMPORTED_FROM_HEAD :
3705 label_counter >= 28 && label_counter <= 35 ?
3706 MICROLABEL_IMPORTED_FROM :
3707 label_counter >= 37 && label_counter <= 40 ?
3708 MICROLABEL_IMPORTED_BY_HEAD :
3709 label_counter >= 42 && label_counter <= 49 ?
3710 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3712 if (leveldir_current->imported_from == NULL &&
3713 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3714 label_state == MICROLABEL_IMPORTED_FROM))
3715 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3716 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3718 DrawPreviewLevelLabel(label_state);
3722 void DrawPreviewPlayers(void)
3724 if (game_status != GAME_MODE_MAIN)
3727 // do not draw preview players if level preview redefined, but players aren't
3728 if (preview.redefined && !menu.main.preview_players.redefined)
3731 boolean player_found[MAX_PLAYERS];
3732 int num_players = 0;
3735 for (i = 0; i < MAX_PLAYERS; i++)
3736 player_found[i] = FALSE;
3738 // check which players can be found in the level (simple approach)
3739 for (x = 0; x < lev_fieldx; x++)
3741 for (y = 0; y < lev_fieldy; y++)
3743 int element = level.field[x][y];
3745 if (IS_PLAYER_ELEMENT(element))
3747 int player_nr = GET_PLAYER_NR(element);
3749 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3751 if (!player_found[player_nr])
3754 player_found[player_nr] = TRUE;
3759 struct TextPosInfo *pos = &menu.main.preview_players;
3760 int tile_size = pos->tile_size;
3761 int border_size = pos->border_size;
3762 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3763 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3764 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3765 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3766 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3767 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3768 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3769 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3770 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3771 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3772 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3773 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3775 // clear area in which the players will be drawn
3776 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3777 max_players_width, max_players_height);
3779 if (!network.enabled && !setup.team_mode)
3782 // only draw players if level is suited for team mode
3783 if (num_players < 2)
3786 // draw all players that were found in the level
3787 for (i = 0; i < MAX_PLAYERS; i++)
3789 if (player_found[i])
3791 int graphic = el2img(EL_PLAYER_1 + i);
3793 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3795 xpos += player_xoffset;
3796 ypos += player_yoffset;
3801 void DrawPreviewLevelInitial(void)
3803 DrawPreviewLevelExt(TRUE);
3804 DrawPreviewPlayers();
3807 void DrawPreviewLevelAnimation(void)
3809 DrawPreviewLevelExt(FALSE);
3812 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3813 int border_size, int font_nr)
3815 int graphic = el2img(EL_PLAYER_1 + player_nr);
3816 int font_height = getFontHeight(font_nr);
3817 int player_height = MAX(tile_size, font_height);
3818 int xoffset_text = tile_size + border_size;
3819 int yoffset_text = (player_height - font_height) / 2;
3820 int yoffset_graphic = (player_height - tile_size) / 2;
3821 char *player_name = getNetworkPlayerName(player_nr + 1);
3823 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3825 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3828 static void DrawNetworkPlayersExt(boolean force)
3830 if (game_status != GAME_MODE_MAIN)
3833 if (!network.connected && !force)
3836 // do not draw network players if level preview redefined, but players aren't
3837 if (preview.redefined && !menu.main.network_players.redefined)
3840 int num_players = 0;
3843 for (i = 0; i < MAX_PLAYERS; i++)
3844 if (stored_player[i].connected_network)
3847 struct TextPosInfo *pos = &menu.main.network_players;
3848 int tile_size = pos->tile_size;
3849 int border_size = pos->border_size;
3850 int xoffset_text = tile_size + border_size;
3851 int font_nr = pos->font;
3852 int font_width = getFontWidth(font_nr);
3853 int font_height = getFontHeight(font_nr);
3854 int player_height = MAX(tile_size, font_height);
3855 int player_yoffset = player_height + border_size;
3856 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3857 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3858 int all_players_height = num_players * player_yoffset - border_size;
3859 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3860 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3861 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3863 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3864 max_players_width, max_players_height);
3866 // first draw local network player ...
3867 for (i = 0; i < MAX_PLAYERS; i++)
3869 if (stored_player[i].connected_network &&
3870 stored_player[i].connected_locally)
3872 char *player_name = getNetworkPlayerName(i + 1);
3873 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3874 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3876 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3878 ypos += player_yoffset;
3882 // ... then draw all other network players
3883 for (i = 0; i < MAX_PLAYERS; i++)
3885 if (stored_player[i].connected_network &&
3886 !stored_player[i].connected_locally)
3888 char *player_name = getNetworkPlayerName(i + 1);
3889 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3890 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3892 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3894 ypos += player_yoffset;
3899 void DrawNetworkPlayers(void)
3901 DrawNetworkPlayersExt(FALSE);
3904 void ClearNetworkPlayers(void)
3906 DrawNetworkPlayersExt(TRUE);
3909 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3910 int graphic, int lx, int ly,
3913 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3915 if (mask_mode == USE_MASKING)
3916 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3918 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3921 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3922 int graphic, int sync_frame, int mask_mode)
3924 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3926 if (mask_mode == USE_MASKING)
3927 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3929 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3932 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3933 int graphic, int sync_frame, int tilesize,
3936 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3938 if (mask_mode == USE_MASKING)
3939 DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
3941 DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
3944 static void DrawGraphicAnimation(int x, int y, int graphic)
3946 int lx = LEVELX(x), ly = LEVELY(y);
3947 int mask_mode = NO_MASKING;
3949 if (!IN_SCR_FIELD(x, y))
3952 if (game.use_masked_elements)
3954 if (Tile[lx][ly] != EL_EMPTY)
3956 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3958 mask_mode = USE_MASKING;
3962 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3963 graphic, lx, ly, mask_mode);
3965 MarkTileDirty(x, y);
3968 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3970 int lx = LEVELX(x), ly = LEVELY(y);
3971 int mask_mode = NO_MASKING;
3973 if (!IN_SCR_FIELD(x, y))
3976 if (game.use_masked_elements)
3978 if (Tile[lx][ly] != EL_EMPTY)
3980 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3982 mask_mode = USE_MASKING;
3986 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3987 graphic, lx, ly, mask_mode);
3989 MarkTileDirty(x, y);
3992 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3994 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3997 void DrawLevelElementAnimation(int x, int y, int element)
3999 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4001 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4004 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
4006 int sx = SCREENX(x), sy = SCREENY(y);
4008 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4011 if (Tile[x][y] == EL_EMPTY)
4012 graphic = el2img(GfxElementEmpty[x][y]);
4014 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4017 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4020 DrawGraphicAnimation(sx, sy, graphic);
4023 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4024 DrawLevelFieldCrumbled(x, y);
4026 if (GFX_CRUMBLED(Tile[x][y]))
4027 DrawLevelFieldCrumbled(x, y);
4031 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4033 int sx = SCREENX(x), sy = SCREENY(y);
4036 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4039 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4041 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4044 DrawGraphicAnimation(sx, sy, graphic);
4046 if (GFX_CRUMBLED(element))
4047 DrawLevelFieldCrumbled(x, y);
4050 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4052 if (player->use_murphy)
4054 // this works only because currently only one player can be "murphy" ...
4055 static int last_horizontal_dir = MV_LEFT;
4056 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4058 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4059 last_horizontal_dir = move_dir;
4061 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4063 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4065 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4071 return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4074 static boolean equalGraphics(int graphic1, int graphic2)
4076 struct GraphicInfo *g1 = &graphic_info[graphic1];
4077 struct GraphicInfo *g2 = &graphic_info[graphic2];
4079 return (g1->bitmap == g2->bitmap &&
4080 g1->src_x == g2->src_x &&
4081 g1->src_y == g2->src_y &&
4082 g1->anim_frames == g2->anim_frames &&
4083 g1->anim_delay == g2->anim_delay &&
4084 g1->anim_mode == g2->anim_mode);
4087 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4091 DRAW_PLAYER_STAGE_INIT = 0,
4092 DRAW_PLAYER_STAGE_LAST_FIELD,
4093 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4094 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4095 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4096 DRAW_PLAYER_STAGE_PLAYER,
4098 DRAW_PLAYER_STAGE_PLAYER,
4099 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4101 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4102 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4104 NUM_DRAW_PLAYER_STAGES
4107 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4109 static int static_last_player_graphic[MAX_PLAYERS];
4110 static int static_last_player_frame[MAX_PLAYERS];
4111 static boolean static_player_is_opaque[MAX_PLAYERS];
4112 static boolean draw_player[MAX_PLAYERS];
4113 int pnr = player->index_nr;
4115 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4117 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4118 static_last_player_frame[pnr] = player->Frame;
4119 static_player_is_opaque[pnr] = FALSE;
4121 draw_player[pnr] = TRUE;
4124 if (!draw_player[pnr])
4128 if (!IN_LEV_FIELD(player->jx, player->jy))
4130 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4131 Debug("draw:DrawPlayerExt", "This should never happen!");
4133 draw_player[pnr] = FALSE;
4139 int last_player_graphic = static_last_player_graphic[pnr];
4140 int last_player_frame = static_last_player_frame[pnr];
4141 boolean player_is_opaque = static_player_is_opaque[pnr];
4143 int jx = player->jx;
4144 int jy = player->jy;
4145 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4146 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4147 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4148 int last_jx = (player->is_moving ? jx - dx : jx);
4149 int last_jy = (player->is_moving ? jy - dy : jy);
4150 int next_jx = jx + dx;
4151 int next_jy = jy + dy;
4152 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4153 int sx = SCREENX(jx);
4154 int sy = SCREENY(jy);
4155 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4156 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4157 int element = Tile[jx][jy];
4158 int last_element = Tile[last_jx][last_jy];
4159 int action = (player->is_pushing ? ACTION_PUSHING :
4160 player->is_digging ? ACTION_DIGGING :
4161 player->is_collecting ? ACTION_COLLECTING :
4162 player->is_moving ? ACTION_MOVING :
4163 player->is_snapping ? ACTION_SNAPPING :
4164 player->is_dropping ? ACTION_DROPPING :
4165 player->is_waiting ? player->action_waiting :
4168 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4170 // ------------------------------------------------------------------------
4171 // initialize drawing the player
4172 // ------------------------------------------------------------------------
4174 draw_player[pnr] = FALSE;
4176 // GfxElement[][] is set to the element the player is digging or collecting;
4177 // remove also for off-screen player if the player is not moving anymore
4178 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4179 GfxElement[jx][jy] = EL_UNDEFINED;
4181 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4184 if (element == EL_EXPLOSION)
4187 InitPlayerGfxAnimation(player, action, move_dir);
4189 draw_player[pnr] = TRUE;
4191 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4193 // ------------------------------------------------------------------------
4194 // draw things in the field the player is leaving, if needed
4195 // ------------------------------------------------------------------------
4197 if (!IN_SCR_FIELD(sx, sy))
4198 draw_player[pnr] = FALSE;
4200 if (!player->is_moving)
4203 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4205 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4207 if (last_element == EL_DYNAMITE_ACTIVE ||
4208 last_element == EL_EM_DYNAMITE_ACTIVE ||
4209 last_element == EL_SP_DISK_RED_ACTIVE)
4210 DrawDynamite(last_jx, last_jy);
4212 DrawLevelFieldThruMask(last_jx, last_jy);
4214 else if (last_element == EL_DYNAMITE_ACTIVE ||
4215 last_element == EL_EM_DYNAMITE_ACTIVE ||
4216 last_element == EL_SP_DISK_RED_ACTIVE)
4217 DrawDynamite(last_jx, last_jy);
4219 DrawLevelField(last_jx, last_jy);
4221 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4223 // ------------------------------------------------------------------------
4224 // draw things behind the player, if needed
4225 // ------------------------------------------------------------------------
4229 DrawLevelElement(jx, jy, Back[jx][jy]);
4234 if (IS_ACTIVE_BOMB(element))
4236 DrawLevelElement(jx, jy, EL_EMPTY);
4241 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4243 int old_element = GfxElement[jx][jy];
4244 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4245 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4247 if (GFX_CRUMBLED(old_element))
4248 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4250 DrawScreenGraphic(sx, sy, old_graphic, frame);
4252 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4253 static_player_is_opaque[pnr] = TRUE;
4257 GfxElement[jx][jy] = EL_UNDEFINED;
4259 // make sure that pushed elements are drawn with correct frame rate
4260 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4262 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4263 GfxFrame[jx][jy] = player->StepFrame;
4265 DrawLevelField(jx, jy);
4268 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4270 // ------------------------------------------------------------------------
4271 // draw things the player is pushing, if needed
4272 // ------------------------------------------------------------------------
4274 if (!player->is_pushing || !player->is_moving)
4277 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4280 int gfx_frame = GfxFrame[jx][jy];
4282 if (!IS_MOVING(jx, jy)) // push movement already finished
4284 element = Tile[next_jx][next_jy];
4285 gfx_frame = GfxFrame[next_jx][next_jy];
4288 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4289 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4290 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4292 // draw background element under pushed element (like the Sokoban field)
4293 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4295 // this allows transparent pushing animation over non-black background
4298 DrawLevelElement(jx, jy, Back[jx][jy]);
4300 DrawLevelElement(jx, jy, EL_EMPTY);
4303 if (Back[next_jx][next_jy])
4304 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4306 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4308 int px = SCREENX(jx), py = SCREENY(jy);
4309 int pxx = (TILEX - ABS(sxx)) * dx;
4310 int pyy = (TILEY - ABS(syy)) * dy;
4313 // do not draw (EM style) pushing animation when pushing is finished
4314 // (two-tile animations usually do not contain start and end frame)
4315 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4316 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4318 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4320 // masked drawing is needed for EMC style (double) movement graphics
4321 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4322 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4325 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4327 // ------------------------------------------------------------------------
4328 // draw player himself
4329 // ------------------------------------------------------------------------
4331 int graphic = getPlayerGraphic(player, move_dir);
4333 // in the case of changed player action or direction, prevent the current
4334 // animation frame from being restarted for identical animations
4335 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4336 player->Frame = last_player_frame;
4338 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4340 if (player_is_opaque)
4341 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4343 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4345 if (SHIELD_ON(player))
4347 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4348 IMG_SHIELD_NORMAL_ACTIVE);
4349 frame = getGraphicAnimationFrame(graphic, -1);
4351 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4354 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4356 // ------------------------------------------------------------------------
4357 // draw things in front of player (active dynamite or dynabombs)
4358 // ------------------------------------------------------------------------
4360 if (IS_ACTIVE_BOMB(element))
4362 int graphic = el2img(element);
4363 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4365 if (game.emulation == EMU_SUPAPLEX)
4366 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4368 DrawGraphicThruMask(sx, sy, graphic, frame);
4371 if (player_is_moving && last_element == EL_EXPLOSION)
4373 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4374 GfxElement[last_jx][last_jy] : EL_EMPTY);
4375 int graphic = el_act2img(element, ACTION_EXPLODING);
4376 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4377 int phase = ExplodePhase[last_jx][last_jy] - 1;
4378 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4381 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4384 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4386 // ------------------------------------------------------------------------
4387 // draw elements the player is just walking/passing through/under
4388 // ------------------------------------------------------------------------
4390 if (player_is_moving)
4392 // handle the field the player is leaving ...
4393 if (IS_ACCESSIBLE_INSIDE(last_element))
4394 DrawLevelField(last_jx, last_jy);
4395 else if (IS_ACCESSIBLE_UNDER(last_element))
4396 DrawLevelFieldThruMask(last_jx, last_jy);
4399 // do not redraw accessible elements if the player is just pushing them
4400 if (!player_is_moving || !player->is_pushing)
4402 // ... and the field the player is entering
4403 if (IS_ACCESSIBLE_INSIDE(element))
4404 DrawLevelField(jx, jy);
4405 else if (IS_ACCESSIBLE_UNDER(element))
4406 DrawLevelFieldThruMask(jx, jy);
4409 MarkTileDirty(sx, sy);
4413 void DrawPlayer(struct PlayerInfo *player)
4417 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4418 DrawPlayerExt(player, i);
4421 void DrawAllPlayers(void)
4425 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4426 for (j = 0; j < MAX_PLAYERS; j++)
4427 if (stored_player[j].active)
4428 DrawPlayerExt(&stored_player[j], i);
4431 void DrawPlayerField(int x, int y)
4433 if (!IS_PLAYER(x, y))
4436 DrawPlayer(PLAYERINFO(x, y));
4439 // ----------------------------------------------------------------------------
4441 void WaitForEventToContinue(void)
4443 boolean first_wait = TRUE;
4444 boolean still_wait = TRUE;
4446 if (program.headless)
4449 // simulate releasing mouse button over last gadget, if still pressed
4451 HandleGadgets(-1, -1, 0);
4453 button_status = MB_RELEASED;
4456 ClearPlayerAction();
4462 if (NextValidEvent(&event))
4466 case EVENT_BUTTONPRESS:
4467 case EVENT_FINGERPRESS:
4471 case EVENT_BUTTONRELEASE:
4472 case EVENT_FINGERRELEASE:
4473 still_wait = first_wait;
4476 case EVENT_KEYPRESS:
4477 case SDL_CONTROLLERBUTTONDOWN:
4478 case SDL_JOYBUTTONDOWN:
4483 HandleOtherEvents(&event);
4487 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4492 if (!PendingEvent())
4497 #define MAX_REQUEST_LINES 13
4498 #define MAX_REQUEST_LINE_FONT1_LEN 7
4499 #define MAX_REQUEST_LINE_FONT2_LEN 10
4501 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4503 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4504 int draw_buffer_last = GetDrawtoField();
4505 int width = request.width;
4506 int height = request.height;
4510 setRequestPosition(&sx, &sy, FALSE);
4512 button_status = MB_RELEASED;
4514 request_gadget_id = -1;
4521 SetDrawtoField(draw_buffer_game);
4523 HandleGameActions();
4525 SetDrawtoField(DRAW_TO_BACKBUFFER);
4532 while (NextValidEvent(&event))
4536 case EVENT_BUTTONPRESS:
4537 case EVENT_BUTTONRELEASE:
4538 case EVENT_MOTIONNOTIFY:
4540 DrawBuffer *drawto_last = drawto;
4543 if (event.type == EVENT_MOTIONNOTIFY)
4548 motion_status = TRUE;
4549 mx = ((MotionEvent *) &event)->x;
4550 my = ((MotionEvent *) &event)->y;
4554 motion_status = FALSE;
4555 mx = ((ButtonEvent *) &event)->x;
4556 my = ((ButtonEvent *) &event)->y;
4557 if (event.type == EVENT_BUTTONPRESS)
4558 button_status = ((ButtonEvent *) &event)->button;
4560 button_status = MB_RELEASED;
4563 if (global.use_envelope_request)
4565 // draw changed button states to temporary bitmap
4566 drawto = bitmap_db_store_1;
4569 // this sets 'request_gadget_id'
4570 HandleGadgets(mx, my, button_status);
4572 if (global.use_envelope_request)
4574 // restore pointer to drawing buffer
4575 drawto = drawto_last;
4577 // prepare complete envelope request from temporary bitmap
4578 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy,
4582 switch (request_gadget_id)
4584 case TOOL_CTRL_ID_YES:
4585 case TOOL_CTRL_ID_TOUCH_YES:
4588 case TOOL_CTRL_ID_NO:
4589 case TOOL_CTRL_ID_TOUCH_NO:
4592 case TOOL_CTRL_ID_CONFIRM:
4593 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4594 result = TRUE | FALSE;
4597 case TOOL_CTRL_ID_PLAYER_1:
4600 case TOOL_CTRL_ID_PLAYER_2:
4603 case TOOL_CTRL_ID_PLAYER_3:
4606 case TOOL_CTRL_ID_PLAYER_4:
4611 // only check clickable animations if no request gadget clicked
4612 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4619 case SDL_WINDOWEVENT:
4620 HandleWindowEvent((WindowEvent *) &event);
4623 case SDL_APP_WILLENTERBACKGROUND:
4624 case SDL_APP_DIDENTERBACKGROUND:
4625 case SDL_APP_WILLENTERFOREGROUND:
4626 case SDL_APP_DIDENTERFOREGROUND:
4627 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4630 case EVENT_KEYPRESS:
4632 Key key = GetEventKey((KeyEvent *)&event);
4637 if (req_state & REQ_CONFIRM)
4646 #if defined(KSYM_Rewind)
4647 case KSYM_Rewind: // for Amazon Fire TV remote
4656 #if defined(KSYM_FastForward)
4657 case KSYM_FastForward: // for Amazon Fire TV remote
4663 HandleKeysDebug(key, KEY_PRESSED);
4667 if (req_state & REQ_PLAYER)
4669 int old_player_nr = setup.network_player_nr;
4672 result = old_player_nr + 1;
4677 result = old_player_nr + 1;
4708 case EVENT_FINGERRELEASE:
4709 case EVENT_KEYRELEASE:
4710 ClearPlayerAction();
4713 case SDL_CONTROLLERBUTTONDOWN:
4714 switch (event.cbutton.button)
4716 case SDL_CONTROLLER_BUTTON_A:
4717 case SDL_CONTROLLER_BUTTON_X:
4718 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4719 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4723 case SDL_CONTROLLER_BUTTON_B:
4724 case SDL_CONTROLLER_BUTTON_Y:
4725 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4726 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4727 case SDL_CONTROLLER_BUTTON_BACK:
4732 if (req_state & REQ_PLAYER)
4734 int old_player_nr = setup.network_player_nr;
4737 result = old_player_nr + 1;
4739 switch (event.cbutton.button)
4741 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4742 case SDL_CONTROLLER_BUTTON_Y:
4746 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4747 case SDL_CONTROLLER_BUTTON_B:
4751 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4752 case SDL_CONTROLLER_BUTTON_A:
4756 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4757 case SDL_CONTROLLER_BUTTON_X:
4768 case SDL_CONTROLLERBUTTONUP:
4769 HandleJoystickEvent(&event);
4770 ClearPlayerAction();
4774 HandleOtherEvents(&event);
4779 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4781 int joy = AnyJoystick();
4783 if (joy & JOY_BUTTON_1)
4785 else if (joy & JOY_BUTTON_2)
4788 else if (AnyJoystick())
4790 int joy = AnyJoystick();
4792 if (req_state & REQ_PLAYER)
4796 else if (joy & JOY_RIGHT)
4798 else if (joy & JOY_DOWN)
4800 else if (joy & JOY_LEFT)
4808 SetDrawtoField(draw_buffer_last);
4813 static boolean RequestDoor(char *text, unsigned int req_state)
4815 int draw_buffer_last = GetDrawtoField();
4816 unsigned int old_door_state = GetDoorState();
4817 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4818 int font_nr = FONT_TEXT_2;
4823 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4825 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4826 font_nr = FONT_TEXT_1;
4829 if (game_status == GAME_MODE_PLAYING)
4830 BlitScreenToBitmap(backbuffer);
4832 // disable deactivated drawing when quick-loading level tape recording
4833 if (tape.playing && tape.deactivate_display)
4834 TapeDeactivateDisplayOff(TRUE);
4836 SetMouseCursor(CURSOR_DEFAULT);
4838 // pause network game while waiting for request to answer
4839 if (network.enabled &&
4840 game_status == GAME_MODE_PLAYING &&
4841 !game.all_players_gone &&
4842 req_state & REQUEST_WAIT_FOR_INPUT)
4843 SendToServer_PausePlaying();
4845 // simulate releasing mouse button over last gadget, if still pressed
4847 HandleGadgets(-1, -1, 0);
4851 // draw released gadget before proceeding
4854 if (old_door_state & DOOR_OPEN_1)
4856 CloseDoor(DOOR_CLOSE_1);
4858 // save old door content
4859 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4860 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4863 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4864 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4866 // clear door drawing field
4867 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4869 // force DOOR font inside door area
4870 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4872 // write text for request
4873 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4875 char text_line[max_request_line_len + 1];
4881 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4883 tc = *(text_ptr + tx);
4884 // if (!tc || tc == ' ')
4885 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4889 if ((tc == '?' || tc == '!') && tl == 0)
4899 strncpy(text_line, text_ptr, tl);
4902 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4903 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4904 text_line, font_nr);
4906 text_ptr += tl + (tc == ' ' ? 1 : 0);
4907 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4912 if (req_state & REQ_ASK)
4914 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4915 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4916 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4917 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4919 else if (req_state & REQ_CONFIRM)
4921 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4922 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4924 else if (req_state & REQ_PLAYER)
4926 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4927 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4928 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4929 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4932 // copy request gadgets to door backbuffer
4933 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4935 OpenDoor(DOOR_OPEN_1);
4937 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4939 if (game_status == GAME_MODE_PLAYING)
4941 SetPanelBackground();
4942 SetDrawBackgroundMask(REDRAW_DOOR_1);
4946 SetDrawBackgroundMask(REDRAW_FIELD);
4952 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4954 // ---------- handle request buttons ----------
4955 result = RequestHandleEvents(req_state, draw_buffer_last);
4959 if (!(req_state & REQ_STAY_OPEN))
4961 CloseDoor(DOOR_CLOSE_1);
4963 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4964 (req_state & REQ_REOPEN))
4965 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4970 if (game_status == GAME_MODE_PLAYING)
4972 SetPanelBackground();
4973 SetDrawBackgroundMask(REDRAW_DOOR_1);
4977 SetDrawBackgroundMask(REDRAW_FIELD);
4980 // continue network game after request
4981 if (network.enabled &&
4982 game_status == GAME_MODE_PLAYING &&
4983 !game.all_players_gone &&
4984 req_state & REQUEST_WAIT_FOR_INPUT)
4985 SendToServer_ContinuePlaying();
4987 // restore deactivated drawing when quick-loading level tape recording
4988 if (tape.playing && tape.deactivate_display)
4989 TapeDeactivateDisplayOn();
4994 static boolean RequestEnvelope(char *text, unsigned int req_state)
4996 int draw_buffer_last = GetDrawtoField();
4999 if (game_status == GAME_MODE_PLAYING)
5000 BlitScreenToBitmap(backbuffer);
5002 // disable deactivated drawing when quick-loading level tape recording
5003 if (tape.playing && tape.deactivate_display)
5004 TapeDeactivateDisplayOff(TRUE);
5006 SetMouseCursor(CURSOR_DEFAULT);
5008 // pause network game while waiting for request to answer
5009 if (network.enabled &&
5010 game_status == GAME_MODE_PLAYING &&
5011 !game.all_players_gone &&
5012 req_state & REQUEST_WAIT_FOR_INPUT)
5013 SendToServer_PausePlaying();
5015 // simulate releasing mouse button over last gadget, if still pressed
5017 HandleGadgets(-1, -1, 0);
5021 // (replace with setting corresponding request background)
5022 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5023 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5025 // clear door drawing field
5026 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5028 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5030 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5032 if (game_status == GAME_MODE_PLAYING)
5034 SetPanelBackground();
5035 SetDrawBackgroundMask(REDRAW_DOOR_1);
5039 SetDrawBackgroundMask(REDRAW_FIELD);
5045 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5047 // ---------- handle request buttons ----------
5048 result = RequestHandleEvents(req_state, draw_buffer_last);
5052 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5056 if (game_status == GAME_MODE_PLAYING)
5058 SetPanelBackground();
5059 SetDrawBackgroundMask(REDRAW_DOOR_1);
5063 SetDrawBackgroundMask(REDRAW_FIELD);
5066 // continue network game after request
5067 if (network.enabled &&
5068 game_status == GAME_MODE_PLAYING &&
5069 !game.all_players_gone &&
5070 req_state & REQUEST_WAIT_FOR_INPUT)
5071 SendToServer_ContinuePlaying();
5073 // restore deactivated drawing when quick-loading level tape recording
5074 if (tape.playing && tape.deactivate_display)
5075 TapeDeactivateDisplayOn();
5080 boolean Request(char *text, unsigned int req_state)
5082 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
5083 boolean overlay_enabled = GetOverlayEnabled();
5086 // when showing request dialog after game ended, deactivate game panel
5088 game.panel.active = FALSE;
5090 game.request_active = TRUE;
5092 SetOverlayEnabled(FALSE);
5094 if (global.use_envelope_request)
5095 result = RequestEnvelope(text, req_state);
5097 result = RequestDoor(text, req_state);
5099 SetOverlayEnabled(overlay_enabled);
5101 game.request_active = FALSE;
5106 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5108 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5109 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5112 if (dpo1->sort_priority != dpo2->sort_priority)
5113 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5115 compare_result = dpo1->nr - dpo2->nr;
5117 return compare_result;
5120 void InitGraphicCompatibilityInfo_Doors(void)
5126 struct DoorInfo *door;
5130 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5131 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5133 { -1, -1, -1, NULL }
5135 struct Rect door_rect_list[] =
5137 { DX, DY, DXSIZE, DYSIZE },
5138 { VX, VY, VXSIZE, VYSIZE }
5142 for (i = 0; doors[i].door_token != -1; i++)
5144 int door_token = doors[i].door_token;
5145 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5146 int part_1 = doors[i].part_1;
5147 int part_8 = doors[i].part_8;
5148 int part_2 = part_1 + 1;
5149 int part_3 = part_1 + 2;
5150 struct DoorInfo *door = doors[i].door;
5151 struct Rect *door_rect = &door_rect_list[door_index];
5152 boolean door_gfx_redefined = FALSE;
5154 // check if any door part graphic definitions have been redefined
5156 for (j = 0; door_part_controls[j].door_token != -1; j++)
5158 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5159 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5161 if (dpc->door_token == door_token && fi->redefined)
5162 door_gfx_redefined = TRUE;
5165 // check for old-style door graphic/animation modifications
5167 if (!door_gfx_redefined)
5169 if (door->anim_mode & ANIM_STATIC_PANEL)
5171 door->panel.step_xoffset = 0;
5172 door->panel.step_yoffset = 0;
5175 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5177 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5178 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5179 int num_door_steps, num_panel_steps;
5181 // remove door part graphics other than the two default wings
5183 for (j = 0; door_part_controls[j].door_token != -1; j++)
5185 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5186 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5188 if (dpc->graphic >= part_3 &&
5189 dpc->graphic <= part_8)
5193 // set graphics and screen positions of the default wings
5195 g_part_1->width = door_rect->width;
5196 g_part_1->height = door_rect->height;
5197 g_part_2->width = door_rect->width;
5198 g_part_2->height = door_rect->height;
5199 g_part_2->src_x = door_rect->width;
5200 g_part_2->src_y = g_part_1->src_y;
5202 door->part_2.x = door->part_1.x;
5203 door->part_2.y = door->part_1.y;
5205 if (door->width != -1)
5207 g_part_1->width = door->width;
5208 g_part_2->width = door->width;
5210 // special treatment for graphics and screen position of right wing
5211 g_part_2->src_x += door_rect->width - door->width;
5212 door->part_2.x += door_rect->width - door->width;
5215 if (door->height != -1)
5217 g_part_1->height = door->height;
5218 g_part_2->height = door->height;
5220 // special treatment for graphics and screen position of bottom wing
5221 g_part_2->src_y += door_rect->height - door->height;
5222 door->part_2.y += door_rect->height - door->height;
5225 // set animation delays for the default wings and panels
5227 door->part_1.step_delay = door->step_delay;
5228 door->part_2.step_delay = door->step_delay;
5229 door->panel.step_delay = door->step_delay;
5231 // set animation draw order for the default wings
5233 door->part_1.sort_priority = 2; // draw left wing over ...
5234 door->part_2.sort_priority = 1; // ... right wing
5236 // set animation draw offset for the default wings
5238 if (door->anim_mode & ANIM_HORIZONTAL)
5240 door->part_1.step_xoffset = door->step_offset;
5241 door->part_1.step_yoffset = 0;
5242 door->part_2.step_xoffset = door->step_offset * -1;
5243 door->part_2.step_yoffset = 0;
5245 num_door_steps = g_part_1->width / door->step_offset;
5247 else // ANIM_VERTICAL
5249 door->part_1.step_xoffset = 0;
5250 door->part_1.step_yoffset = door->step_offset;
5251 door->part_2.step_xoffset = 0;
5252 door->part_2.step_yoffset = door->step_offset * -1;
5254 num_door_steps = g_part_1->height / door->step_offset;
5257 // set animation draw offset for the default panels
5259 if (door->step_offset > 1)
5261 num_panel_steps = 2 * door_rect->height / door->step_offset;
5262 door->panel.start_step = num_panel_steps - num_door_steps;
5263 door->panel.start_step_closing = door->panel.start_step;
5267 num_panel_steps = door_rect->height / door->step_offset;
5268 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5269 door->panel.start_step_closing = door->panel.start_step;
5270 door->panel.step_delay *= 2;
5277 void InitDoors(void)
5281 for (i = 0; door_part_controls[i].door_token != -1; i++)
5283 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5284 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5286 // initialize "start_step_opening" and "start_step_closing", if needed
5287 if (dpc->pos->start_step_opening == 0 &&
5288 dpc->pos->start_step_closing == 0)
5290 // dpc->pos->start_step_opening = dpc->pos->start_step;
5291 dpc->pos->start_step_closing = dpc->pos->start_step;
5294 // fill structure for door part draw order (sorted below)
5296 dpo->sort_priority = dpc->pos->sort_priority;
5299 // sort door part controls according to sort_priority and graphic number
5300 qsort(door_part_order, MAX_DOOR_PARTS,
5301 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5304 unsigned int OpenDoor(unsigned int door_state)
5306 if (door_state & DOOR_COPY_BACK)
5308 if (door_state & DOOR_OPEN_1)
5309 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5310 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5312 if (door_state & DOOR_OPEN_2)
5313 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5314 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5316 door_state &= ~DOOR_COPY_BACK;
5319 return MoveDoor(door_state);
5322 unsigned int CloseDoor(unsigned int door_state)
5324 unsigned int old_door_state = GetDoorState();
5326 if (!(door_state & DOOR_NO_COPY_BACK))
5328 if (old_door_state & DOOR_OPEN_1)
5329 BlitBitmap(backbuffer, bitmap_db_door_1,
5330 DX, DY, DXSIZE, DYSIZE, 0, 0);
5332 if (old_door_state & DOOR_OPEN_2)
5333 BlitBitmap(backbuffer, bitmap_db_door_2,
5334 VX, VY, VXSIZE, VYSIZE, 0, 0);
5336 door_state &= ~DOOR_NO_COPY_BACK;
5339 return MoveDoor(door_state);
5342 unsigned int GetDoorState(void)
5344 return MoveDoor(DOOR_GET_STATE);
5347 unsigned int SetDoorState(unsigned int door_state)
5349 return MoveDoor(door_state | DOOR_SET_STATE);
5352 static int euclid(int a, int b)
5354 return (b ? euclid(b, a % b) : a);
5357 unsigned int MoveDoor(unsigned int door_state)
5359 struct Rect door_rect_list[] =
5361 { DX, DY, DXSIZE, DYSIZE },
5362 { VX, VY, VXSIZE, VYSIZE }
5364 static int door1 = DOOR_CLOSE_1;
5365 static int door2 = DOOR_CLOSE_2;
5366 DelayCounter door_delay = { 0 };
5369 if (door_state == DOOR_GET_STATE)
5370 return (door1 | door2);
5372 if (door_state & DOOR_SET_STATE)
5374 if (door_state & DOOR_ACTION_1)
5375 door1 = door_state & DOOR_ACTION_1;
5376 if (door_state & DOOR_ACTION_2)
5377 door2 = door_state & DOOR_ACTION_2;
5379 return (door1 | door2);
5382 if (!(door_state & DOOR_FORCE_REDRAW))
5384 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5385 door_state &= ~DOOR_OPEN_1;
5386 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5387 door_state &= ~DOOR_CLOSE_1;
5388 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5389 door_state &= ~DOOR_OPEN_2;
5390 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5391 door_state &= ~DOOR_CLOSE_2;
5394 if (global.autoplay_leveldir)
5396 door_state |= DOOR_NO_DELAY;
5397 door_state &= ~DOOR_CLOSE_ALL;
5400 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5401 door_state |= DOOR_NO_DELAY;
5403 if (door_state & DOOR_ACTION)
5405 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
5406 boolean door_panel_drawn[NUM_DOORS];
5407 boolean panel_has_doors[NUM_DOORS];
5408 boolean door_part_skip[MAX_DOOR_PARTS];
5409 boolean door_part_done[MAX_DOOR_PARTS];
5410 boolean door_part_done_all;
5411 int num_steps[MAX_DOOR_PARTS];
5412 int max_move_delay = 0; // delay for complete animations of all doors
5413 int max_step_delay = 0; // delay (ms) between two animation frames
5414 int num_move_steps = 0; // number of animation steps for all doors
5415 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5416 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5420 for (i = 0; i < NUM_DOORS; i++)
5421 panel_has_doors[i] = FALSE;
5423 for (i = 0; i < MAX_DOOR_PARTS; i++)
5425 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5426 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5427 int door_token = dpc->door_token;
5429 door_part_done[i] = FALSE;
5430 door_part_skip[i] = (!(door_state & door_token) ||
5434 for (i = 0; i < MAX_DOOR_PARTS; i++)
5436 int nr = door_part_order[i].nr;
5437 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5438 struct DoorPartPosInfo *pos = dpc->pos;
5439 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5440 int door_token = dpc->door_token;
5441 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5442 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5443 int step_xoffset = ABS(pos->step_xoffset);
5444 int step_yoffset = ABS(pos->step_yoffset);
5445 int step_delay = pos->step_delay;
5446 int current_door_state = door_state & door_token;
5447 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5448 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5449 boolean part_opening = (is_panel ? door_closing : door_opening);
5450 int start_step = (part_opening ? pos->start_step_opening :
5451 pos->start_step_closing);
5452 float move_xsize = (step_xoffset ? g->width : 0);
5453 float move_ysize = (step_yoffset ? g->height : 0);
5454 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5455 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5456 int move_steps = (move_xsteps && move_ysteps ?
5457 MIN(move_xsteps, move_ysteps) :
5458 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5459 int move_delay = move_steps * step_delay;
5461 if (door_part_skip[nr])
5464 max_move_delay = MAX(max_move_delay, move_delay);
5465 max_step_delay = (max_step_delay == 0 ? step_delay :
5466 euclid(max_step_delay, step_delay));
5467 num_steps[nr] = move_steps;
5471 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5473 panel_has_doors[door_index] = TRUE;
5477 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5479 num_move_steps = max_move_delay / max_step_delay;
5480 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5482 door_delay.value = max_step_delay;
5484 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5486 start = num_move_steps - 1;
5490 // opening door sound has priority over simultaneously closing door
5491 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5493 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5495 if (door_state & DOOR_OPEN_1)
5496 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5497 if (door_state & DOOR_OPEN_2)
5498 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5500 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5502 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5504 if (door_state & DOOR_CLOSE_1)
5505 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5506 if (door_state & DOOR_CLOSE_2)
5507 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5511 for (k = start; k < num_move_steps; k++)
5513 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5515 door_part_done_all = TRUE;
5517 for (i = 0; i < NUM_DOORS; i++)
5518 door_panel_drawn[i] = FALSE;
5520 for (i = 0; i < MAX_DOOR_PARTS; i++)
5522 int nr = door_part_order[i].nr;
5523 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5524 struct DoorPartPosInfo *pos = dpc->pos;
5525 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5526 int door_token = dpc->door_token;
5527 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5528 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5529 boolean is_panel_and_door_has_closed = FALSE;
5530 struct Rect *door_rect = &door_rect_list[door_index];
5531 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5533 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5534 int current_door_state = door_state & door_token;
5535 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5536 boolean door_closing = !door_opening;
5537 boolean part_opening = (is_panel ? door_closing : door_opening);
5538 boolean part_closing = !part_opening;
5539 int start_step = (part_opening ? pos->start_step_opening :
5540 pos->start_step_closing);
5541 int step_delay = pos->step_delay;
5542 int step_factor = step_delay / max_step_delay;
5543 int k1 = (step_factor ? k / step_factor + 1 : k);
5544 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5545 int kk = MAX(0, k2);
5548 int src_x, src_y, src_xx, src_yy;
5549 int dst_x, dst_y, dst_xx, dst_yy;
5552 if (door_part_skip[nr])
5555 if (!(door_state & door_token))
5563 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5564 int kk_door = MAX(0, k2_door);
5565 int sync_frame = kk_door * door_delay.value;
5566 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5568 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5569 &g_src_x, &g_src_y);
5574 if (!door_panel_drawn[door_index])
5576 ClearRectangle(drawto, door_rect->x, door_rect->y,
5577 door_rect->width, door_rect->height);
5579 door_panel_drawn[door_index] = TRUE;
5582 // draw opening or closing door parts
5584 if (pos->step_xoffset < 0) // door part on right side
5587 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5590 if (dst_xx + width > door_rect->width)
5591 width = door_rect->width - dst_xx;
5593 else // door part on left side
5596 dst_xx = pos->x - kk * pos->step_xoffset;
5600 src_xx = ABS(dst_xx);
5604 width = g->width - src_xx;
5606 if (width > door_rect->width)
5607 width = door_rect->width;
5609 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5612 if (pos->step_yoffset < 0) // door part on bottom side
5615 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5618 if (dst_yy + height > door_rect->height)
5619 height = door_rect->height - dst_yy;
5621 else // door part on top side
5624 dst_yy = pos->y - kk * pos->step_yoffset;
5628 src_yy = ABS(dst_yy);
5632 height = g->height - src_yy;
5635 src_x = g_src_x + src_xx;
5636 src_y = g_src_y + src_yy;
5638 dst_x = door_rect->x + dst_xx;
5639 dst_y = door_rect->y + dst_yy;
5641 is_panel_and_door_has_closed =
5644 panel_has_doors[door_index] &&
5645 k >= num_move_steps_doors_only - 1);
5647 if (width >= 0 && width <= g->width &&
5648 height >= 0 && height <= g->height &&
5649 !is_panel_and_door_has_closed)
5651 if (is_panel || !pos->draw_masked)
5652 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5655 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5659 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5661 if ((part_opening && (width < 0 || height < 0)) ||
5662 (part_closing && (width >= g->width && height >= g->height)))
5663 door_part_done[nr] = TRUE;
5665 // continue door part animations, but not panel after door has closed
5666 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5667 door_part_done_all = FALSE;
5670 if (!(door_state & DOOR_NO_DELAY))
5673 HandleGameActions();
5677 SkipUntilDelayReached(&door_delay, &k, last_frame);
5679 // prevent OS (Windows) from complaining about program not responding
5683 if (door_part_done_all)
5687 if (!(door_state & DOOR_NO_DELAY))
5689 // wait for specified door action post delay
5690 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5691 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5692 else if (door_state & DOOR_ACTION_1)
5693 door_delay.value = door_1.post_delay;
5694 else if (door_state & DOOR_ACTION_2)
5695 door_delay.value = door_2.post_delay;
5697 while (!DelayReached(&door_delay))
5700 HandleGameActions();
5707 if (door_state & DOOR_ACTION_1)
5708 door1 = door_state & DOOR_ACTION_1;
5709 if (door_state & DOOR_ACTION_2)
5710 door2 = door_state & DOOR_ACTION_2;
5712 // draw masked border over door area
5713 DrawMaskedBorder(REDRAW_DOOR_1);
5714 DrawMaskedBorder(REDRAW_DOOR_2);
5716 ClearAutoRepeatKeyEvents();
5718 return (door1 | door2);
5721 static boolean useSpecialEditorDoor(void)
5723 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5724 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5726 // do not draw special editor door if editor border defined or redefined
5727 if (graphic_info[graphic].bitmap != NULL || redefined)
5730 // do not draw special editor door if global border defined to be empty
5731 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5734 // do not draw special editor door if viewport definitions do not match
5738 EY + EYSIZE != VY + VYSIZE)
5744 void DrawSpecialEditorDoor(void)
5746 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5747 int top_border_width = gfx1->width;
5748 int top_border_height = gfx1->height;
5749 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5750 int ex = EX - outer_border;
5751 int ey = EY - outer_border;
5752 int vy = VY - outer_border;
5753 int exsize = EXSIZE + 2 * outer_border;
5755 if (!useSpecialEditorDoor())
5758 // draw bigger level editor toolbox window
5759 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5760 top_border_width, top_border_height, ex, ey - top_border_height);
5761 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5762 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5764 redraw_mask |= REDRAW_ALL;
5767 void UndrawSpecialEditorDoor(void)
5769 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5770 int top_border_width = gfx1->width;
5771 int top_border_height = gfx1->height;
5772 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5773 int ex = EX - outer_border;
5774 int ey = EY - outer_border;
5775 int ey_top = ey - top_border_height;
5776 int exsize = EXSIZE + 2 * outer_border;
5777 int eysize = EYSIZE + 2 * outer_border;
5779 if (!useSpecialEditorDoor())
5782 // draw normal tape recorder window
5783 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5785 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5786 ex, ey_top, top_border_width, top_border_height,
5788 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5789 ex, ey, exsize, eysize, ex, ey);
5793 // if screen background is set to "[NONE]", clear editor toolbox window
5794 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5795 ClearRectangle(drawto, ex, ey, exsize, eysize);
5798 redraw_mask |= REDRAW_ALL;
5802 // ---------- new tool button stuff -------------------------------------------
5807 struct TextPosInfo *pos;
5809 boolean is_touch_button;
5811 } toolbutton_info[NUM_TOOL_BUTTONS] =
5814 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5815 TOOL_CTRL_ID_YES, FALSE, "yes"
5818 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5819 TOOL_CTRL_ID_NO, FALSE, "no"
5822 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5823 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5826 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5827 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5830 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5831 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5834 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5835 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5838 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5839 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5842 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5843 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5846 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5847 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5850 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5851 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5855 void CreateToolButtons(void)
5859 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5861 int graphic = toolbutton_info[i].graphic;
5862 struct GraphicInfo *gfx = &graphic_info[graphic];
5863 struct TextPosInfo *pos = toolbutton_info[i].pos;
5864 struct GadgetInfo *gi;
5865 Bitmap *deco_bitmap = None;
5866 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5867 unsigned int event_mask = GD_EVENT_RELEASED;
5868 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5869 int base_x = (is_touch_button ? 0 : DX);
5870 int base_y = (is_touch_button ? 0 : DY);
5871 int gd_x = gfx->src_x;
5872 int gd_y = gfx->src_y;
5873 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5874 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5879 // do not use touch buttons if overlay touch buttons are disabled
5880 if (is_touch_button && !setup.touch.overlay_buttons)
5883 if (global.use_envelope_request && !is_touch_button)
5885 setRequestPosition(&base_x, &base_y, TRUE);
5887 // check if request buttons are outside of envelope and fix, if needed
5888 if (x < 0 || x + gfx->width > request.width ||
5889 y < 0 || y + gfx->height > request.height)
5891 if (id == TOOL_CTRL_ID_YES)
5894 y = request.height - 2 * request.border_size - gfx->height;
5896 else if (id == TOOL_CTRL_ID_NO)
5898 x = request.width - 2 * request.border_size - gfx->width;
5899 y = request.height - 2 * request.border_size - gfx->height;
5901 else if (id == TOOL_CTRL_ID_CONFIRM)
5903 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5904 y = request.height - 2 * request.border_size - gfx->height;
5906 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5908 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5910 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5911 y = request.height - 2 * request.border_size - gfx->height * 2;
5913 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5914 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5919 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5922 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5924 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5925 pos->size, &deco_bitmap, &deco_x, &deco_y);
5926 deco_xpos = (gfx->width - pos->size) / 2;
5927 deco_ypos = (gfx->height - pos->size) / 2;
5930 gi = CreateGadget(GDI_CUSTOM_ID, id,
5931 GDI_IMAGE_ID, graphic,
5932 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5935 GDI_WIDTH, gfx->width,
5936 GDI_HEIGHT, gfx->height,
5937 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5938 GDI_STATE, GD_BUTTON_UNPRESSED,
5939 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5940 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5941 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5942 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5943 GDI_DECORATION_SIZE, pos->size, pos->size,
5944 GDI_DECORATION_SHIFTING, 1, 1,
5945 GDI_DIRECT_DRAW, FALSE,
5946 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5947 GDI_EVENT_MASK, event_mask,
5948 GDI_CALLBACK_ACTION, HandleToolButtons,
5952 Fail("cannot create gadget");
5954 tool_gadget[id] = gi;
5958 void FreeToolButtons(void)
5962 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5963 FreeGadget(tool_gadget[i]);
5966 static void UnmapToolButtons(void)
5970 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5971 UnmapGadget(tool_gadget[i]);
5974 static void HandleToolButtons(struct GadgetInfo *gi)
5976 request_gadget_id = gi->custom_id;
5979 static struct Mapping_EM_to_RND_object
5982 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5983 boolean is_backside; // backside of moving element
5989 em_object_mapping_list[GAME_TILE_MAX + 1] =
5992 Zborder, FALSE, FALSE,
5996 Zplayer, FALSE, FALSE,
6005 Ztank, FALSE, FALSE,
6009 Zeater, FALSE, FALSE,
6013 Zdynamite, FALSE, FALSE,
6017 Zboom, FALSE, FALSE,
6022 Xchain, FALSE, FALSE,
6023 EL_DEFAULT, ACTION_EXPLODING, -1
6026 Xboom_bug, FALSE, FALSE,
6027 EL_BUG, ACTION_EXPLODING, -1
6030 Xboom_tank, FALSE, FALSE,
6031 EL_SPACESHIP, ACTION_EXPLODING, -1
6034 Xboom_android, FALSE, FALSE,
6035 EL_EMC_ANDROID, ACTION_OTHER, -1
6038 Xboom_1, FALSE, FALSE,
6039 EL_DEFAULT, ACTION_EXPLODING, -1
6042 Xboom_2, FALSE, FALSE,
6043 EL_DEFAULT, ACTION_EXPLODING, -1
6047 Xblank, TRUE, FALSE,
6052 Xsplash_e, FALSE, FALSE,
6053 EL_ACID_SPLASH_RIGHT, -1, -1
6056 Xsplash_w, FALSE, FALSE,
6057 EL_ACID_SPLASH_LEFT, -1, -1
6061 Xplant, TRUE, FALSE,
6062 EL_EMC_PLANT, -1, -1
6065 Yplant, FALSE, FALSE,
6066 EL_EMC_PLANT, -1, -1
6070 Xacid_1, TRUE, FALSE,
6074 Xacid_2, FALSE, FALSE,
6078 Xacid_3, FALSE, FALSE,
6082 Xacid_4, FALSE, FALSE,
6086 Xacid_5, FALSE, FALSE,
6090 Xacid_6, FALSE, FALSE,
6094 Xacid_7, FALSE, FALSE,
6098 Xacid_8, FALSE, FALSE,
6103 Xfake_acid_1, TRUE, FALSE,
6104 EL_EMC_FAKE_ACID, -1, -1
6107 Xfake_acid_2, FALSE, FALSE,
6108 EL_EMC_FAKE_ACID, -1, -1
6111 Xfake_acid_3, FALSE, FALSE,
6112 EL_EMC_FAKE_ACID, -1, -1
6115 Xfake_acid_4, FALSE, FALSE,
6116 EL_EMC_FAKE_ACID, -1, -1
6119 Xfake_acid_5, FALSE, FALSE,
6120 EL_EMC_FAKE_ACID, -1, -1
6123 Xfake_acid_6, FALSE, FALSE,
6124 EL_EMC_FAKE_ACID, -1, -1
6127 Xfake_acid_7, FALSE, FALSE,
6128 EL_EMC_FAKE_ACID, -1, -1
6131 Xfake_acid_8, FALSE, FALSE,
6132 EL_EMC_FAKE_ACID, -1, -1
6136 Xfake_acid_1_player, FALSE, FALSE,
6137 EL_EMC_FAKE_ACID, -1, -1
6140 Xfake_acid_2_player, FALSE, FALSE,
6141 EL_EMC_FAKE_ACID, -1, -1
6144 Xfake_acid_3_player, FALSE, FALSE,
6145 EL_EMC_FAKE_ACID, -1, -1
6148 Xfake_acid_4_player, FALSE, FALSE,
6149 EL_EMC_FAKE_ACID, -1, -1
6152 Xfake_acid_5_player, FALSE, FALSE,
6153 EL_EMC_FAKE_ACID, -1, -1
6156 Xfake_acid_6_player, FALSE, FALSE,
6157 EL_EMC_FAKE_ACID, -1, -1
6160 Xfake_acid_7_player, FALSE, FALSE,
6161 EL_EMC_FAKE_ACID, -1, -1
6164 Xfake_acid_8_player, FALSE, FALSE,
6165 EL_EMC_FAKE_ACID, -1, -1
6169 Xgrass, TRUE, FALSE,
6170 EL_EMC_GRASS, -1, -1
6173 Ygrass_nB, FALSE, FALSE,
6174 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6177 Ygrass_eB, FALSE, FALSE,
6178 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6181 Ygrass_sB, FALSE, FALSE,
6182 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6185 Ygrass_wB, FALSE, FALSE,
6186 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6194 Ydirt_nB, FALSE, FALSE,
6195 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6198 Ydirt_eB, FALSE, FALSE,
6199 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6202 Ydirt_sB, FALSE, FALSE,
6203 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6206 Ydirt_wB, FALSE, FALSE,
6207 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6211 Xandroid, TRUE, FALSE,
6212 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6215 Xandroid_1_n, FALSE, FALSE,
6216 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6219 Xandroid_2_n, FALSE, FALSE,
6220 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6223 Xandroid_1_e, FALSE, FALSE,
6224 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6227 Xandroid_2_e, FALSE, FALSE,
6228 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6231 Xandroid_1_w, FALSE, FALSE,
6232 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6235 Xandroid_2_w, FALSE, FALSE,
6236 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6239 Xandroid_1_s, FALSE, FALSE,
6240 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6243 Xandroid_2_s, FALSE, FALSE,
6244 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6247 Yandroid_n, FALSE, FALSE,
6248 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6251 Yandroid_nB, FALSE, TRUE,
6252 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6255 Yandroid_ne, FALSE, FALSE,
6256 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6259 Yandroid_neB, FALSE, TRUE,
6260 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6263 Yandroid_e, FALSE, FALSE,
6264 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6267 Yandroid_eB, FALSE, TRUE,
6268 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6271 Yandroid_se, FALSE, FALSE,
6272 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6275 Yandroid_seB, FALSE, TRUE,
6276 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6279 Yandroid_s, FALSE, FALSE,
6280 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6283 Yandroid_sB, FALSE, TRUE,
6284 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6287 Yandroid_sw, FALSE, FALSE,
6288 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6291 Yandroid_swB, FALSE, TRUE,
6292 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6295 Yandroid_w, FALSE, FALSE,
6296 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6299 Yandroid_wB, FALSE, TRUE,
6300 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6303 Yandroid_nw, FALSE, FALSE,
6304 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6307 Yandroid_nwB, FALSE, TRUE,
6308 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6312 Xeater_n, TRUE, FALSE,
6313 EL_YAMYAM_UP, -1, -1
6316 Xeater_e, TRUE, FALSE,
6317 EL_YAMYAM_RIGHT, -1, -1
6320 Xeater_w, TRUE, FALSE,
6321 EL_YAMYAM_LEFT, -1, -1
6324 Xeater_s, TRUE, FALSE,
6325 EL_YAMYAM_DOWN, -1, -1
6328 Yeater_n, FALSE, FALSE,
6329 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6332 Yeater_nB, FALSE, TRUE,
6333 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6336 Yeater_e, FALSE, FALSE,
6337 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6340 Yeater_eB, FALSE, TRUE,
6341 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6344 Yeater_s, FALSE, FALSE,
6345 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6348 Yeater_sB, FALSE, TRUE,
6349 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6352 Yeater_w, FALSE, FALSE,
6353 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6356 Yeater_wB, FALSE, TRUE,
6357 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6360 Yeater_stone, FALSE, FALSE,
6361 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6364 Yeater_spring, FALSE, FALSE,
6365 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6369 Xalien, TRUE, FALSE,
6373 Xalien_pause, FALSE, FALSE,
6377 Yalien_n, FALSE, FALSE,
6378 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6381 Yalien_nB, FALSE, TRUE,
6382 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6385 Yalien_e, FALSE, FALSE,
6386 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6389 Yalien_eB, FALSE, TRUE,
6390 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6393 Yalien_s, FALSE, FALSE,
6394 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6397 Yalien_sB, FALSE, TRUE,
6398 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6401 Yalien_w, FALSE, FALSE,
6402 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6405 Yalien_wB, FALSE, TRUE,
6406 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6409 Yalien_stone, FALSE, FALSE,
6410 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6413 Yalien_spring, FALSE, FALSE,
6414 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6418 Xbug_1_n, TRUE, FALSE,
6422 Xbug_1_e, TRUE, FALSE,
6423 EL_BUG_RIGHT, -1, -1
6426 Xbug_1_s, TRUE, FALSE,
6430 Xbug_1_w, TRUE, FALSE,
6434 Xbug_2_n, FALSE, FALSE,
6438 Xbug_2_e, FALSE, FALSE,
6439 EL_BUG_RIGHT, -1, -1
6442 Xbug_2_s, FALSE, FALSE,
6446 Xbug_2_w, FALSE, FALSE,
6450 Ybug_n, FALSE, FALSE,
6451 EL_BUG, ACTION_MOVING, MV_BIT_UP
6454 Ybug_nB, FALSE, TRUE,
6455 EL_BUG, ACTION_MOVING, MV_BIT_UP
6458 Ybug_e, FALSE, FALSE,
6459 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6462 Ybug_eB, FALSE, TRUE,
6463 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6466 Ybug_s, FALSE, FALSE,
6467 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6470 Ybug_sB, FALSE, TRUE,
6471 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6474 Ybug_w, FALSE, FALSE,
6475 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6478 Ybug_wB, FALSE, TRUE,
6479 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6482 Ybug_w_n, FALSE, FALSE,
6483 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6486 Ybug_n_e, FALSE, FALSE,
6487 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6490 Ybug_e_s, FALSE, FALSE,
6491 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6494 Ybug_s_w, FALSE, FALSE,
6495 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6498 Ybug_e_n, FALSE, FALSE,
6499 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6502 Ybug_s_e, FALSE, FALSE,
6503 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6506 Ybug_w_s, FALSE, FALSE,
6507 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6510 Ybug_n_w, FALSE, FALSE,
6511 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6514 Ybug_stone, FALSE, FALSE,
6515 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6518 Ybug_spring, FALSE, FALSE,
6519 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6523 Xtank_1_n, TRUE, FALSE,
6524 EL_SPACESHIP_UP, -1, -1
6527 Xtank_1_e, TRUE, FALSE,
6528 EL_SPACESHIP_RIGHT, -1, -1
6531 Xtank_1_s, TRUE, FALSE,
6532 EL_SPACESHIP_DOWN, -1, -1
6535 Xtank_1_w, TRUE, FALSE,
6536 EL_SPACESHIP_LEFT, -1, -1
6539 Xtank_2_n, FALSE, FALSE,
6540 EL_SPACESHIP_UP, -1, -1
6543 Xtank_2_e, FALSE, FALSE,
6544 EL_SPACESHIP_RIGHT, -1, -1
6547 Xtank_2_s, FALSE, FALSE,
6548 EL_SPACESHIP_DOWN, -1, -1
6551 Xtank_2_w, FALSE, FALSE,
6552 EL_SPACESHIP_LEFT, -1, -1
6555 Ytank_n, FALSE, FALSE,
6556 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6559 Ytank_nB, FALSE, TRUE,
6560 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6563 Ytank_e, FALSE, FALSE,
6564 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6567 Ytank_eB, FALSE, TRUE,
6568 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6571 Ytank_s, FALSE, FALSE,
6572 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6575 Ytank_sB, FALSE, TRUE,
6576 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6579 Ytank_w, FALSE, FALSE,
6580 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6583 Ytank_wB, FALSE, TRUE,
6584 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6587 Ytank_w_n, FALSE, FALSE,
6588 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6591 Ytank_n_e, FALSE, FALSE,
6592 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6595 Ytank_e_s, FALSE, FALSE,
6596 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6599 Ytank_s_w, FALSE, FALSE,
6600 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6603 Ytank_e_n, FALSE, FALSE,
6604 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6607 Ytank_s_e, FALSE, FALSE,
6608 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6611 Ytank_w_s, FALSE, FALSE,
6612 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6615 Ytank_n_w, FALSE, FALSE,
6616 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6619 Ytank_stone, FALSE, FALSE,
6620 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6623 Ytank_spring, FALSE, FALSE,
6624 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6628 Xemerald, TRUE, FALSE,
6632 Xemerald_pause, FALSE, FALSE,
6636 Xemerald_fall, FALSE, FALSE,
6640 Xemerald_shine, FALSE, FALSE,
6641 EL_EMERALD, ACTION_TWINKLING, -1
6644 Yemerald_s, FALSE, FALSE,
6645 EL_EMERALD, ACTION_FALLING, -1
6648 Yemerald_sB, FALSE, TRUE,
6649 EL_EMERALD, ACTION_FALLING, -1
6652 Yemerald_e, FALSE, FALSE,
6653 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6656 Yemerald_eB, FALSE, TRUE,
6657 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6660 Yemerald_w, FALSE, FALSE,
6661 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6664 Yemerald_wB, FALSE, TRUE,
6665 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6668 Yemerald_blank, FALSE, FALSE,
6669 EL_EMERALD, ACTION_COLLECTING, -1
6673 Xdiamond, TRUE, FALSE,
6677 Xdiamond_pause, FALSE, FALSE,
6681 Xdiamond_fall, FALSE, FALSE,
6685 Xdiamond_shine, FALSE, FALSE,
6686 EL_DIAMOND, ACTION_TWINKLING, -1
6689 Ydiamond_s, FALSE, FALSE,
6690 EL_DIAMOND, ACTION_FALLING, -1
6693 Ydiamond_sB, FALSE, TRUE,
6694 EL_DIAMOND, ACTION_FALLING, -1
6697 Ydiamond_e, FALSE, FALSE,
6698 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6701 Ydiamond_eB, FALSE, TRUE,
6702 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6705 Ydiamond_w, FALSE, FALSE,
6706 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6709 Ydiamond_wB, FALSE, TRUE,
6710 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6713 Ydiamond_blank, FALSE, FALSE,
6714 EL_DIAMOND, ACTION_COLLECTING, -1
6717 Ydiamond_stone, FALSE, FALSE,
6718 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6722 Xstone, TRUE, FALSE,
6726 Xstone_pause, FALSE, FALSE,
6730 Xstone_fall, FALSE, FALSE,
6734 Ystone_s, FALSE, FALSE,
6735 EL_ROCK, ACTION_FALLING, -1
6738 Ystone_sB, FALSE, TRUE,
6739 EL_ROCK, ACTION_FALLING, -1
6742 Ystone_e, FALSE, FALSE,
6743 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6746 Ystone_eB, FALSE, TRUE,
6747 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6750 Ystone_w, FALSE, FALSE,
6751 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6754 Ystone_wB, FALSE, TRUE,
6755 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6763 Xbomb_pause, FALSE, FALSE,
6767 Xbomb_fall, FALSE, FALSE,
6771 Ybomb_s, FALSE, FALSE,
6772 EL_BOMB, ACTION_FALLING, -1
6775 Ybomb_sB, FALSE, TRUE,
6776 EL_BOMB, ACTION_FALLING, -1
6779 Ybomb_e, FALSE, FALSE,
6780 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6783 Ybomb_eB, FALSE, TRUE,
6784 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6787 Ybomb_w, FALSE, FALSE,
6788 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6791 Ybomb_wB, FALSE, TRUE,
6792 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6795 Ybomb_blank, FALSE, FALSE,
6796 EL_BOMB, ACTION_ACTIVATING, -1
6804 Xnut_pause, FALSE, FALSE,
6808 Xnut_fall, FALSE, FALSE,
6812 Ynut_s, FALSE, FALSE,
6813 EL_NUT, ACTION_FALLING, -1
6816 Ynut_sB, FALSE, TRUE,
6817 EL_NUT, ACTION_FALLING, -1
6820 Ynut_e, FALSE, FALSE,
6821 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6824 Ynut_eB, FALSE, TRUE,
6825 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6828 Ynut_w, FALSE, FALSE,
6829 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6832 Ynut_wB, FALSE, TRUE,
6833 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6836 Ynut_stone, FALSE, FALSE,
6837 EL_NUT, ACTION_BREAKING, -1
6841 Xspring, TRUE, FALSE,
6845 Xspring_pause, FALSE, FALSE,
6849 Xspring_e, TRUE, FALSE,
6850 EL_SPRING_RIGHT, -1, -1
6853 Xspring_w, TRUE, FALSE,
6854 EL_SPRING_LEFT, -1, -1
6857 Xspring_fall, FALSE, FALSE,
6861 Yspring_s, FALSE, FALSE,
6862 EL_SPRING, ACTION_FALLING, -1
6865 Yspring_sB, FALSE, TRUE,
6866 EL_SPRING, ACTION_FALLING, -1
6869 Yspring_e, FALSE, FALSE,
6870 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6873 Yspring_eB, FALSE, TRUE,
6874 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6877 Yspring_w, FALSE, FALSE,
6878 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6881 Yspring_wB, FALSE, TRUE,
6882 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6885 Yspring_alien_e, FALSE, FALSE,
6886 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6889 Yspring_alien_eB, FALSE, TRUE,
6890 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6893 Yspring_alien_w, FALSE, FALSE,
6894 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6897 Yspring_alien_wB, FALSE, TRUE,
6898 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6902 Xpush_emerald_e, FALSE, FALSE,
6903 EL_EMERALD, -1, MV_BIT_RIGHT
6906 Xpush_emerald_w, FALSE, FALSE,
6907 EL_EMERALD, -1, MV_BIT_LEFT
6910 Xpush_diamond_e, FALSE, FALSE,
6911 EL_DIAMOND, -1, MV_BIT_RIGHT
6914 Xpush_diamond_w, FALSE, FALSE,
6915 EL_DIAMOND, -1, MV_BIT_LEFT
6918 Xpush_stone_e, FALSE, FALSE,
6919 EL_ROCK, -1, MV_BIT_RIGHT
6922 Xpush_stone_w, FALSE, FALSE,
6923 EL_ROCK, -1, MV_BIT_LEFT
6926 Xpush_bomb_e, FALSE, FALSE,
6927 EL_BOMB, -1, MV_BIT_RIGHT
6930 Xpush_bomb_w, FALSE, FALSE,
6931 EL_BOMB, -1, MV_BIT_LEFT
6934 Xpush_nut_e, FALSE, FALSE,
6935 EL_NUT, -1, MV_BIT_RIGHT
6938 Xpush_nut_w, FALSE, FALSE,
6939 EL_NUT, -1, MV_BIT_LEFT
6942 Xpush_spring_e, FALSE, FALSE,
6943 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6946 Xpush_spring_w, FALSE, FALSE,
6947 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6951 Xdynamite, TRUE, FALSE,
6952 EL_EM_DYNAMITE, -1, -1
6955 Ydynamite_blank, FALSE, FALSE,
6956 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6959 Xdynamite_1, TRUE, FALSE,
6960 EL_EM_DYNAMITE_ACTIVE, -1, -1
6963 Xdynamite_2, FALSE, FALSE,
6964 EL_EM_DYNAMITE_ACTIVE, -1, -1
6967 Xdynamite_3, FALSE, FALSE,
6968 EL_EM_DYNAMITE_ACTIVE, -1, -1
6971 Xdynamite_4, FALSE, FALSE,
6972 EL_EM_DYNAMITE_ACTIVE, -1, -1
6976 Xkey_1, TRUE, FALSE,
6980 Xkey_2, TRUE, FALSE,
6984 Xkey_3, TRUE, FALSE,
6988 Xkey_4, TRUE, FALSE,
6992 Xkey_5, TRUE, FALSE,
6993 EL_EMC_KEY_5, -1, -1
6996 Xkey_6, TRUE, FALSE,
6997 EL_EMC_KEY_6, -1, -1
7000 Xkey_7, TRUE, FALSE,
7001 EL_EMC_KEY_7, -1, -1
7004 Xkey_8, TRUE, FALSE,
7005 EL_EMC_KEY_8, -1, -1
7009 Xdoor_1, TRUE, FALSE,
7010 EL_EM_GATE_1, -1, -1
7013 Xdoor_2, TRUE, FALSE,
7014 EL_EM_GATE_2, -1, -1
7017 Xdoor_3, TRUE, FALSE,
7018 EL_EM_GATE_3, -1, -1
7021 Xdoor_4, TRUE, FALSE,
7022 EL_EM_GATE_4, -1, -1
7025 Xdoor_5, TRUE, FALSE,
7026 EL_EMC_GATE_5, -1, -1
7029 Xdoor_6, TRUE, FALSE,
7030 EL_EMC_GATE_6, -1, -1
7033 Xdoor_7, TRUE, FALSE,
7034 EL_EMC_GATE_7, -1, -1
7037 Xdoor_8, TRUE, FALSE,
7038 EL_EMC_GATE_8, -1, -1
7042 Xfake_door_1, TRUE, FALSE,
7043 EL_EM_GATE_1_GRAY, -1, -1
7046 Xfake_door_2, TRUE, FALSE,
7047 EL_EM_GATE_2_GRAY, -1, -1
7050 Xfake_door_3, TRUE, FALSE,
7051 EL_EM_GATE_3_GRAY, -1, -1
7054 Xfake_door_4, TRUE, FALSE,
7055 EL_EM_GATE_4_GRAY, -1, -1
7058 Xfake_door_5, TRUE, FALSE,
7059 EL_EMC_GATE_5_GRAY, -1, -1
7062 Xfake_door_6, TRUE, FALSE,
7063 EL_EMC_GATE_6_GRAY, -1, -1
7066 Xfake_door_7, TRUE, FALSE,
7067 EL_EMC_GATE_7_GRAY, -1, -1
7070 Xfake_door_8, TRUE, FALSE,
7071 EL_EMC_GATE_8_GRAY, -1, -1
7075 Xballoon, TRUE, FALSE,
7079 Yballoon_n, FALSE, FALSE,
7080 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7083 Yballoon_nB, FALSE, TRUE,
7084 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7087 Yballoon_e, FALSE, FALSE,
7088 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7091 Yballoon_eB, FALSE, TRUE,
7092 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7095 Yballoon_s, FALSE, FALSE,
7096 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7099 Yballoon_sB, FALSE, TRUE,
7100 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7103 Yballoon_w, FALSE, FALSE,
7104 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7107 Yballoon_wB, FALSE, TRUE,
7108 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7112 Xball_1, TRUE, FALSE,
7113 EL_EMC_MAGIC_BALL, -1, -1
7116 Yball_1, FALSE, FALSE,
7117 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7120 Xball_2, FALSE, FALSE,
7121 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7124 Yball_2, FALSE, FALSE,
7125 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7128 Yball_blank, FALSE, FALSE,
7129 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7133 Xamoeba_1, TRUE, FALSE,
7134 EL_AMOEBA_DRY, ACTION_OTHER, -1
7137 Xamoeba_2, FALSE, FALSE,
7138 EL_AMOEBA_DRY, ACTION_OTHER, -1
7141 Xamoeba_3, FALSE, FALSE,
7142 EL_AMOEBA_DRY, ACTION_OTHER, -1
7145 Xamoeba_4, FALSE, FALSE,
7146 EL_AMOEBA_DRY, ACTION_OTHER, -1
7149 Xamoeba_5, TRUE, FALSE,
7150 EL_AMOEBA_WET, ACTION_OTHER, -1
7153 Xamoeba_6, FALSE, FALSE,
7154 EL_AMOEBA_WET, ACTION_OTHER, -1
7157 Xamoeba_7, FALSE, FALSE,
7158 EL_AMOEBA_WET, ACTION_OTHER, -1
7161 Xamoeba_8, FALSE, FALSE,
7162 EL_AMOEBA_WET, ACTION_OTHER, -1
7167 EL_AMOEBA_DROP, ACTION_GROWING, -1
7170 Xdrip_fall, FALSE, FALSE,
7171 EL_AMOEBA_DROP, -1, -1
7174 Xdrip_stretch, FALSE, FALSE,
7175 EL_AMOEBA_DROP, ACTION_FALLING, -1
7178 Xdrip_stretchB, FALSE, TRUE,
7179 EL_AMOEBA_DROP, ACTION_FALLING, -1
7182 Ydrip_1_s, FALSE, FALSE,
7183 EL_AMOEBA_DROP, ACTION_FALLING, -1
7186 Ydrip_1_sB, FALSE, TRUE,
7187 EL_AMOEBA_DROP, ACTION_FALLING, -1
7190 Ydrip_2_s, FALSE, FALSE,
7191 EL_AMOEBA_DROP, ACTION_FALLING, -1
7194 Ydrip_2_sB, FALSE, TRUE,
7195 EL_AMOEBA_DROP, ACTION_FALLING, -1
7199 Xwonderwall, TRUE, FALSE,
7200 EL_MAGIC_WALL, -1, -1
7203 Ywonderwall, FALSE, FALSE,
7204 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7208 Xwheel, TRUE, FALSE,
7209 EL_ROBOT_WHEEL, -1, -1
7212 Ywheel, FALSE, FALSE,
7213 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7217 Xswitch, TRUE, FALSE,
7218 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7221 Yswitch, FALSE, FALSE,
7222 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7226 Xbumper, TRUE, FALSE,
7227 EL_EMC_SPRING_BUMPER, -1, -1
7230 Ybumper, FALSE, FALSE,
7231 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7235 Xacid_nw, TRUE, FALSE,
7236 EL_ACID_POOL_TOPLEFT, -1, -1
7239 Xacid_ne, TRUE, FALSE,
7240 EL_ACID_POOL_TOPRIGHT, -1, -1
7243 Xacid_sw, TRUE, FALSE,
7244 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7247 Xacid_s, TRUE, FALSE,
7248 EL_ACID_POOL_BOTTOM, -1, -1
7251 Xacid_se, TRUE, FALSE,
7252 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7256 Xfake_blank, TRUE, FALSE,
7257 EL_INVISIBLE_WALL, -1, -1
7260 Yfake_blank, FALSE, FALSE,
7261 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7265 Xfake_grass, TRUE, FALSE,
7266 EL_EMC_FAKE_GRASS, -1, -1
7269 Yfake_grass, FALSE, FALSE,
7270 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7274 Xfake_amoeba, TRUE, FALSE,
7275 EL_EMC_DRIPPER, -1, -1
7278 Yfake_amoeba, FALSE, FALSE,
7279 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7283 Xlenses, TRUE, FALSE,
7284 EL_EMC_LENSES, -1, -1
7288 Xmagnify, TRUE, FALSE,
7289 EL_EMC_MAGNIFIER, -1, -1
7294 EL_QUICKSAND_EMPTY, -1, -1
7297 Xsand_stone, TRUE, FALSE,
7298 EL_QUICKSAND_FULL, -1, -1
7301 Xsand_stonein_1, FALSE, TRUE,
7302 EL_ROCK, ACTION_FILLING, -1
7305 Xsand_stonein_2, FALSE, TRUE,
7306 EL_ROCK, ACTION_FILLING, -1
7309 Xsand_stonein_3, FALSE, TRUE,
7310 EL_ROCK, ACTION_FILLING, -1
7313 Xsand_stonein_4, FALSE, TRUE,
7314 EL_ROCK, ACTION_FILLING, -1
7317 Xsand_sandstone_1, FALSE, FALSE,
7318 EL_QUICKSAND_FILLING, -1, -1
7321 Xsand_sandstone_2, FALSE, FALSE,
7322 EL_QUICKSAND_FILLING, -1, -1
7325 Xsand_sandstone_3, FALSE, FALSE,
7326 EL_QUICKSAND_FILLING, -1, -1
7329 Xsand_sandstone_4, FALSE, FALSE,
7330 EL_QUICKSAND_FILLING, -1, -1
7333 Xsand_stonesand_1, FALSE, FALSE,
7334 EL_QUICKSAND_EMPTYING, -1, -1
7337 Xsand_stonesand_2, FALSE, FALSE,
7338 EL_QUICKSAND_EMPTYING, -1, -1
7341 Xsand_stonesand_3, FALSE, FALSE,
7342 EL_QUICKSAND_EMPTYING, -1, -1
7345 Xsand_stonesand_4, FALSE, FALSE,
7346 EL_QUICKSAND_EMPTYING, -1, -1
7349 Xsand_stoneout_1, FALSE, FALSE,
7350 EL_ROCK, ACTION_EMPTYING, -1
7353 Xsand_stoneout_2, FALSE, FALSE,
7354 EL_ROCK, ACTION_EMPTYING, -1
7357 Xsand_stonesand_quickout_1, FALSE, FALSE,
7358 EL_QUICKSAND_EMPTYING, -1, -1
7361 Xsand_stonesand_quickout_2, FALSE, FALSE,
7362 EL_QUICKSAND_EMPTYING, -1, -1
7366 Xslide_ns, TRUE, FALSE,
7367 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7370 Yslide_ns_blank, FALSE, FALSE,
7371 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7374 Xslide_ew, TRUE, FALSE,
7375 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7378 Yslide_ew_blank, FALSE, FALSE,
7379 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7383 Xwind_n, TRUE, FALSE,
7384 EL_BALLOON_SWITCH_UP, -1, -1
7387 Xwind_e, TRUE, FALSE,
7388 EL_BALLOON_SWITCH_RIGHT, -1, -1
7391 Xwind_s, TRUE, FALSE,
7392 EL_BALLOON_SWITCH_DOWN, -1, -1
7395 Xwind_w, TRUE, FALSE,
7396 EL_BALLOON_SWITCH_LEFT, -1, -1
7399 Xwind_any, TRUE, FALSE,
7400 EL_BALLOON_SWITCH_ANY, -1, -1
7403 Xwind_stop, TRUE, FALSE,
7404 EL_BALLOON_SWITCH_NONE, -1, -1
7409 EL_EM_EXIT_CLOSED, -1, -1
7412 Xexit_1, TRUE, FALSE,
7413 EL_EM_EXIT_OPEN, -1, -1
7416 Xexit_2, FALSE, FALSE,
7417 EL_EM_EXIT_OPEN, -1, -1
7420 Xexit_3, FALSE, FALSE,
7421 EL_EM_EXIT_OPEN, -1, -1
7425 Xpause, FALSE, FALSE,
7430 Xwall_1, TRUE, FALSE,
7434 Xwall_2, TRUE, FALSE,
7435 EL_EMC_WALL_14, -1, -1
7438 Xwall_3, TRUE, FALSE,
7439 EL_EMC_WALL_15, -1, -1
7442 Xwall_4, TRUE, FALSE,
7443 EL_EMC_WALL_16, -1, -1
7447 Xroundwall_1, TRUE, FALSE,
7448 EL_WALL_SLIPPERY, -1, -1
7451 Xroundwall_2, TRUE, FALSE,
7452 EL_EMC_WALL_SLIPPERY_2, -1, -1
7455 Xroundwall_3, TRUE, FALSE,
7456 EL_EMC_WALL_SLIPPERY_3, -1, -1
7459 Xroundwall_4, TRUE, FALSE,
7460 EL_EMC_WALL_SLIPPERY_4, -1, -1
7464 Xsteel_1, TRUE, FALSE,
7465 EL_STEELWALL, -1, -1
7468 Xsteel_2, TRUE, FALSE,
7469 EL_EMC_STEELWALL_2, -1, -1
7472 Xsteel_3, TRUE, FALSE,
7473 EL_EMC_STEELWALL_3, -1, -1
7476 Xsteel_4, TRUE, FALSE,
7477 EL_EMC_STEELWALL_4, -1, -1
7481 Xdecor_1, TRUE, FALSE,
7482 EL_EMC_WALL_8, -1, -1
7485 Xdecor_2, TRUE, FALSE,
7486 EL_EMC_WALL_6, -1, -1
7489 Xdecor_3, TRUE, FALSE,
7490 EL_EMC_WALL_4, -1, -1
7493 Xdecor_4, TRUE, FALSE,
7494 EL_EMC_WALL_7, -1, -1
7497 Xdecor_5, TRUE, FALSE,
7498 EL_EMC_WALL_5, -1, -1
7501 Xdecor_6, TRUE, FALSE,
7502 EL_EMC_WALL_9, -1, -1
7505 Xdecor_7, TRUE, FALSE,
7506 EL_EMC_WALL_10, -1, -1
7509 Xdecor_8, TRUE, FALSE,
7510 EL_EMC_WALL_1, -1, -1
7513 Xdecor_9, TRUE, FALSE,
7514 EL_EMC_WALL_2, -1, -1
7517 Xdecor_10, TRUE, FALSE,
7518 EL_EMC_WALL_3, -1, -1
7521 Xdecor_11, TRUE, FALSE,
7522 EL_EMC_WALL_11, -1, -1
7525 Xdecor_12, TRUE, FALSE,
7526 EL_EMC_WALL_12, -1, -1
7530 Xalpha_0, TRUE, FALSE,
7531 EL_CHAR('0'), -1, -1
7534 Xalpha_1, TRUE, FALSE,
7535 EL_CHAR('1'), -1, -1
7538 Xalpha_2, TRUE, FALSE,
7539 EL_CHAR('2'), -1, -1
7542 Xalpha_3, TRUE, FALSE,
7543 EL_CHAR('3'), -1, -1
7546 Xalpha_4, TRUE, FALSE,
7547 EL_CHAR('4'), -1, -1
7550 Xalpha_5, TRUE, FALSE,
7551 EL_CHAR('5'), -1, -1
7554 Xalpha_6, TRUE, FALSE,
7555 EL_CHAR('6'), -1, -1
7558 Xalpha_7, TRUE, FALSE,
7559 EL_CHAR('7'), -1, -1
7562 Xalpha_8, TRUE, FALSE,
7563 EL_CHAR('8'), -1, -1
7566 Xalpha_9, TRUE, FALSE,
7567 EL_CHAR('9'), -1, -1
7570 Xalpha_excla, TRUE, FALSE,
7571 EL_CHAR('!'), -1, -1
7574 Xalpha_apost, TRUE, FALSE,
7575 EL_CHAR('\''), -1, -1
7578 Xalpha_comma, TRUE, FALSE,
7579 EL_CHAR(','), -1, -1
7582 Xalpha_minus, TRUE, FALSE,
7583 EL_CHAR('-'), -1, -1
7586 Xalpha_perio, TRUE, FALSE,
7587 EL_CHAR('.'), -1, -1
7590 Xalpha_colon, TRUE, FALSE,
7591 EL_CHAR(':'), -1, -1
7594 Xalpha_quest, TRUE, FALSE,
7595 EL_CHAR('?'), -1, -1
7598 Xalpha_a, TRUE, FALSE,
7599 EL_CHAR('A'), -1, -1
7602 Xalpha_b, TRUE, FALSE,
7603 EL_CHAR('B'), -1, -1
7606 Xalpha_c, TRUE, FALSE,
7607 EL_CHAR('C'), -1, -1
7610 Xalpha_d, TRUE, FALSE,
7611 EL_CHAR('D'), -1, -1
7614 Xalpha_e, TRUE, FALSE,
7615 EL_CHAR('E'), -1, -1
7618 Xalpha_f, TRUE, FALSE,
7619 EL_CHAR('F'), -1, -1
7622 Xalpha_g, TRUE, FALSE,
7623 EL_CHAR('G'), -1, -1
7626 Xalpha_h, TRUE, FALSE,
7627 EL_CHAR('H'), -1, -1
7630 Xalpha_i, TRUE, FALSE,
7631 EL_CHAR('I'), -1, -1
7634 Xalpha_j, TRUE, FALSE,
7635 EL_CHAR('J'), -1, -1
7638 Xalpha_k, TRUE, FALSE,
7639 EL_CHAR('K'), -1, -1
7642 Xalpha_l, TRUE, FALSE,
7643 EL_CHAR('L'), -1, -1
7646 Xalpha_m, TRUE, FALSE,
7647 EL_CHAR('M'), -1, -1
7650 Xalpha_n, TRUE, FALSE,
7651 EL_CHAR('N'), -1, -1
7654 Xalpha_o, TRUE, FALSE,
7655 EL_CHAR('O'), -1, -1
7658 Xalpha_p, TRUE, FALSE,
7659 EL_CHAR('P'), -1, -1
7662 Xalpha_q, TRUE, FALSE,
7663 EL_CHAR('Q'), -1, -1
7666 Xalpha_r, TRUE, FALSE,
7667 EL_CHAR('R'), -1, -1
7670 Xalpha_s, TRUE, FALSE,
7671 EL_CHAR('S'), -1, -1
7674 Xalpha_t, TRUE, FALSE,
7675 EL_CHAR('T'), -1, -1
7678 Xalpha_u, TRUE, FALSE,
7679 EL_CHAR('U'), -1, -1
7682 Xalpha_v, TRUE, FALSE,
7683 EL_CHAR('V'), -1, -1
7686 Xalpha_w, TRUE, FALSE,
7687 EL_CHAR('W'), -1, -1
7690 Xalpha_x, TRUE, FALSE,
7691 EL_CHAR('X'), -1, -1
7694 Xalpha_y, TRUE, FALSE,
7695 EL_CHAR('Y'), -1, -1
7698 Xalpha_z, TRUE, FALSE,
7699 EL_CHAR('Z'), -1, -1
7702 Xalpha_arrow_e, TRUE, FALSE,
7703 EL_CHAR('>'), -1, -1
7706 Xalpha_arrow_w, TRUE, FALSE,
7707 EL_CHAR('<'), -1, -1
7710 Xalpha_copyr, TRUE, FALSE,
7711 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7715 Ykey_1_blank, FALSE, FALSE,
7716 EL_EM_KEY_1, ACTION_COLLECTING, -1
7719 Ykey_2_blank, FALSE, FALSE,
7720 EL_EM_KEY_2, ACTION_COLLECTING, -1
7723 Ykey_3_blank, FALSE, FALSE,
7724 EL_EM_KEY_3, ACTION_COLLECTING, -1
7727 Ykey_4_blank, FALSE, FALSE,
7728 EL_EM_KEY_4, ACTION_COLLECTING, -1
7731 Ykey_5_blank, FALSE, FALSE,
7732 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7735 Ykey_6_blank, FALSE, FALSE,
7736 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7739 Ykey_7_blank, FALSE, FALSE,
7740 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7743 Ykey_8_blank, FALSE, FALSE,
7744 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7747 Ylenses_blank, FALSE, FALSE,
7748 EL_EMC_LENSES, ACTION_COLLECTING, -1
7751 Ymagnify_blank, FALSE, FALSE,
7752 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7755 Ygrass_blank, FALSE, FALSE,
7756 EL_EMC_GRASS, ACTION_SNAPPING, -1
7759 Ydirt_blank, FALSE, FALSE,
7760 EL_SAND, ACTION_SNAPPING, -1
7769 static struct Mapping_EM_to_RND_player
7778 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7782 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7786 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7790 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7794 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7798 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7802 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7806 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7810 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7814 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7818 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7822 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7826 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7830 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7834 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7838 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7842 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7846 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7850 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7854 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7858 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7862 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7866 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7870 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7874 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7878 EL_PLAYER_1, ACTION_DEFAULT, -1,
7882 EL_PLAYER_2, ACTION_DEFAULT, -1,
7886 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7890 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7894 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7898 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7902 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7906 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7910 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7914 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7918 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7922 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7926 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7930 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7934 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7938 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7942 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7946 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7950 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7954 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7958 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7962 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7966 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7970 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7974 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7978 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7982 EL_PLAYER_3, ACTION_DEFAULT, -1,
7986 EL_PLAYER_4, ACTION_DEFAULT, -1,
7995 int map_element_RND_to_EM_cave(int element_rnd)
7997 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7998 static boolean mapping_initialized = FALSE;
8000 if (!mapping_initialized)
8004 // return "Xalpha_quest" for all undefined elements in mapping array
8005 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8006 mapping_RND_to_EM[i] = Xalpha_quest;
8008 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8009 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
8010 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
8011 em_object_mapping_list[i].element_em;
8013 mapping_initialized = TRUE;
8016 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
8018 Warn("invalid RND level element %d", element_rnd);
8023 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
8026 int map_element_EM_to_RND_cave(int element_em_cave)
8028 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8029 static boolean mapping_initialized = FALSE;
8031 if (!mapping_initialized)
8035 // return "EL_UNKNOWN" for all undefined elements in mapping array
8036 for (i = 0; i < GAME_TILE_MAX; i++)
8037 mapping_EM_to_RND[i] = EL_UNKNOWN;
8039 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8040 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8041 em_object_mapping_list[i].element_rnd;
8043 mapping_initialized = TRUE;
8046 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8048 Warn("invalid EM cave element %d", element_em_cave);
8053 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8056 int map_element_EM_to_RND_game(int element_em_game)
8058 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8059 static boolean mapping_initialized = FALSE;
8061 if (!mapping_initialized)
8065 // return "EL_UNKNOWN" for all undefined elements in mapping array
8066 for (i = 0; i < GAME_TILE_MAX; i++)
8067 mapping_EM_to_RND[i] = EL_UNKNOWN;
8069 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8070 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8071 em_object_mapping_list[i].element_rnd;
8073 mapping_initialized = TRUE;
8076 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8078 Warn("invalid EM game element %d", element_em_game);
8083 return mapping_EM_to_RND[element_em_game];
8086 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8088 struct LevelInfo_EM *level_em = level->native_em_level;
8089 struct CAVE *cav = level_em->cav;
8092 for (i = 0; i < GAME_TILE_MAX; i++)
8093 cav->android_array[i] = Cblank;
8095 for (i = 0; i < level->num_android_clone_elements; i++)
8097 int element_rnd = level->android_clone_element[i];
8098 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8100 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8101 if (em_object_mapping_list[j].element_rnd == element_rnd)
8102 cav->android_array[em_object_mapping_list[j].element_em] =
8107 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8109 struct LevelInfo_EM *level_em = level->native_em_level;
8110 struct CAVE *cav = level_em->cav;
8113 level->num_android_clone_elements = 0;
8115 for (i = 0; i < GAME_TILE_MAX; i++)
8117 int element_em_cave = cav->android_array[i];
8119 boolean element_found = FALSE;
8121 if (element_em_cave == Cblank)
8124 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8126 for (j = 0; j < level->num_android_clone_elements; j++)
8127 if (level->android_clone_element[j] == element_rnd)
8128 element_found = TRUE;
8132 level->android_clone_element[level->num_android_clone_elements++] =
8135 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8140 if (level->num_android_clone_elements == 0)
8142 level->num_android_clone_elements = 1;
8143 level->android_clone_element[0] = EL_EMPTY;
8147 int map_direction_RND_to_EM(int direction)
8149 return (direction == MV_UP ? 0 :
8150 direction == MV_RIGHT ? 1 :
8151 direction == MV_DOWN ? 2 :
8152 direction == MV_LEFT ? 3 :
8156 int map_direction_EM_to_RND(int direction)
8158 return (direction == 0 ? MV_UP :
8159 direction == 1 ? MV_RIGHT :
8160 direction == 2 ? MV_DOWN :
8161 direction == 3 ? MV_LEFT :
8165 int map_element_RND_to_SP(int element_rnd)
8167 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8169 if (element_rnd >= EL_SP_START &&
8170 element_rnd <= EL_SP_END)
8171 element_sp = element_rnd - EL_SP_START;
8172 else if (element_rnd == EL_EMPTY_SPACE)
8174 else if (element_rnd == EL_INVISIBLE_WALL)
8180 int map_element_SP_to_RND(int element_sp)
8182 int element_rnd = EL_UNKNOWN;
8184 if (element_sp >= 0x00 &&
8186 element_rnd = EL_SP_START + element_sp;
8187 else if (element_sp == 0x28)
8188 element_rnd = EL_INVISIBLE_WALL;
8193 int map_action_SP_to_RND(int action_sp)
8197 case actActive: return ACTION_ACTIVE;
8198 case actImpact: return ACTION_IMPACT;
8199 case actExploding: return ACTION_EXPLODING;
8200 case actDigging: return ACTION_DIGGING;
8201 case actSnapping: return ACTION_SNAPPING;
8202 case actCollecting: return ACTION_COLLECTING;
8203 case actPassing: return ACTION_PASSING;
8204 case actPushing: return ACTION_PUSHING;
8205 case actDropping: return ACTION_DROPPING;
8207 default: return ACTION_DEFAULT;
8211 int map_element_RND_to_MM(int element_rnd)
8213 return (element_rnd >= EL_MM_START_1 &&
8214 element_rnd <= EL_MM_END_1 ?
8215 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8217 element_rnd >= EL_MM_START_2 &&
8218 element_rnd <= EL_MM_END_2 ?
8219 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8221 element_rnd >= EL_MM_START_3 &&
8222 element_rnd <= EL_MM_END_3 ?
8223 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
8225 element_rnd >= EL_CHAR_START &&
8226 element_rnd <= EL_CHAR_END ?
8227 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8229 element_rnd >= EL_MM_RUNTIME_START &&
8230 element_rnd <= EL_MM_RUNTIME_END ?
8231 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8233 EL_MM_EMPTY_NATIVE);
8236 int map_element_MM_to_RND(int element_mm)
8238 return (element_mm == EL_MM_EMPTY_NATIVE ||
8239 element_mm == EL_DF_EMPTY_NATIVE ?
8242 element_mm >= EL_MM_START_1_NATIVE &&
8243 element_mm <= EL_MM_END_1_NATIVE ?
8244 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8246 element_mm >= EL_MM_START_2_NATIVE &&
8247 element_mm <= EL_MM_END_2_NATIVE ?
8248 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8250 element_mm >= EL_MM_START_3_NATIVE &&
8251 element_mm <= EL_MM_END_3_NATIVE ?
8252 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
8254 element_mm >= EL_MM_CHAR_START_NATIVE &&
8255 element_mm <= EL_MM_CHAR_END_NATIVE ?
8256 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8258 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8259 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8260 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8265 int map_action_MM_to_RND(int action_mm)
8267 // all MM actions are defined to exactly match their RND counterparts
8271 int map_sound_MM_to_RND(int sound_mm)
8275 case SND_MM_GAME_LEVELTIME_CHARGING:
8276 return SND_GAME_LEVELTIME_CHARGING;
8278 case SND_MM_GAME_HEALTH_CHARGING:
8279 return SND_GAME_HEALTH_CHARGING;
8282 return SND_UNDEFINED;
8286 int map_mm_wall_element(int element)
8288 return (element >= EL_MM_STEEL_WALL_START &&
8289 element <= EL_MM_STEEL_WALL_END ?
8292 element >= EL_MM_WOODEN_WALL_START &&
8293 element <= EL_MM_WOODEN_WALL_END ?
8296 element >= EL_MM_ICE_WALL_START &&
8297 element <= EL_MM_ICE_WALL_END ?
8300 element >= EL_MM_AMOEBA_WALL_START &&
8301 element <= EL_MM_AMOEBA_WALL_END ?
8304 element >= EL_DF_STEEL_WALL_START &&
8305 element <= EL_DF_STEEL_WALL_END ?
8308 element >= EL_DF_WOODEN_WALL_START &&
8309 element <= EL_DF_WOODEN_WALL_END ?
8315 int map_mm_wall_element_editor(int element)
8319 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8320 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8321 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8322 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8323 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8324 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8326 default: return element;
8330 int get_next_element(int element)
8334 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8335 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8336 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8337 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8338 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8339 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8340 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8341 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8342 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8343 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8344 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8346 default: return element;
8350 int el2img_mm(int element_mm)
8352 return el2img(map_element_MM_to_RND(element_mm));
8355 int el_act2img_mm(int element_mm, int action)
8357 return el_act2img(map_element_MM_to_RND(element_mm), action);
8360 int el_act_dir2img(int element, int action, int direction)
8362 element = GFX_ELEMENT(element);
8363 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8365 // direction_graphic[][] == graphic[] for undefined direction graphics
8366 return element_info[element].direction_graphic[action][direction];
8369 static int el_act_dir2crm(int element, int action, int direction)
8371 element = GFX_ELEMENT(element);
8372 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8374 // direction_graphic[][] == graphic[] for undefined direction graphics
8375 return element_info[element].direction_crumbled[action][direction];
8378 int el_act2img(int element, int action)
8380 element = GFX_ELEMENT(element);
8382 return element_info[element].graphic[action];
8385 int el_act2crm(int element, int action)
8387 element = GFX_ELEMENT(element);
8389 return element_info[element].crumbled[action];
8392 int el_dir2img(int element, int direction)
8394 element = GFX_ELEMENT(element);
8396 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8399 int el2baseimg(int element)
8401 return element_info[element].graphic[ACTION_DEFAULT];
8404 int el2img(int element)
8406 element = GFX_ELEMENT(element);
8408 return element_info[element].graphic[ACTION_DEFAULT];
8411 int el2edimg(int element)
8413 element = GFX_ELEMENT(element);
8415 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8418 int el2preimg(int element)
8420 element = GFX_ELEMENT(element);
8422 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8425 int el2panelimg(int element)
8427 element = GFX_ELEMENT(element);
8429 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8432 int font2baseimg(int font_nr)
8434 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8437 int getBeltNrFromBeltElement(int element)
8439 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8440 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8441 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8444 int getBeltNrFromBeltActiveElement(int element)
8446 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8447 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8448 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8451 int getBeltNrFromBeltSwitchElement(int element)
8453 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8454 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8455 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8458 int getBeltDirNrFromBeltElement(int element)
8460 static int belt_base_element[4] =
8462 EL_CONVEYOR_BELT_1_LEFT,
8463 EL_CONVEYOR_BELT_2_LEFT,
8464 EL_CONVEYOR_BELT_3_LEFT,
8465 EL_CONVEYOR_BELT_4_LEFT
8468 int belt_nr = getBeltNrFromBeltElement(element);
8469 int belt_dir_nr = element - belt_base_element[belt_nr];
8471 return (belt_dir_nr % 3);
8474 int getBeltDirNrFromBeltSwitchElement(int element)
8476 static int belt_base_element[4] =
8478 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8479 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8480 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8481 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8484 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8485 int belt_dir_nr = element - belt_base_element[belt_nr];
8487 return (belt_dir_nr % 3);
8490 int getBeltDirFromBeltElement(int element)
8492 static int belt_move_dir[3] =
8499 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8501 return belt_move_dir[belt_dir_nr];
8504 int getBeltDirFromBeltSwitchElement(int element)
8506 static int belt_move_dir[3] =
8513 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8515 return belt_move_dir[belt_dir_nr];
8518 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8520 static int belt_base_element[4] =
8522 EL_CONVEYOR_BELT_1_LEFT,
8523 EL_CONVEYOR_BELT_2_LEFT,
8524 EL_CONVEYOR_BELT_3_LEFT,
8525 EL_CONVEYOR_BELT_4_LEFT
8528 return belt_base_element[belt_nr] + belt_dir_nr;
8531 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8533 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8535 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8538 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8540 static int belt_base_element[4] =
8542 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8543 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8544 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8545 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8548 return belt_base_element[belt_nr] + belt_dir_nr;
8551 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8553 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8555 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8558 boolean swapTiles_EM(boolean is_pre_emc_cave)
8560 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8563 boolean getTeamMode_EM(void)
8565 return game.team_mode || network_playing;
8568 boolean isActivePlayer_EM(int player_nr)
8570 return stored_player[player_nr].active;
8573 unsigned int InitRND(int seed)
8575 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8576 return InitEngineRandom_EM(seed);
8577 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8578 return InitEngineRandom_SP(seed);
8579 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8580 return InitEngineRandom_MM(seed);
8582 return InitEngineRandom_RND(seed);
8585 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8586 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8588 static int get_effective_element_EM(int tile, int frame_em)
8590 int element = object_mapping[tile].element_rnd;
8591 int action = object_mapping[tile].action;
8592 boolean is_backside = object_mapping[tile].is_backside;
8593 boolean action_removing = (action == ACTION_DIGGING ||
8594 action == ACTION_SNAPPING ||
8595 action == ACTION_COLLECTING);
8603 return (frame_em > 5 ? EL_EMPTY : element);
8609 else // frame_em == 7
8620 case Ydiamond_stone:
8624 case Xdrip_stretchB:
8640 case Ymagnify_blank:
8643 case Xsand_stonein_1:
8644 case Xsand_stonein_2:
8645 case Xsand_stonein_3:
8646 case Xsand_stonein_4:
8650 return (is_backside || action_removing ? EL_EMPTY : element);
8655 static boolean check_linear_animation_EM(int tile)
8659 case Xsand_stonesand_1:
8660 case Xsand_stonesand_quickout_1:
8661 case Xsand_sandstone_1:
8662 case Xsand_stonein_1:
8663 case Xsand_stoneout_1:
8691 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8692 boolean has_crumbled_graphics,
8693 int crumbled, int sync_frame)
8695 // if element can be crumbled, but certain action graphics are just empty
8696 // space (like instantly snapping sand to empty space in 1 frame), do not
8697 // treat these empty space graphics as crumbled graphics in EMC engine
8698 if (crumbled == IMG_EMPTY_SPACE)
8699 has_crumbled_graphics = FALSE;
8701 if (has_crumbled_graphics)
8703 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8704 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8705 g_crumbled->anim_delay,
8706 g_crumbled->anim_mode,
8707 g_crumbled->anim_start_frame,
8710 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8711 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8713 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8714 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8716 g_em->has_crumbled_graphics = TRUE;
8720 g_em->crumbled_bitmap = NULL;
8721 g_em->crumbled_src_x = 0;
8722 g_em->crumbled_src_y = 0;
8723 g_em->crumbled_border_size = 0;
8724 g_em->crumbled_tile_size = 0;
8726 g_em->has_crumbled_graphics = FALSE;
8731 void ResetGfxAnimation_EM(int x, int y, int tile)
8737 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8738 int tile, int frame_em, int x, int y)
8740 int action = object_mapping[tile].action;
8741 int direction = object_mapping[tile].direction;
8742 int effective_element = get_effective_element_EM(tile, frame_em);
8743 int graphic = (direction == MV_NONE ?
8744 el_act2img(effective_element, action) :
8745 el_act_dir2img(effective_element, action, direction));
8746 struct GraphicInfo *g = &graphic_info[graphic];
8748 boolean action_removing = (action == ACTION_DIGGING ||
8749 action == ACTION_SNAPPING ||
8750 action == ACTION_COLLECTING);
8751 boolean action_moving = (action == ACTION_FALLING ||
8752 action == ACTION_MOVING ||
8753 action == ACTION_PUSHING ||
8754 action == ACTION_EATING ||
8755 action == ACTION_FILLING ||
8756 action == ACTION_EMPTYING);
8757 boolean action_falling = (action == ACTION_FALLING ||
8758 action == ACTION_FILLING ||
8759 action == ACTION_EMPTYING);
8761 // special case: graphic uses "2nd movement tile" and has defined
8762 // 7 frames for movement animation (or less) => use default graphic
8763 // for last (8th) frame which ends the movement animation
8764 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8766 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8767 graphic = (direction == MV_NONE ?
8768 el_act2img(effective_element, action) :
8769 el_act_dir2img(effective_element, action, direction));
8771 g = &graphic_info[graphic];
8774 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8778 else if (action_moving)
8780 boolean is_backside = object_mapping[tile].is_backside;
8784 int direction = object_mapping[tile].direction;
8785 int move_dir = (action_falling ? MV_DOWN : direction);
8790 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8791 if (g->double_movement && frame_em == 0)
8795 if (move_dir == MV_LEFT)
8796 GfxFrame[x - 1][y] = GfxFrame[x][y];
8797 else if (move_dir == MV_RIGHT)
8798 GfxFrame[x + 1][y] = GfxFrame[x][y];
8799 else if (move_dir == MV_UP)
8800 GfxFrame[x][y - 1] = GfxFrame[x][y];
8801 else if (move_dir == MV_DOWN)
8802 GfxFrame[x][y + 1] = GfxFrame[x][y];
8809 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8810 if (tile == Xsand_stonesand_quickout_1 ||
8811 tile == Xsand_stonesand_quickout_2)
8815 if (graphic_info[graphic].anim_global_sync)
8816 sync_frame = FrameCounter;
8817 else if (graphic_info[graphic].anim_global_anim_sync)
8818 sync_frame = getGlobalAnimSyncFrame();
8819 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8820 sync_frame = GfxFrame[x][y];
8822 sync_frame = 0; // playfield border (pseudo steel)
8824 SetRandomAnimationValue(x, y);
8826 int frame = getAnimationFrame(g->anim_frames,
8829 g->anim_start_frame,
8832 g_em->unique_identifier =
8833 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8836 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8837 int tile, int frame_em, int x, int y)
8839 int action = object_mapping[tile].action;
8840 int direction = object_mapping[tile].direction;
8841 boolean is_backside = object_mapping[tile].is_backside;
8842 int effective_element = get_effective_element_EM(tile, frame_em);
8843 int effective_action = action;
8844 int graphic = (direction == MV_NONE ?
8845 el_act2img(effective_element, effective_action) :
8846 el_act_dir2img(effective_element, effective_action,
8848 int crumbled = (direction == MV_NONE ?
8849 el_act2crm(effective_element, effective_action) :
8850 el_act_dir2crm(effective_element, effective_action,
8852 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8853 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8854 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8855 struct GraphicInfo *g = &graphic_info[graphic];
8858 // special case: graphic uses "2nd movement tile" and has defined
8859 // 7 frames for movement animation (or less) => use default graphic
8860 // for last (8th) frame which ends the movement animation
8861 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8863 effective_action = ACTION_DEFAULT;
8864 graphic = (direction == MV_NONE ?
8865 el_act2img(effective_element, effective_action) :
8866 el_act_dir2img(effective_element, effective_action,
8868 crumbled = (direction == MV_NONE ?
8869 el_act2crm(effective_element, effective_action) :
8870 el_act_dir2crm(effective_element, effective_action,
8873 g = &graphic_info[graphic];
8876 if (graphic_info[graphic].anim_global_sync)
8877 sync_frame = FrameCounter;
8878 else if (graphic_info[graphic].anim_global_anim_sync)
8879 sync_frame = getGlobalAnimSyncFrame();
8880 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8881 sync_frame = GfxFrame[x][y];
8883 sync_frame = 0; // playfield border (pseudo steel)
8885 SetRandomAnimationValue(x, y);
8887 int frame = getAnimationFrame(g->anim_frames,
8890 g->anim_start_frame,
8893 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8894 g->double_movement && is_backside);
8896 // (updating the "crumbled" graphic definitions is probably not really needed,
8897 // as animations for crumbled graphics can't be longer than one EMC cycle)
8898 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8902 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8903 int player_nr, int anim, int frame_em)
8905 int element = player_mapping[player_nr][anim].element_rnd;
8906 int action = player_mapping[player_nr][anim].action;
8907 int direction = player_mapping[player_nr][anim].direction;
8908 int graphic = (direction == MV_NONE ?
8909 el_act2img(element, action) :
8910 el_act_dir2img(element, action, direction));
8911 struct GraphicInfo *g = &graphic_info[graphic];
8914 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8916 stored_player[player_nr].StepFrame = frame_em;
8918 sync_frame = stored_player[player_nr].Frame;
8920 int frame = getAnimationFrame(g->anim_frames,
8923 g->anim_start_frame,
8926 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8927 &g_em->src_x, &g_em->src_y, FALSE);
8930 void InitGraphicInfo_EM(void)
8934 // always start with reliable default values
8935 for (i = 0; i < GAME_TILE_MAX; i++)
8937 object_mapping[i].element_rnd = EL_UNKNOWN;
8938 object_mapping[i].is_backside = FALSE;
8939 object_mapping[i].action = ACTION_DEFAULT;
8940 object_mapping[i].direction = MV_NONE;
8943 // always start with reliable default values
8944 for (p = 0; p < MAX_PLAYERS; p++)
8946 for (i = 0; i < PLY_MAX; i++)
8948 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8949 player_mapping[p][i].action = ACTION_DEFAULT;
8950 player_mapping[p][i].direction = MV_NONE;
8954 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8956 int e = em_object_mapping_list[i].element_em;
8958 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8959 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8961 if (em_object_mapping_list[i].action != -1)
8962 object_mapping[e].action = em_object_mapping_list[i].action;
8964 if (em_object_mapping_list[i].direction != -1)
8965 object_mapping[e].direction =
8966 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8969 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8971 int a = em_player_mapping_list[i].action_em;
8972 int p = em_player_mapping_list[i].player_nr;
8974 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8976 if (em_player_mapping_list[i].action != -1)
8977 player_mapping[p][a].action = em_player_mapping_list[i].action;
8979 if (em_player_mapping_list[i].direction != -1)
8980 player_mapping[p][a].direction =
8981 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8984 for (i = 0; i < GAME_TILE_MAX; i++)
8986 int element = object_mapping[i].element_rnd;
8987 int action = object_mapping[i].action;
8988 int direction = object_mapping[i].direction;
8989 boolean is_backside = object_mapping[i].is_backside;
8990 boolean action_exploding = ((action == ACTION_EXPLODING ||
8991 action == ACTION_SMASHED_BY_ROCK ||
8992 action == ACTION_SMASHED_BY_SPRING) &&
8993 element != EL_DIAMOND);
8994 boolean action_active = (action == ACTION_ACTIVE);
8995 boolean action_other = (action == ACTION_OTHER);
8997 for (j = 0; j < 8; j++)
8999 int effective_element = get_effective_element_EM(i, j);
9000 int effective_action = (j < 7 ? action :
9001 i == Xdrip_stretch ? action :
9002 i == Xdrip_stretchB ? action :
9003 i == Ydrip_1_s ? action :
9004 i == Ydrip_1_sB ? action :
9005 i == Yball_1 ? action :
9006 i == Xball_2 ? action :
9007 i == Yball_2 ? action :
9008 i == Yball_blank ? action :
9009 i == Ykey_1_blank ? action :
9010 i == Ykey_2_blank ? action :
9011 i == Ykey_3_blank ? action :
9012 i == Ykey_4_blank ? action :
9013 i == Ykey_5_blank ? action :
9014 i == Ykey_6_blank ? action :
9015 i == Ykey_7_blank ? action :
9016 i == Ykey_8_blank ? action :
9017 i == Ylenses_blank ? action :
9018 i == Ymagnify_blank ? action :
9019 i == Ygrass_blank ? action :
9020 i == Ydirt_blank ? action :
9021 i == Xsand_stonein_1 ? action :
9022 i == Xsand_stonein_2 ? action :
9023 i == Xsand_stonein_3 ? action :
9024 i == Xsand_stonein_4 ? action :
9025 i == Xsand_stoneout_1 ? action :
9026 i == Xsand_stoneout_2 ? action :
9027 i == Xboom_android ? ACTION_EXPLODING :
9028 action_exploding ? ACTION_EXPLODING :
9029 action_active ? action :
9030 action_other ? action :
9032 int graphic = (el_act_dir2img(effective_element, effective_action,
9034 int crumbled = (el_act_dir2crm(effective_element, effective_action,
9036 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
9037 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
9038 boolean has_action_graphics = (graphic != base_graphic);
9039 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9040 struct GraphicInfo *g = &graphic_info[graphic];
9041 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9044 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9045 boolean special_animation = (action != ACTION_DEFAULT &&
9046 g->anim_frames == 3 &&
9047 g->anim_delay == 2 &&
9048 g->anim_mode & ANIM_LINEAR);
9049 int sync_frame = (i == Xdrip_stretch ? 7 :
9050 i == Xdrip_stretchB ? 7 :
9051 i == Ydrip_2_s ? j + 8 :
9052 i == Ydrip_2_sB ? j + 8 :
9061 i == Xfake_acid_1 ? 0 :
9062 i == Xfake_acid_2 ? 10 :
9063 i == Xfake_acid_3 ? 20 :
9064 i == Xfake_acid_4 ? 30 :
9065 i == Xfake_acid_5 ? 40 :
9066 i == Xfake_acid_6 ? 50 :
9067 i == Xfake_acid_7 ? 60 :
9068 i == Xfake_acid_8 ? 70 :
9069 i == Xfake_acid_1_player ? 0 :
9070 i == Xfake_acid_2_player ? 10 :
9071 i == Xfake_acid_3_player ? 20 :
9072 i == Xfake_acid_4_player ? 30 :
9073 i == Xfake_acid_5_player ? 40 :
9074 i == Xfake_acid_6_player ? 50 :
9075 i == Xfake_acid_7_player ? 60 :
9076 i == Xfake_acid_8_player ? 70 :
9078 i == Yball_2 ? j + 8 :
9079 i == Yball_blank ? j + 1 :
9080 i == Ykey_1_blank ? j + 1 :
9081 i == Ykey_2_blank ? j + 1 :
9082 i == Ykey_3_blank ? j + 1 :
9083 i == Ykey_4_blank ? j + 1 :
9084 i == Ykey_5_blank ? j + 1 :
9085 i == Ykey_6_blank ? j + 1 :
9086 i == Ykey_7_blank ? j + 1 :
9087 i == Ykey_8_blank ? j + 1 :
9088 i == Ylenses_blank ? j + 1 :
9089 i == Ymagnify_blank ? j + 1 :
9090 i == Ygrass_blank ? j + 1 :
9091 i == Ydirt_blank ? j + 1 :
9092 i == Xamoeba_1 ? 0 :
9093 i == Xamoeba_2 ? 1 :
9094 i == Xamoeba_3 ? 2 :
9095 i == Xamoeba_4 ? 3 :
9096 i == Xamoeba_5 ? 0 :
9097 i == Xamoeba_6 ? 1 :
9098 i == Xamoeba_7 ? 2 :
9099 i == Xamoeba_8 ? 3 :
9100 i == Xexit_2 ? j + 8 :
9101 i == Xexit_3 ? j + 16 :
9102 i == Xdynamite_1 ? 0 :
9103 i == Xdynamite_2 ? 8 :
9104 i == Xdynamite_3 ? 16 :
9105 i == Xdynamite_4 ? 24 :
9106 i == Xsand_stonein_1 ? j + 1 :
9107 i == Xsand_stonein_2 ? j + 9 :
9108 i == Xsand_stonein_3 ? j + 17 :
9109 i == Xsand_stonein_4 ? j + 25 :
9110 i == Xsand_stoneout_1 && j == 0 ? 0 :
9111 i == Xsand_stoneout_1 && j == 1 ? 0 :
9112 i == Xsand_stoneout_1 && j == 2 ? 1 :
9113 i == Xsand_stoneout_1 && j == 3 ? 2 :
9114 i == Xsand_stoneout_1 && j == 4 ? 2 :
9115 i == Xsand_stoneout_1 && j == 5 ? 3 :
9116 i == Xsand_stoneout_1 && j == 6 ? 4 :
9117 i == Xsand_stoneout_1 && j == 7 ? 4 :
9118 i == Xsand_stoneout_2 && j == 0 ? 5 :
9119 i == Xsand_stoneout_2 && j == 1 ? 6 :
9120 i == Xsand_stoneout_2 && j == 2 ? 7 :
9121 i == Xsand_stoneout_2 && j == 3 ? 8 :
9122 i == Xsand_stoneout_2 && j == 4 ? 9 :
9123 i == Xsand_stoneout_2 && j == 5 ? 11 :
9124 i == Xsand_stoneout_2 && j == 6 ? 13 :
9125 i == Xsand_stoneout_2 && j == 7 ? 15 :
9126 i == Xboom_bug && j == 1 ? 2 :
9127 i == Xboom_bug && j == 2 ? 2 :
9128 i == Xboom_bug && j == 3 ? 4 :
9129 i == Xboom_bug && j == 4 ? 4 :
9130 i == Xboom_bug && j == 5 ? 2 :
9131 i == Xboom_bug && j == 6 ? 2 :
9132 i == Xboom_bug && j == 7 ? 0 :
9133 i == Xboom_tank && j == 1 ? 2 :
9134 i == Xboom_tank && j == 2 ? 2 :
9135 i == Xboom_tank && j == 3 ? 4 :
9136 i == Xboom_tank && j == 4 ? 4 :
9137 i == Xboom_tank && j == 5 ? 2 :
9138 i == Xboom_tank && j == 6 ? 2 :
9139 i == Xboom_tank && j == 7 ? 0 :
9140 i == Xboom_android && j == 7 ? 6 :
9141 i == Xboom_1 && j == 1 ? 2 :
9142 i == Xboom_1 && j == 2 ? 2 :
9143 i == Xboom_1 && j == 3 ? 4 :
9144 i == Xboom_1 && j == 4 ? 4 :
9145 i == Xboom_1 && j == 5 ? 6 :
9146 i == Xboom_1 && j == 6 ? 6 :
9147 i == Xboom_1 && j == 7 ? 8 :
9148 i == Xboom_2 && j == 0 ? 8 :
9149 i == Xboom_2 && j == 1 ? 8 :
9150 i == Xboom_2 && j == 2 ? 10 :
9151 i == Xboom_2 && j == 3 ? 10 :
9152 i == Xboom_2 && j == 4 ? 10 :
9153 i == Xboom_2 && j == 5 ? 12 :
9154 i == Xboom_2 && j == 6 ? 12 :
9155 i == Xboom_2 && j == 7 ? 12 :
9156 special_animation && j == 4 ? 3 :
9157 effective_action != action ? 0 :
9159 int frame = getAnimationFrame(g->anim_frames,
9162 g->anim_start_frame,
9165 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9166 g->double_movement && is_backside);
9168 g_em->bitmap = src_bitmap;
9169 g_em->src_x = src_x;
9170 g_em->src_y = src_y;
9171 g_em->src_offset_x = 0;
9172 g_em->src_offset_y = 0;
9173 g_em->dst_offset_x = 0;
9174 g_em->dst_offset_y = 0;
9175 g_em->width = TILEX;
9176 g_em->height = TILEY;
9178 g_em->preserve_background = FALSE;
9180 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9183 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9184 effective_action == ACTION_MOVING ||
9185 effective_action == ACTION_PUSHING ||
9186 effective_action == ACTION_EATING)) ||
9187 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9188 effective_action == ACTION_EMPTYING)))
9191 (effective_action == ACTION_FALLING ||
9192 effective_action == ACTION_FILLING ||
9193 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9194 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9195 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9196 int num_steps = (i == Ydrip_1_s ? 16 :
9197 i == Ydrip_1_sB ? 16 :
9198 i == Ydrip_2_s ? 16 :
9199 i == Ydrip_2_sB ? 16 :
9200 i == Xsand_stonein_1 ? 32 :
9201 i == Xsand_stonein_2 ? 32 :
9202 i == Xsand_stonein_3 ? 32 :
9203 i == Xsand_stonein_4 ? 32 :
9204 i == Xsand_stoneout_1 ? 16 :
9205 i == Xsand_stoneout_2 ? 16 : 8);
9206 int cx = ABS(dx) * (TILEX / num_steps);
9207 int cy = ABS(dy) * (TILEY / num_steps);
9208 int step_frame = (i == Ydrip_2_s ? j + 8 :
9209 i == Ydrip_2_sB ? j + 8 :
9210 i == Xsand_stonein_2 ? j + 8 :
9211 i == Xsand_stonein_3 ? j + 16 :
9212 i == Xsand_stonein_4 ? j + 24 :
9213 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9214 int step = (is_backside ? step_frame : num_steps - step_frame);
9216 if (is_backside) // tile where movement starts
9218 if (dx < 0 || dy < 0)
9220 g_em->src_offset_x = cx * step;
9221 g_em->src_offset_y = cy * step;
9225 g_em->dst_offset_x = cx * step;
9226 g_em->dst_offset_y = cy * step;
9229 else // tile where movement ends
9231 if (dx < 0 || dy < 0)
9233 g_em->dst_offset_x = cx * step;
9234 g_em->dst_offset_y = cy * step;
9238 g_em->src_offset_x = cx * step;
9239 g_em->src_offset_y = cy * step;
9243 g_em->width = TILEX - cx * step;
9244 g_em->height = TILEY - cy * step;
9247 // create unique graphic identifier to decide if tile must be redrawn
9248 /* bit 31 - 16 (16 bit): EM style graphic
9249 bit 15 - 12 ( 4 bit): EM style frame
9250 bit 11 - 6 ( 6 bit): graphic width
9251 bit 5 - 0 ( 6 bit): graphic height */
9252 g_em->unique_identifier =
9253 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9257 for (i = 0; i < GAME_TILE_MAX; i++)
9259 for (j = 0; j < 8; j++)
9261 int element = object_mapping[i].element_rnd;
9262 int action = object_mapping[i].action;
9263 int direction = object_mapping[i].direction;
9264 boolean is_backside = object_mapping[i].is_backside;
9265 int graphic_action = el_act_dir2img(element, action, direction);
9266 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9268 if ((action == ACTION_SMASHED_BY_ROCK ||
9269 action == ACTION_SMASHED_BY_SPRING ||
9270 action == ACTION_EATING) &&
9271 graphic_action == graphic_default)
9273 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9274 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9275 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9276 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9279 // no separate animation for "smashed by rock" -- use rock instead
9280 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9281 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9283 g_em->bitmap = g_xx->bitmap;
9284 g_em->src_x = g_xx->src_x;
9285 g_em->src_y = g_xx->src_y;
9286 g_em->src_offset_x = g_xx->src_offset_x;
9287 g_em->src_offset_y = g_xx->src_offset_y;
9288 g_em->dst_offset_x = g_xx->dst_offset_x;
9289 g_em->dst_offset_y = g_xx->dst_offset_y;
9290 g_em->width = g_xx->width;
9291 g_em->height = g_xx->height;
9292 g_em->unique_identifier = g_xx->unique_identifier;
9295 g_em->preserve_background = TRUE;
9300 for (p = 0; p < MAX_PLAYERS; p++)
9302 for (i = 0; i < PLY_MAX; i++)
9304 int element = player_mapping[p][i].element_rnd;
9305 int action = player_mapping[p][i].action;
9306 int direction = player_mapping[p][i].direction;
9308 for (j = 0; j < 8; j++)
9310 int effective_element = element;
9311 int effective_action = action;
9312 int graphic = (direction == MV_NONE ?
9313 el_act2img(effective_element, effective_action) :
9314 el_act_dir2img(effective_element, effective_action,
9316 struct GraphicInfo *g = &graphic_info[graphic];
9317 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9321 int frame = getAnimationFrame(g->anim_frames,
9324 g->anim_start_frame,
9327 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9329 g_em->bitmap = src_bitmap;
9330 g_em->src_x = src_x;
9331 g_em->src_y = src_y;
9332 g_em->src_offset_x = 0;
9333 g_em->src_offset_y = 0;
9334 g_em->dst_offset_x = 0;
9335 g_em->dst_offset_y = 0;
9336 g_em->width = TILEX;
9337 g_em->height = TILEY;
9343 static void CheckSaveEngineSnapshot_EM(int frame,
9344 boolean any_player_moving,
9345 boolean any_player_snapping,
9346 boolean any_player_dropping)
9348 if (frame == 7 && !any_player_dropping)
9350 if (!local_player->was_waiting)
9352 if (!CheckSaveEngineSnapshotToList())
9355 local_player->was_waiting = TRUE;
9358 else if (any_player_moving || any_player_snapping || any_player_dropping)
9360 local_player->was_waiting = FALSE;
9364 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9365 boolean murphy_is_dropping)
9367 if (murphy_is_waiting)
9369 if (!local_player->was_waiting)
9371 if (!CheckSaveEngineSnapshotToList())
9374 local_player->was_waiting = TRUE;
9379 local_player->was_waiting = FALSE;
9383 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9384 boolean button_released)
9386 if (button_released)
9388 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9389 CheckSaveEngineSnapshotToList();
9391 else if (element_clicked)
9393 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9394 CheckSaveEngineSnapshotToList();
9396 game.snapshot.changed_action = TRUE;
9400 boolean CheckSingleStepMode_EM(int frame,
9401 boolean any_player_moving,
9402 boolean any_player_snapping,
9403 boolean any_player_dropping)
9405 if (tape.single_step && tape.recording && !tape.pausing)
9406 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9407 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9409 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9410 any_player_snapping, any_player_dropping);
9412 return tape.pausing;
9415 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9416 boolean murphy_is_dropping)
9418 boolean murphy_starts_dropping = FALSE;
9421 for (i = 0; i < MAX_PLAYERS; i++)
9422 if (stored_player[i].force_dropping)
9423 murphy_starts_dropping = TRUE;
9425 if (tape.single_step && tape.recording && !tape.pausing)
9426 if (murphy_is_waiting && !murphy_starts_dropping)
9427 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9429 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9432 void CheckSingleStepMode_MM(boolean element_clicked,
9433 boolean button_released)
9435 if (tape.single_step && tape.recording && !tape.pausing)
9436 if (button_released)
9437 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9439 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9442 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9443 int graphic, int sync_frame)
9445 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9447 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9450 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9452 return (IS_NEXT_FRAME(sync_frame, graphic));
9455 int getGraphicInfo_Delay(int graphic)
9457 return graphic_info[graphic].anim_delay;
9460 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9462 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9465 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9471 void PlayMenuSoundExt(int sound)
9473 if (sound == SND_UNDEFINED)
9476 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9477 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9480 if (IS_LOOP_SOUND(sound))
9481 PlaySoundLoop(sound);
9486 void PlayMenuSound(void)
9488 PlayMenuSoundExt(menu.sound[game_status]);
9491 void PlayMenuSoundStereo(int sound, int stereo_position)
9493 if (sound == SND_UNDEFINED)
9496 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9497 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9500 if (IS_LOOP_SOUND(sound))
9501 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9503 PlaySoundStereo(sound, stereo_position);
9506 void PlayMenuSoundIfLoopExt(int sound)
9508 if (sound == SND_UNDEFINED)
9511 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9512 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9515 if (IS_LOOP_SOUND(sound))
9516 PlaySoundLoop(sound);
9519 void PlayMenuSoundIfLoop(void)
9521 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9524 void PlayMenuMusicExt(int music)
9526 if (music == MUS_UNDEFINED)
9529 if (!setup.sound_music)
9532 if (IS_LOOP_MUSIC(music))
9533 PlayMusicLoop(music);
9538 void PlayMenuMusic(void)
9540 char *curr_music = getCurrentlyPlayingMusicFilename();
9541 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9543 if (!strEqual(curr_music, next_music))
9544 PlayMenuMusicExt(menu.music[game_status]);
9547 void PlayMenuSoundsAndMusic(void)
9553 static void FadeMenuSounds(void)
9558 static void FadeMenuMusic(void)
9560 char *curr_music = getCurrentlyPlayingMusicFilename();
9561 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9563 if (!strEqual(curr_music, next_music))
9567 void FadeMenuSoundsAndMusic(void)
9573 void PlaySoundActivating(void)
9576 PlaySound(SND_MENU_ITEM_ACTIVATING);
9580 void PlaySoundSelecting(void)
9583 PlaySound(SND_MENU_ITEM_SELECTING);
9587 void ToggleFullscreenIfNeeded(void)
9589 // if setup and video fullscreen state are already matching, nothing do do
9590 if (setup.fullscreen == video.fullscreen_enabled ||
9591 !video.fullscreen_available)
9594 SDLSetWindowFullscreen(setup.fullscreen);
9596 // set setup value according to successfully changed fullscreen mode
9597 setup.fullscreen = video.fullscreen_enabled;
9600 void ChangeWindowScalingIfNeeded(void)
9602 // if setup and video window scaling are already matching, nothing do do
9603 if (setup.window_scaling_percent == video.window_scaling_percent ||
9604 video.fullscreen_enabled)
9607 SDLSetWindowScaling(setup.window_scaling_percent);
9609 // set setup value according to successfully changed window scaling
9610 setup.window_scaling_percent = video.window_scaling_percent;
9613 void ChangeVsyncModeIfNeeded(void)
9615 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9616 int video_vsync_mode = video.vsync_mode;
9618 // if setup and video vsync mode are already matching, nothing do do
9619 if (setup_vsync_mode == video_vsync_mode)
9622 // if renderer is using OpenGL, vsync mode can directly be changed
9623 SDLSetScreenVsyncMode(setup.vsync_mode);
9625 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9626 if (video.vsync_mode == video_vsync_mode)
9628 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9630 // save backbuffer content which gets lost when re-creating screen
9631 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9633 // force re-creating screen and renderer to set new vsync mode
9634 video.fullscreen_enabled = !setup.fullscreen;
9636 // when creating new renderer, destroy textures linked to old renderer
9637 FreeAllImageTextures(); // needs old renderer to free the textures
9639 // re-create screen and renderer (including change of vsync mode)
9640 ChangeVideoModeIfNeeded(setup.fullscreen);
9642 // set setup value according to successfully changed fullscreen mode
9643 setup.fullscreen = video.fullscreen_enabled;
9645 // restore backbuffer content from temporary backbuffer backup bitmap
9646 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9647 FreeBitmap(tmp_backbuffer);
9649 // update visible window/screen
9650 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9652 // when changing vsync mode, re-create textures for new renderer
9653 InitImageTextures();
9656 // set setup value according to successfully changed vsync mode
9657 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9660 static void JoinRectangles(int *x, int *y, int *width, int *height,
9661 int x2, int y2, int width2, int height2)
9663 // do not join with "off-screen" rectangle
9664 if (x2 == -1 || y2 == -1)
9669 *width = MAX(*width, width2);
9670 *height = MAX(*height, height2);
9673 void SetAnimStatus(int anim_status_new)
9675 if (anim_status_new == GAME_MODE_MAIN)
9676 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9677 else if (anim_status_new == GAME_MODE_NAMES)
9678 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9679 else if (anim_status_new == GAME_MODE_SCORES)
9680 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9682 global.anim_status_next = anim_status_new;
9684 // directly set screen modes that are entered without fading
9685 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9686 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9687 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9688 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9689 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9690 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9691 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9692 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9693 global.anim_status = global.anim_status_next;
9696 void SetGameStatus(int game_status_new)
9698 if (game_status_new != game_status)
9699 game_status_last_screen = game_status;
9701 game_status = game_status_new;
9703 SetAnimStatus(game_status_new);
9706 void SetFontStatus(int game_status_new)
9708 static int last_game_status = -1;
9710 if (game_status_new != -1)
9712 // set game status for font use after storing last game status
9713 last_game_status = game_status;
9714 game_status = game_status_new;
9718 // reset game status after font use from last stored game status
9719 game_status = last_game_status;
9723 void ResetFontStatus(void)
9728 void SetLevelSetInfo(char *identifier, int level_nr)
9730 setString(&levelset.identifier, identifier);
9732 levelset.level_nr = level_nr;
9735 boolean CheckIfAllViewportsHaveChanged(void)
9737 // if game status has not changed, viewports have not changed either
9738 if (game_status == game_status_last)
9741 // check if all viewports have changed with current game status
9743 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9744 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9745 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9746 int new_real_sx = vp_playfield->x;
9747 int new_real_sy = vp_playfield->y;
9748 int new_full_sxsize = vp_playfield->width;
9749 int new_full_sysize = vp_playfield->height;
9750 int new_dx = vp_door_1->x;
9751 int new_dy = vp_door_1->y;
9752 int new_dxsize = vp_door_1->width;
9753 int new_dysize = vp_door_1->height;
9754 int new_vx = vp_door_2->x;
9755 int new_vy = vp_door_2->y;
9756 int new_vxsize = vp_door_2->width;
9757 int new_vysize = vp_door_2->height;
9759 boolean playfield_viewport_has_changed =
9760 (new_real_sx != REAL_SX ||
9761 new_real_sy != REAL_SY ||
9762 new_full_sxsize != FULL_SXSIZE ||
9763 new_full_sysize != FULL_SYSIZE);
9765 boolean door_1_viewport_has_changed =
9768 new_dxsize != DXSIZE ||
9769 new_dysize != DYSIZE);
9771 boolean door_2_viewport_has_changed =
9774 new_vxsize != VXSIZE ||
9775 new_vysize != VYSIZE ||
9776 game_status_last == GAME_MODE_EDITOR);
9778 return (playfield_viewport_has_changed &&
9779 door_1_viewport_has_changed &&
9780 door_2_viewport_has_changed);
9783 boolean CheckFadeAll(void)
9785 return (CheckIfGlobalBorderHasChanged() ||
9786 CheckIfAllViewportsHaveChanged());
9789 void ChangeViewportPropertiesIfNeeded(void)
9791 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9792 FALSE : setup.small_game_graphics);
9793 int gfx_game_mode = getGlobalGameStatus(game_status);
9794 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9796 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9797 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9798 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9799 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9800 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9801 int new_win_xsize = vp_window->width;
9802 int new_win_ysize = vp_window->height;
9803 int border_left = vp_playfield->border_left;
9804 int border_right = vp_playfield->border_right;
9805 int border_top = vp_playfield->border_top;
9806 int border_bottom = vp_playfield->border_bottom;
9807 int new_sx = vp_playfield->x + border_left;
9808 int new_sy = vp_playfield->y + border_top;
9809 int new_sxsize = vp_playfield->width - border_left - border_right;
9810 int new_sysize = vp_playfield->height - border_top - border_bottom;
9811 int new_real_sx = vp_playfield->x;
9812 int new_real_sy = vp_playfield->y;
9813 int new_full_sxsize = vp_playfield->width;
9814 int new_full_sysize = vp_playfield->height;
9815 int new_dx = vp_door_1->x;
9816 int new_dy = vp_door_1->y;
9817 int new_dxsize = vp_door_1->width;
9818 int new_dysize = vp_door_1->height;
9819 int new_vx = vp_door_2->x;
9820 int new_vy = vp_door_2->y;
9821 int new_vxsize = vp_door_2->width;
9822 int new_vysize = vp_door_2->height;
9823 int new_ex = vp_door_3->x;
9824 int new_ey = vp_door_3->y;
9825 int new_exsize = vp_door_3->width;
9826 int new_eysize = vp_door_3->height;
9827 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9828 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9829 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9830 int new_scr_fieldx = new_sxsize / tilesize;
9831 int new_scr_fieldy = new_sysize / tilesize;
9832 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9833 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9834 boolean init_gfx_buffers = FALSE;
9835 boolean init_video_buffer = FALSE;
9836 boolean init_gadgets_and_anims = FALSE;
9837 boolean init_em_graphics = FALSE;
9839 if (new_win_xsize != WIN_XSIZE ||
9840 new_win_ysize != WIN_YSIZE)
9842 WIN_XSIZE = new_win_xsize;
9843 WIN_YSIZE = new_win_ysize;
9845 init_video_buffer = TRUE;
9846 init_gfx_buffers = TRUE;
9847 init_gadgets_and_anims = TRUE;
9849 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9852 if (new_scr_fieldx != SCR_FIELDX ||
9853 new_scr_fieldy != SCR_FIELDY)
9855 // this always toggles between MAIN and GAME when using small tile size
9857 SCR_FIELDX = new_scr_fieldx;
9858 SCR_FIELDY = new_scr_fieldy;
9860 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9871 new_sxsize != SXSIZE ||
9872 new_sysize != SYSIZE ||
9873 new_dxsize != DXSIZE ||
9874 new_dysize != DYSIZE ||
9875 new_vxsize != VXSIZE ||
9876 new_vysize != VYSIZE ||
9877 new_exsize != EXSIZE ||
9878 new_eysize != EYSIZE ||
9879 new_real_sx != REAL_SX ||
9880 new_real_sy != REAL_SY ||
9881 new_full_sxsize != FULL_SXSIZE ||
9882 new_full_sysize != FULL_SYSIZE ||
9883 new_tilesize_var != TILESIZE_VAR
9886 // ------------------------------------------------------------------------
9887 // determine next fading area for changed viewport definitions
9888 // ------------------------------------------------------------------------
9890 // start with current playfield area (default fading area)
9893 FADE_SXSIZE = FULL_SXSIZE;
9894 FADE_SYSIZE = FULL_SYSIZE;
9896 // add new playfield area if position or size has changed
9897 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9898 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9900 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9901 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9904 // add current and new door 1 area if position or size has changed
9905 if (new_dx != DX || new_dy != DY ||
9906 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9908 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9909 DX, DY, DXSIZE, DYSIZE);
9910 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9911 new_dx, new_dy, new_dxsize, new_dysize);
9914 // add current and new door 2 area if position or size has changed
9915 if (new_vx != VX || new_vy != VY ||
9916 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9918 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9919 VX, VY, VXSIZE, VYSIZE);
9920 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9921 new_vx, new_vy, new_vxsize, new_vysize);
9924 // ------------------------------------------------------------------------
9925 // handle changed tile size
9926 // ------------------------------------------------------------------------
9928 if (new_tilesize_var != TILESIZE_VAR)
9930 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9932 // changing tile size invalidates scroll values of engine snapshots
9933 FreeEngineSnapshotSingle();
9935 // changing tile size requires update of graphic mapping for EM engine
9936 init_em_graphics = TRUE;
9947 SXSIZE = new_sxsize;
9948 SYSIZE = new_sysize;
9949 DXSIZE = new_dxsize;
9950 DYSIZE = new_dysize;
9951 VXSIZE = new_vxsize;
9952 VYSIZE = new_vysize;
9953 EXSIZE = new_exsize;
9954 EYSIZE = new_eysize;
9955 REAL_SX = new_real_sx;
9956 REAL_SY = new_real_sy;
9957 FULL_SXSIZE = new_full_sxsize;
9958 FULL_SYSIZE = new_full_sysize;
9959 TILESIZE_VAR = new_tilesize_var;
9961 init_gfx_buffers = TRUE;
9962 init_gadgets_and_anims = TRUE;
9964 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9965 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9968 if (init_gfx_buffers)
9970 // Debug("tools:viewport", "init_gfx_buffers");
9972 SCR_FIELDX = new_scr_fieldx_buffers;
9973 SCR_FIELDY = new_scr_fieldy_buffers;
9977 SCR_FIELDX = new_scr_fieldx;
9978 SCR_FIELDY = new_scr_fieldy;
9980 SetDrawDeactivationMask(REDRAW_NONE);
9981 SetDrawBackgroundMask(REDRAW_FIELD);
9984 if (init_video_buffer)
9986 // Debug("tools:viewport", "init_video_buffer");
9988 FreeAllImageTextures(); // needs old renderer to free the textures
9990 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9991 InitImageTextures();
9994 if (init_gadgets_and_anims)
9996 // Debug("tools:viewport", "init_gadgets_and_anims");
9999 InitGlobalAnimations();
10002 if (init_em_graphics)
10004 InitGraphicInfo_EM();
10008 void OpenURL(char *url)
10010 #if SDL_VERSION_ATLEAST(2,0,14)
10013 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
10014 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
10015 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
10019 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
10021 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
10025 // ============================================================================
10027 // ============================================================================
10029 #if defined(PLATFORM_WINDOWS)
10030 /* FILETIME of Jan 1 1970 00:00:00. */
10031 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
10034 * timezone information is stored outside the kernel so tzp isn't used anymore.
10036 * Note: this function is not for Win32 high precision timing purpose. See
10039 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
10041 FILETIME file_time;
10042 SYSTEMTIME system_time;
10043 ULARGE_INTEGER ularge;
10045 GetSystemTime(&system_time);
10046 SystemTimeToFileTime(&system_time, &file_time);
10047 ularge.LowPart = file_time.dwLowDateTime;
10048 ularge.HighPart = file_time.dwHighDateTime;
10050 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10051 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10057 static char *test_init_uuid_random_function_simple(void)
10059 static char seed_text[100];
10060 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10062 sprintf(seed_text, "%d", seed);
10067 static char *test_init_uuid_random_function_better(void)
10069 static char seed_text[100];
10070 struct timeval current_time;
10072 gettimeofday(¤t_time, NULL);
10074 prng_seed_bytes(¤t_time, sizeof(current_time));
10076 sprintf(seed_text, "%ld.%ld",
10077 (long)current_time.tv_sec,
10078 (long)current_time.tv_usec);
10083 #if defined(PLATFORM_WINDOWS)
10084 static char *test_init_uuid_random_function_better_windows(void)
10086 static char seed_text[100];
10087 struct timeval current_time;
10089 gettimeofday_windows(¤t_time, NULL);
10091 prng_seed_bytes(¤t_time, sizeof(current_time));
10093 sprintf(seed_text, "%ld.%ld",
10094 (long)current_time.tv_sec,
10095 (long)current_time.tv_usec);
10101 static unsigned int test_uuid_random_function_simple(int max)
10103 return GetSimpleRandom(max);
10106 static unsigned int test_uuid_random_function_better(int max)
10108 return (max > 0 ? prng_get_uint() % max : 0);
10111 #if defined(PLATFORM_WINDOWS)
10112 #define NUM_UUID_TESTS 3
10114 #define NUM_UUID_TESTS 2
10117 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10119 struct hashtable *hash_seeds =
10120 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10121 struct hashtable *hash_uuids =
10122 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10123 static char message[100];
10126 char *random_name = (nr == 0 ? "simple" : "better");
10127 char *random_type = (always_seed ? "always" : "only once");
10128 char *(*init_random_function)(void) =
10130 test_init_uuid_random_function_simple :
10131 test_init_uuid_random_function_better);
10132 unsigned int (*random_function)(int) =
10134 test_uuid_random_function_simple :
10135 test_uuid_random_function_better);
10138 #if defined(PLATFORM_WINDOWS)
10141 random_name = "windows";
10142 init_random_function = test_init_uuid_random_function_better_windows;
10148 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10149 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10151 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10152 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10153 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10155 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10159 // always initialize random number generator at least once
10160 init_random_function();
10162 unsigned int time_start = SDL_GetTicks();
10164 for (i = 0; i < num_uuids; i++)
10168 char *seed = getStringCopy(init_random_function());
10170 hashtable_remove(hash_seeds, seed);
10171 hashtable_insert(hash_seeds, seed, "1");
10174 char *uuid = getStringCopy(getUUIDExt(random_function));
10176 hashtable_remove(hash_uuids, uuid);
10177 hashtable_insert(hash_uuids, uuid, "1");
10180 int num_unique_seeds = hashtable_count(hash_seeds);
10181 int num_unique_uuids = hashtable_count(hash_uuids);
10183 unsigned int time_needed = SDL_GetTicks() - time_start;
10185 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10187 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10190 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10192 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10193 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10195 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10197 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10199 Request(message, REQ_CONFIRM);
10201 hashtable_destroy(hash_seeds, 0);
10202 hashtable_destroy(hash_uuids, 0);
10205 void TestGeneratingUUIDs(void)
10207 int num_uuids = 1000000;
10210 for (i = 0; i < NUM_UUID_TESTS; i++)
10211 for (j = 0; j < 2; j++)
10212 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10214 CloseAllAndExit(0);