1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 #define DEBUG_FRAME_TIME FALSE
28 // tool button identifiers
29 #define TOOL_CTRL_ID_YES 0
30 #define TOOL_CTRL_ID_NO 1
31 #define TOOL_CTRL_ID_CONFIRM 2
32 #define TOOL_CTRL_ID_PLAYER_1 3
33 #define TOOL_CTRL_ID_PLAYER_2 4
34 #define TOOL_CTRL_ID_PLAYER_3 5
35 #define TOOL_CTRL_ID_PLAYER_4 6
36 #define TOOL_CTRL_ID_TOUCH_YES 7
37 #define TOOL_CTRL_ID_TOUCH_NO 8
38 #define TOOL_CTRL_ID_TOUCH_CONFIRM 9
40 #define NUM_TOOL_BUTTONS 10
42 // constants for number of doors and door parts
44 #define NUM_PANELS NUM_DOORS
45 // #define NUM_PANELS 0
46 #define MAX_PARTS_PER_DOOR 8
47 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
48 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
51 struct DoorPartOrderInfo
57 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
59 struct DoorPartControlInfo
63 struct DoorPartPosInfo *pos;
66 static struct DoorPartControlInfo door_part_controls[] =
70 IMG_GFX_DOOR_1_PART_1,
75 IMG_GFX_DOOR_1_PART_2,
80 IMG_GFX_DOOR_1_PART_3,
85 IMG_GFX_DOOR_1_PART_4,
90 IMG_GFX_DOOR_1_PART_5,
95 IMG_GFX_DOOR_1_PART_6,
100 IMG_GFX_DOOR_1_PART_7,
105 IMG_GFX_DOOR_1_PART_8,
111 IMG_GFX_DOOR_2_PART_1,
116 IMG_GFX_DOOR_2_PART_2,
121 IMG_GFX_DOOR_2_PART_3,
126 IMG_GFX_DOOR_2_PART_4,
131 IMG_GFX_DOOR_2_PART_5,
136 IMG_GFX_DOOR_2_PART_6,
141 IMG_GFX_DOOR_2_PART_7,
146 IMG_GFX_DOOR_2_PART_8,
152 IMG_BACKGROUND_PANEL,
168 static struct XY xy_topdown[] =
177 // forward declaration for internal use
178 static void UnmapToolButtons(void);
179 static void HandleToolButtons(struct GadgetInfo *);
180 static int el_act_dir2crm(int, int, int);
181 static int el_act2crm(int, int);
183 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
184 static int request_gadget_id = -1;
186 static char *print_if_not_empty(int element)
188 static char *s = NULL;
189 char *token_name = element_info[element].token_name;
194 s = checked_malloc(strlen(token_name) + 10 + 1);
196 if (element != EL_EMPTY)
197 sprintf(s, "%d\t['%s']", element, token_name);
199 sprintf(s, "%d", element);
204 int getFieldbufferOffsetX_RND(int dir, int pos)
206 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
207 int dx = (dir & MV_HORIZONTAL ? pos : 0);
208 int dx_var = dx * TILESIZE_VAR / TILESIZE;
211 if (EVEN(SCR_FIELDX))
213 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
214 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
216 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
217 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
219 fx += (dx_var > 0 ? TILEX_VAR : 0);
226 if (full_lev_fieldx <= SCR_FIELDX)
228 if (EVEN(SCR_FIELDX))
229 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
231 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
237 int getFieldbufferOffsetY_RND(int dir, int pos)
239 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
240 int dy = (dir & MV_VERTICAL ? pos : 0);
241 int dy_var = dy * TILESIZE_VAR / TILESIZE;
244 if (EVEN(SCR_FIELDY))
246 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
247 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
249 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
250 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
252 fy += (dy_var > 0 ? TILEY_VAR : 0);
259 if (full_lev_fieldy <= SCR_FIELDY)
261 if (EVEN(SCR_FIELDY))
262 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
264 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
270 static int getLevelFromScreenX_RND(int sx)
272 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
275 int lx = LEVELX((px + dx) / TILESIZE_VAR);
280 static int getLevelFromScreenY_RND(int sy)
282 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
285 int ly = LEVELY((py + dy) / TILESIZE_VAR);
290 static int getLevelFromScreenX_EM(int sx)
292 int level_xsize = level.native_em_level->cav->width;
293 int full_xsize = level_xsize * TILESIZE_VAR;
295 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
297 int fx = getFieldbufferOffsetX_EM();
300 int lx = LEVELX((px + dx) / TILESIZE_VAR);
305 static int getLevelFromScreenY_EM(int sy)
307 int level_ysize = level.native_em_level->cav->height;
308 int full_ysize = level_ysize * TILESIZE_VAR;
310 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
312 int fy = getFieldbufferOffsetY_EM();
315 int ly = LEVELY((py + dy) / TILESIZE_VAR);
320 static int getLevelFromScreenX_SP(int sx)
322 int menBorder = setup.sp_show_border_elements;
323 int level_xsize = level.native_sp_level->width;
324 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
326 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
328 int fx = getFieldbufferOffsetX_SP();
331 int lx = LEVELX((px + dx) / TILESIZE_VAR);
336 static int getLevelFromScreenY_SP(int sy)
338 int menBorder = setup.sp_show_border_elements;
339 int level_ysize = level.native_sp_level->height;
340 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
342 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
344 int fy = getFieldbufferOffsetY_SP();
347 int ly = LEVELY((py + dy) / TILESIZE_VAR);
352 static int getLevelFromScreenX_MM(int sx)
354 int level_xsize = level.native_mm_level->fieldx;
355 int full_xsize = level_xsize * TILESIZE_VAR;
357 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
360 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
365 static int getLevelFromScreenY_MM(int sy)
367 int level_ysize = level.native_mm_level->fieldy;
368 int full_ysize = level_ysize * TILESIZE_VAR;
370 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
373 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
378 int getLevelFromScreenX(int x)
380 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
381 return getLevelFromScreenX_EM(x);
382 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
383 return getLevelFromScreenX_SP(x);
384 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
385 return getLevelFromScreenX_MM(x);
387 return getLevelFromScreenX_RND(x);
390 int getLevelFromScreenY(int y)
392 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
393 return getLevelFromScreenY_EM(y);
394 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
395 return getLevelFromScreenY_SP(y);
396 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
397 return getLevelFromScreenY_MM(y);
399 return getLevelFromScreenY_RND(y);
402 int getScreenFieldSizeX(void)
404 return (tape.playing ? tape.scr_fieldx : SCR_FIELDX);
407 int getScreenFieldSizeY(void)
409 return (tape.playing ? tape.scr_fieldy : SCR_FIELDY);
412 void DumpTile(int x, int y)
419 Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
422 if (!IN_LEV_FIELD(x, y))
424 Info("(not in level field)");
430 token_name = element_info[Tile[x][y]].token_name;
432 Info("Tile: %d\t['%s']", Tile[x][y], token_name);
433 Info("Back: %s", print_if_not_empty(Back[x][y]));
434 Info("Store: %s", print_if_not_empty(Store[x][y]));
435 Info("Store2: %s", print_if_not_empty(Store2[x][y]));
436 Info("StorePlayer: %s", print_if_not_empty(StorePlayer[x][y]));
437 Info("MovPos: %d", MovPos[x][y]);
438 Info("MovDir: %d", MovDir[x][y]);
439 Info("MovDelay: %d", MovDelay[x][y]);
440 Info("ChangeDelay: %d", ChangeDelay[x][y]);
441 Info("CustomValue: %d", CustomValue[x][y]);
442 Info("GfxElement: %d", GfxElement[x][y]);
443 Info("GfxAction: %d", GfxAction[x][y]);
444 Info("GfxFrame: %d [%d]", GfxFrame[x][y], FrameCounter);
445 Info("Player x/y: %d, %d", local_player->jx, local_player->jy);
449 void DumpTileFromScreen(int sx, int sy)
451 int lx = getLevelFromScreenX(sx);
452 int ly = getLevelFromScreenY(sy);
457 void SetDrawtoField(int mode)
459 if (mode == DRAW_TO_FIELDBUFFER)
465 BX2 = SCR_FIELDX + 1;
466 BY2 = SCR_FIELDY + 1;
468 drawto_field = fieldbuffer;
470 else // DRAW_TO_BACKBUFFER
476 BX2 = SCR_FIELDX - 1;
477 BY2 = SCR_FIELDY - 1;
479 drawto_field = backbuffer;
483 int GetDrawtoField(void)
485 return (drawto_field == fieldbuffer ? DRAW_TO_FIELDBUFFER : DRAW_TO_BACKBUFFER);
488 static void RedrawPlayfield_RND(void)
490 if (game.envelope_active)
493 DrawLevel(REDRAW_ALL);
497 void RedrawPlayfield(void)
499 if (game_status != GAME_MODE_PLAYING)
502 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
503 RedrawPlayfield_EM(TRUE);
504 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
505 RedrawPlayfield_SP(TRUE);
506 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
507 RedrawPlayfield_MM();
508 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
509 RedrawPlayfield_RND();
511 BlitScreenToBitmap(backbuffer);
513 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
517 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
520 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
521 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
523 // may happen for "border.draw_masked.*" with undefined "global.border.*"
524 if (src_bitmap == NULL)
527 if (x == -1 && y == -1)
530 if (draw_target == DRAW_TO_SCREEN)
531 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
533 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
536 static void DrawMaskedBorderExt_FIELD(int draw_target)
538 if (global.border_status >= GAME_MODE_MAIN &&
539 global.border_status <= GAME_MODE_PLAYING &&
540 border.draw_masked[global.border_status])
541 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
545 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
547 // when drawing to backbuffer, never draw border over open doors
548 if (draw_target == DRAW_TO_BACKBUFFER &&
549 (GetDoorState() & DOOR_OPEN_1))
552 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
553 (global.border_status != GAME_MODE_EDITOR ||
554 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
555 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
558 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
560 // when drawing to backbuffer, never draw border over open doors
561 if (draw_target == DRAW_TO_BACKBUFFER &&
562 (GetDoorState() & DOOR_OPEN_2))
565 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
566 global.border_status != GAME_MODE_EDITOR)
567 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
570 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
572 // currently not available
575 static void DrawMaskedBorderExt_ALL(int draw_target)
577 DrawMaskedBorderExt_FIELD(draw_target);
578 DrawMaskedBorderExt_DOOR_1(draw_target);
579 DrawMaskedBorderExt_DOOR_2(draw_target);
580 DrawMaskedBorderExt_DOOR_3(draw_target);
583 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
585 // never draw masked screen borders on borderless screens
586 if (global.border_status == GAME_MODE_LOADING ||
587 global.border_status == GAME_MODE_TITLE)
590 if (redraw_mask & REDRAW_ALL)
591 DrawMaskedBorderExt_ALL(draw_target);
594 if (redraw_mask & REDRAW_FIELD)
595 DrawMaskedBorderExt_FIELD(draw_target);
596 if (redraw_mask & REDRAW_DOOR_1)
597 DrawMaskedBorderExt_DOOR_1(draw_target);
598 if (redraw_mask & REDRAW_DOOR_2)
599 DrawMaskedBorderExt_DOOR_2(draw_target);
600 if (redraw_mask & REDRAW_DOOR_3)
601 DrawMaskedBorderExt_DOOR_3(draw_target);
605 void DrawMaskedBorder_FIELD(void)
607 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
610 void DrawMaskedBorder(int redraw_mask)
612 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
615 void DrawMaskedBorderToTarget(int draw_target)
617 if (draw_target == DRAW_TO_BACKBUFFER ||
618 draw_target == DRAW_TO_SCREEN)
620 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
624 int last_border_status = global.border_status;
626 if (draw_target == DRAW_TO_FADE_SOURCE)
628 global.border_status = gfx.fade_border_source_status;
629 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
631 else if (draw_target == DRAW_TO_FADE_TARGET)
633 global.border_status = gfx.fade_border_target_status;
634 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
637 // always use global border for PLAYING when restarting the game
638 if (global.border_status == GAME_MODE_PSEUDO_RESTARTING)
639 global.border_status = GAME_MODE_PLAYING;
641 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
643 global.border_status = last_border_status;
644 gfx.masked_border_bitmap_ptr = backbuffer;
648 void DrawTileCursor(int draw_target)
650 DrawTileCursor_MM(draw_target, game_status == GAME_MODE_PLAYING);
653 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
655 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
658 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
660 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
661 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
663 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
666 void BlitScreenToBitmap(Bitmap *target_bitmap)
668 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
669 BlitScreenToBitmap_EM(target_bitmap);
670 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
671 BlitScreenToBitmap_SP(target_bitmap);
672 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
673 BlitScreenToBitmap_MM(target_bitmap);
674 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
675 BlitScreenToBitmap_RND(target_bitmap);
677 redraw_mask |= REDRAW_FIELD;
680 static void DrawFramesPerSecond(void)
683 int font_nr = FONT_TEXT_2;
684 int font_width = getFontWidth(font_nr);
685 int draw_deactivation_mask = GetDrawDeactivationMask();
686 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
688 // draw FPS with leading space (needed if field buffer deactivated)
689 sprintf(text, " %04.1f fps", global.frames_per_second);
691 // override draw deactivation mask (required for invisible warp mode)
692 SetDrawDeactivationMask(REDRAW_NONE);
694 // draw opaque FPS if field buffer deactivated, else draw masked FPS
695 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
696 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
698 // set draw deactivation mask to previous value
699 SetDrawDeactivationMask(draw_deactivation_mask);
701 // force full-screen redraw in this frame
702 redraw_mask = REDRAW_ALL;
706 static void PrintFrameTimeDebugging(void)
708 static unsigned int last_counter = 0;
709 unsigned int counter = Counter();
710 int diff_1 = counter - last_counter;
711 int diff_2 = diff_1 - GAME_FRAME_DELAY;
713 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
714 char diff_bar[2 * diff_2_max + 5];
718 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
720 for (i = 0; i < diff_2_max; i++)
721 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
722 i >= diff_2_max - diff_2_cut ? '-' : ' ');
724 diff_bar[pos++] = '|';
726 for (i = 0; i < diff_2_max; i++)
727 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
729 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
731 diff_bar[pos++] = '\0';
733 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
736 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
739 last_counter = counter;
743 static int unifiedRedrawMask(int mask)
745 if (mask & REDRAW_ALL)
748 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
754 static boolean equalRedrawMasks(int mask_1, int mask_2)
756 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
759 void BackToFront(void)
761 static int last_redraw_mask = REDRAW_NONE;
763 // force screen redraw in every frame to continue drawing global animations
764 // (but always use the last redraw mask to prevent unwanted side effects)
765 if (redraw_mask == REDRAW_NONE)
766 redraw_mask = last_redraw_mask;
768 last_redraw_mask = redraw_mask;
771 // masked border now drawn immediately when blitting backbuffer to window
773 // draw masked border to all viewports, if defined
774 DrawMaskedBorder(redraw_mask);
777 // draw frames per second (only if debug mode is enabled)
778 if (redraw_mask & REDRAW_FPS)
779 DrawFramesPerSecond();
781 // remove playfield redraw before potentially merging with doors redraw
782 if (DrawingDeactivated(REAL_SX, REAL_SY))
783 redraw_mask &= ~REDRAW_FIELD;
785 // redraw complete window if both playfield and (some) doors need redraw
786 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
787 redraw_mask = REDRAW_ALL;
789 /* although redrawing the whole window would be fine for normal gameplay,
790 being able to only redraw the playfield is required for deactivating
791 certain drawing areas (mainly playfield) to work, which is needed for
792 warp-forward to be fast enough (by skipping redraw of most frames) */
794 if (redraw_mask & REDRAW_ALL)
796 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
798 else if (redraw_mask & REDRAW_FIELD)
800 BlitBitmap(backbuffer, window,
801 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
803 else if (redraw_mask & REDRAW_DOORS)
805 // merge door areas to prevent calling screen redraw more than once
811 if (redraw_mask & REDRAW_DOOR_1)
815 x2 = MAX(x2, DX + DXSIZE);
816 y2 = MAX(y2, DY + DYSIZE);
819 if (redraw_mask & REDRAW_DOOR_2)
823 x2 = MAX(x2, VX + VXSIZE);
824 y2 = MAX(y2, VY + VYSIZE);
827 if (redraw_mask & REDRAW_DOOR_3)
831 x2 = MAX(x2, EX + EXSIZE);
832 y2 = MAX(y2, EY + EYSIZE);
835 // make sure that at least one pixel is blitted, and inside the screen
836 // (else nothing is blitted, causing the animations not to be updated)
837 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
838 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
839 x2 = MIN(MAX(1, x2), WIN_XSIZE);
840 y2 = MIN(MAX(1, y2), WIN_YSIZE);
842 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
845 redraw_mask = REDRAW_NONE;
848 PrintFrameTimeDebugging();
852 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
854 unsigned int frame_delay_value_old = GetVideoFrameDelay();
856 SetVideoFrameDelay(frame_delay_value);
860 SetVideoFrameDelay(frame_delay_value_old);
863 static int fade_type_skip = FADE_TYPE_NONE;
865 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
867 void (*draw_border_function)(void) = NULL;
868 int x, y, width, height;
869 int fade_delay, post_delay;
871 if (fade_type == FADE_TYPE_FADE_OUT)
873 if (fade_type_skip != FADE_TYPE_NONE)
875 // skip all fade operations until specified fade operation
876 if (fade_type & fade_type_skip)
877 fade_type_skip = FADE_TYPE_NONE;
882 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
886 redraw_mask |= fade_mask;
888 if (fade_type == FADE_TYPE_SKIP)
890 fade_type_skip = fade_mode;
895 fade_delay = fading.fade_delay;
896 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
898 if (fade_type_skip != FADE_TYPE_NONE)
900 // skip all fade operations until specified fade operation
901 if (fade_type & fade_type_skip)
902 fade_type_skip = FADE_TYPE_NONE;
907 if (global.autoplay_leveldir)
912 if (fade_mask == REDRAW_FIELD)
917 height = FADE_SYSIZE;
919 if (border.draw_masked_when_fading)
920 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
922 DrawMaskedBorder_FIELD(); // draw once
932 // when switching screens without fading, set fade delay to zero
933 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
936 // do not display black frame when fading out without fade delay
937 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
940 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
941 draw_border_function);
943 redraw_mask &= ~fade_mask;
945 ClearAutoRepeatKeyEvents();
948 static void SetScreenStates_BeforeFadingIn(void)
950 // temporarily set screen mode for animations to screen after fading in
951 global.anim_status = global.anim_status_next;
953 // store backbuffer with all animations that will be started after fading in
954 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
956 // set screen mode for animations back to fading
957 global.anim_status = GAME_MODE_PSEUDO_FADING;
960 static void SetScreenStates_AfterFadingIn(void)
962 // store new source screen (to use correct masked border for fading)
963 gfx.fade_border_source_status = global.border_status;
965 global.anim_status = global.anim_status_next;
968 static void SetScreenStates_BeforeFadingOut(void)
970 // store new target screen (to use correct masked border for fading)
971 gfx.fade_border_target_status = game_status;
973 // set screen mode for animations to fading
974 global.anim_status = GAME_MODE_PSEUDO_FADING;
976 // store backbuffer with all animations that will be stopped for fading out
977 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
980 static void SetScreenStates_AfterFadingOut(void)
982 global.border_status = game_status;
984 // always use global border for PLAYING when restarting the game
985 if (global.border_status == GAME_MODE_PSEUDO_RESTARTING)
986 global.border_status = GAME_MODE_PLAYING;
989 void FadeIn(int fade_mask)
991 SetScreenStates_BeforeFadingIn();
994 DrawMaskedBorder(REDRAW_ALL);
997 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
998 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1000 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1004 FADE_SXSIZE = FULL_SXSIZE;
1005 FADE_SYSIZE = FULL_SYSIZE;
1007 // activate virtual buttons depending on upcoming game status
1008 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1009 game_status == GAME_MODE_PLAYING && !tape.playing)
1010 SetOverlayActive(TRUE);
1012 SetScreenStates_AfterFadingIn();
1014 // force update of global animation status in case of rapid screen changes
1015 redraw_mask = REDRAW_ALL;
1019 void FadeOut(int fade_mask)
1021 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1022 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1023 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1026 SetScreenStates_BeforeFadingOut();
1028 SetTileCursorActive(FALSE);
1029 SetOverlayActive(FALSE);
1032 DrawMaskedBorder(REDRAW_ALL);
1035 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1036 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1038 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1040 SetScreenStates_AfterFadingOut();
1043 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1045 static struct TitleFadingInfo fading_leave_stored;
1048 fading_leave_stored = fading_leave;
1050 fading = fading_leave_stored;
1053 void FadeSetEnterMenu(void)
1055 fading = menu.enter_menu;
1057 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1060 void FadeSetLeaveMenu(void)
1062 fading = menu.leave_menu;
1064 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1067 void FadeSetEnterScreen(void)
1069 fading = menu.enter_screen[game_status];
1071 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1074 void FadeSetNextScreen(void)
1076 fading = menu.next_screen[game_status];
1078 // (do not overwrite fade mode set by FadeSetEnterScreen)
1079 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1082 void FadeSetLeaveScreen(void)
1084 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1087 void FadeSetFromType(int type)
1089 if (type & TYPE_ENTER_SCREEN)
1090 FadeSetEnterScreen();
1091 else if (type & TYPE_ENTER)
1093 else if (type & TYPE_LEAVE)
1097 void FadeSetDisabled(void)
1099 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1101 fading = fading_none;
1104 void FadeSkipNextFadeIn(void)
1106 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1109 void FadeSkipNextFadeOut(void)
1111 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1114 static int getGlobalGameStatus(int status)
1116 return (status == GAME_MODE_PSEUDO_TYPENAME ? GAME_MODE_MAIN :
1117 status == GAME_MODE_SCOREINFO ? GAME_MODE_SCORES :
1121 int getImageFromGraphicOrDefault(int graphic, int default_graphic)
1123 if (graphic == IMG_UNDEFINED)
1124 return IMG_UNDEFINED;
1126 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1128 return (graphic_info[graphic].bitmap != NULL || redefined ?
1129 graphic : default_graphic);
1132 static int getBackgroundImage(int graphic)
1134 return getImageFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1137 static int getGlobalBorderImage(int graphic)
1139 return getImageFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1142 Bitmap *getGlobalBorderBitmapFromStatus(int status_raw)
1144 int status = getGlobalGameStatus(status_raw);
1146 (status == GAME_MODE_MAIN ? IMG_GLOBAL_BORDER_MAIN :
1147 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1148 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1149 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1151 int graphic_final = getGlobalBorderImage(graphic);
1153 return graphic_info[graphic_final].bitmap;
1156 void SetBackgroundImage(int graphic, int redraw_mask)
1158 struct GraphicInfo *g = &graphic_info[graphic];
1159 struct GraphicInfo g_undefined = { 0 };
1161 if (graphic == IMG_UNDEFINED)
1164 // always use original size bitmap for backgrounds, if existing
1165 Bitmap *bitmap = (g->bitmaps != NULL &&
1166 g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] != NULL ?
1167 g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] : g->bitmap);
1169 // remove every mask before setting mask for window, and
1170 // remove window area mask before setting mask for main or door area
1171 int remove_mask = (redraw_mask == REDRAW_ALL ? 0xffff : REDRAW_ALL);
1173 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
1174 SetBackgroundBitmap(NULL, remove_mask, 0, 0, 0, 0); // !!! FIX THIS !!!
1175 SetBackgroundBitmap(bitmap, redraw_mask,
1177 g->width, g->height);
1180 void SetWindowBackgroundImageIfDefined(int graphic)
1182 if (graphic_info[graphic].bitmap)
1183 SetBackgroundImage(graphic, REDRAW_ALL);
1186 void SetMainBackgroundImageIfDefined(int graphic)
1188 if (graphic_info[graphic].bitmap)
1189 SetBackgroundImage(graphic, REDRAW_FIELD);
1192 void SetDoorBackgroundImageIfDefined(int graphic)
1194 if (graphic_info[graphic].bitmap)
1195 SetBackgroundImage(graphic, REDRAW_DOOR_1);
1198 void SetWindowBackgroundImage(int graphic)
1200 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_ALL);
1203 void SetMainBackgroundImage(int graphic)
1205 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_FIELD);
1208 void SetDoorBackgroundImage(int graphic)
1210 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_DOOR_1);
1213 void SetPanelBackground(void)
1215 SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
1218 void DrawBackground(int x, int y, int width, int height)
1220 // "drawto" might still point to playfield buffer here (hall of fame)
1221 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1223 if (IN_GFX_FIELD_FULL(x, y))
1224 redraw_mask |= REDRAW_FIELD;
1225 else if (IN_GFX_DOOR_1(x, y))
1226 redraw_mask |= REDRAW_DOOR_1;
1227 else if (IN_GFX_DOOR_2(x, y))
1228 redraw_mask |= REDRAW_DOOR_2;
1229 else if (IN_GFX_DOOR_3(x, y))
1230 redraw_mask |= REDRAW_DOOR_3;
1233 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1235 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1237 if (font->bitmap == NULL)
1240 DrawBackground(x, y, width, height);
1243 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1245 struct GraphicInfo *g = &graphic_info[graphic];
1247 if (g->bitmap == NULL)
1250 DrawBackground(x, y, width, height);
1253 static int game_status_last = -1;
1254 static Bitmap *global_border_bitmap_last = NULL;
1255 static Bitmap *global_border_bitmap = NULL;
1256 static int real_sx_last = -1, real_sy_last = -1;
1257 static int full_sxsize_last = -1, full_sysize_last = -1;
1258 static int dx_last = -1, dy_last = -1;
1259 static int dxsize_last = -1, dysize_last = -1;
1260 static int vx_last = -1, vy_last = -1;
1261 static int vxsize_last = -1, vysize_last = -1;
1262 static int ex_last = -1, ey_last = -1;
1263 static int exsize_last = -1, eysize_last = -1;
1265 boolean CheckIfGlobalBorderHasChanged(void)
1267 // if game status has not changed, global border has not changed either
1268 if (game_status == game_status_last)
1271 // determine and store new global border bitmap for current game status
1272 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1274 return (global_border_bitmap_last != global_border_bitmap);
1277 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1279 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1280 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1282 // if game status has not changed, nothing has to be redrawn
1283 if (game_status == game_status_last)
1286 // redraw if last screen was title screen
1287 if (game_status_last == GAME_MODE_TITLE)
1290 // redraw if global screen border has changed
1291 if (CheckIfGlobalBorderHasChanged())
1294 // redraw if position or size of playfield area has changed
1295 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1296 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1299 // redraw if position or size of door area has changed
1300 if (dx_last != DX || dy_last != DY ||
1301 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1304 // redraw if position or size of tape area has changed
1305 if (vx_last != VX || vy_last != VY ||
1306 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1309 // redraw if position or size of editor area has changed
1310 if (ex_last != EX || ey_last != EY ||
1311 exsize_last != EXSIZE || eysize_last != EYSIZE)
1318 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1321 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1323 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1326 void RedrawGlobalBorder(void)
1328 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1330 RedrawGlobalBorderFromBitmap(bitmap);
1332 redraw_mask = REDRAW_ALL;
1335 static void RedrawGlobalBorderIfNeeded(void)
1337 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1338 if (game_status == game_status_last)
1342 // copy current draw buffer to later copy back areas that have not changed
1343 if (game_status_last != GAME_MODE_TITLE)
1344 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1346 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1347 if (CheckIfGlobalBorderRedrawIsNeeded())
1349 // determine and store new global border bitmap for current game status
1350 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1353 // redraw global screen border (or clear, if defined to be empty)
1354 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1356 if (game_status == GAME_MODE_EDITOR)
1357 DrawSpecialEditorDoor();
1359 // copy previous playfield and door areas, if they are defined on both
1360 // previous and current screen and if they still have the same size
1362 if (real_sx_last != -1 && real_sy_last != -1 &&
1363 REAL_SX != -1 && REAL_SY != -1 &&
1364 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1365 BlitBitmap(bitmap_db_store_1, backbuffer,
1366 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1369 if (dx_last != -1 && dy_last != -1 &&
1370 DX != -1 && DY != -1 &&
1371 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1372 BlitBitmap(bitmap_db_store_1, backbuffer,
1373 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1375 if (game_status != GAME_MODE_EDITOR)
1377 if (vx_last != -1 && vy_last != -1 &&
1378 VX != -1 && VY != -1 &&
1379 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1380 BlitBitmap(bitmap_db_store_1, backbuffer,
1381 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1385 if (ex_last != -1 && ey_last != -1 &&
1386 EX != -1 && EY != -1 &&
1387 exsize_last == EXSIZE && eysize_last == EYSIZE)
1388 BlitBitmap(bitmap_db_store_1, backbuffer,
1389 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1392 redraw_mask = REDRAW_ALL;
1395 game_status_last = game_status;
1397 global_border_bitmap_last = global_border_bitmap;
1399 real_sx_last = REAL_SX;
1400 real_sy_last = REAL_SY;
1401 full_sxsize_last = FULL_SXSIZE;
1402 full_sysize_last = FULL_SYSIZE;
1405 dxsize_last = DXSIZE;
1406 dysize_last = DYSIZE;
1409 vxsize_last = VXSIZE;
1410 vysize_last = VYSIZE;
1413 exsize_last = EXSIZE;
1414 eysize_last = EYSIZE;
1417 void ClearField(void)
1419 RedrawGlobalBorderIfNeeded();
1421 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1422 // (when entering hall of fame after playing)
1423 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1425 // !!! maybe this should be done before clearing the background !!!
1426 if (game_status == GAME_MODE_PLAYING)
1428 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1429 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1433 SetDrawtoField(DRAW_TO_BACKBUFFER);
1437 void MarkTileDirty(int x, int y)
1439 redraw_mask |= REDRAW_FIELD;
1442 void SetBorderElement(void)
1446 BorderElement = EL_EMPTY;
1448 // only the R'n'D game engine may use an additional steelwall border
1449 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1452 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1454 for (x = 0; x < lev_fieldx; x++)
1456 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1457 BorderElement = EL_STEELWALL;
1459 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1465 void FloodFillLevelExt(int start_x, int start_y, int fill_element,
1466 int max_array_fieldx, int max_array_fieldy,
1467 short field[max_array_fieldx][max_array_fieldy],
1468 int max_fieldx, int max_fieldy)
1470 static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY];
1471 struct XY *check = xy_topdown;
1472 int old_element = field[start_x][start_y];
1475 // do nothing if start field already has the desired content
1476 if (old_element == fill_element)
1479 stack_buffer[stack_pos++] = (struct XY){ start_x, start_y };
1481 while (stack_pos > 0)
1483 struct XY current = stack_buffer[--stack_pos];
1486 field[current.x][current.y] = fill_element;
1488 for (i = 0; i < 4; i++)
1490 int x = current.x + check[i].x;
1491 int y = current.y + check[i].y;
1493 // check for stack buffer overflow (should not happen)
1494 if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY)
1495 Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug.");
1497 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1498 stack_buffer[stack_pos++] = (struct XY){ x, y };
1503 void FloodFillLevel(int from_x, int from_y, int fill_element,
1504 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1505 int max_fieldx, int max_fieldy)
1507 FloodFillLevelExt(from_x, from_y, fill_element,
1508 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1509 max_fieldx, max_fieldy);
1512 void SetRandomAnimationValue(int x, int y)
1514 gfx.anim_random_frame = GfxRandom[x][y];
1517 int getGraphicAnimationFrame(int graphic, int sync_frame)
1519 // animation synchronized with global frame counter, not move position
1520 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1521 sync_frame = FrameCounter;
1522 else if (graphic_info[graphic].anim_global_anim_sync)
1523 sync_frame = getGlobalAnimSyncFrame();
1525 return getAnimationFrame(graphic_info[graphic].anim_frames,
1526 graphic_info[graphic].anim_delay,
1527 graphic_info[graphic].anim_mode,
1528 graphic_info[graphic].anim_start_frame,
1532 int getGraphicAnimationFrameXY(int graphic, int lx, int ly)
1534 if (graphic_info[graphic].anim_mode & ANIM_TILED)
1536 struct GraphicInfo *g = &graphic_info[graphic];
1537 int xsize = MAX(1, g->anim_frames_per_line);
1538 int ysize = MAX(1, g->anim_frames / xsize);
1539 int xoffset = g->anim_start_frame % xsize;
1540 int yoffset = g->anim_start_frame % ysize;
1541 // may be needed if screen field is significantly larger than playfield
1542 int x = (lx + xoffset + SCR_FIELDX * xsize) % xsize;
1543 int y = (ly + yoffset + SCR_FIELDY * ysize) % ysize;
1544 int sync_frame = y * xsize + x;
1546 return sync_frame % g->anim_frames;
1548 else if (graphic_info[graphic].anim_mode & ANIM_RANDOM_STATIC)
1550 struct GraphicInfo *g = &graphic_info[graphic];
1551 // may be needed if screen field is significantly larger than playfield
1552 int x = (lx + SCR_FIELDX * lev_fieldx) % lev_fieldx;
1553 int y = (ly + SCR_FIELDY * lev_fieldy) % lev_fieldy;
1554 int sync_frame = GfxRandomStatic[x][y];
1556 return sync_frame % g->anim_frames;
1560 int sync_frame = (IN_LEV_FIELD(lx, ly) ? GfxFrame[lx][ly] : -1);
1562 return getGraphicAnimationFrame(graphic, sync_frame);
1566 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1568 struct GraphicInfo *g = &graphic_info[graphic];
1569 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1571 if (tilesize == gfx.standard_tile_size)
1572 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1573 else if (tilesize == game.tile_size)
1574 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1576 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1579 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1580 boolean get_backside)
1582 struct GraphicInfo *g = &graphic_info[graphic];
1583 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1584 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1586 if (g->offset_y == 0) // frames are ordered horizontally
1588 int max_width = g->anim_frames_per_line * g->width;
1589 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1591 *x = pos % max_width;
1592 *y = src_y % g->height + pos / max_width * g->height;
1594 else if (g->offset_x == 0) // frames are ordered vertically
1596 int max_height = g->anim_frames_per_line * g->height;
1597 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1599 *x = src_x % g->width + pos / max_height * g->width;
1600 *y = pos % max_height;
1602 else // frames are ordered diagonally
1604 *x = src_x + frame * g->offset_x;
1605 *y = src_y + frame * g->offset_y;
1609 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1610 Bitmap **bitmap, int *x, int *y,
1611 boolean get_backside)
1613 struct GraphicInfo *g = &graphic_info[graphic];
1615 // if no graphics defined at all, use fallback graphics
1616 if (g->bitmaps == NULL)
1617 *g = graphic_info[IMG_CHAR_EXCLAM];
1619 // if no in-game graphics defined, always use standard graphic size
1620 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1621 tilesize = TILESIZE;
1623 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1624 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1626 *x = *x * tilesize / g->tile_size;
1627 *y = *y * tilesize / g->tile_size;
1630 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1631 Bitmap **bitmap, int *x, int *y)
1633 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1636 void getFixedGraphicSource(int graphic, int frame,
1637 Bitmap **bitmap, int *x, int *y)
1639 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1642 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1644 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1647 void getGlobalAnimGraphicSource(int graphic, int frame,
1648 Bitmap **bitmap, int *x, int *y)
1650 struct GraphicInfo *g = &graphic_info[graphic];
1652 // if no graphics defined at all, use fallback graphics
1653 if (g->bitmaps == NULL)
1654 *g = graphic_info[IMG_CHAR_EXCLAM];
1656 // use original size graphics, if existing, else use standard size graphics
1657 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1658 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1660 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1662 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1665 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1666 int *x, int *y, boolean get_backside)
1668 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1672 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1674 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1677 void DrawGraphic(int x, int y, int graphic, int frame)
1680 if (!IN_SCR_FIELD(x, y))
1682 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1683 Debug("draw:DrawGraphic", "This should never happen!");
1689 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1692 MarkTileDirty(x, y);
1695 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1698 if (!IN_SCR_FIELD(x, y))
1700 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1702 Debug("draw:DrawFixedGraphic", "This should never happen!");
1708 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1710 MarkTileDirty(x, y);
1713 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1719 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1721 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1724 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1730 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1731 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1734 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1737 if (!IN_SCR_FIELD(x, y))
1739 Debug("draw:DrawGraphicThruMask", "x = %d, y = %d, graphic = %d",
1741 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1747 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1750 MarkTileDirty(x, y);
1753 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1756 if (!IN_SCR_FIELD(x, y))
1758 Debug("draw:DrawFixedGraphicThruMask", "x = %d, y = %d, graphic = %d",
1760 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1766 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1768 MarkTileDirty(x, y);
1771 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1777 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1779 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1783 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1784 int graphic, int frame)
1789 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1791 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1795 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1797 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1799 MarkTileDirty(x / tilesize, y / tilesize);
1802 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1805 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1806 graphic, frame, tilesize);
1807 MarkTileDirty(x / tilesize, y / tilesize);
1810 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1816 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1817 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1820 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1821 int frame, int tilesize)
1826 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1827 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1830 void DrawMiniGraphic(int x, int y, int graphic)
1832 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX, SY + y * MINI_TILEY, graphic);
1833 MarkTileDirty(x / 2, y / 2);
1836 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1841 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1842 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1845 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1846 int graphic, int frame,
1847 int cut_mode, int mask_mode)
1852 int width = TILEX, height = TILEY;
1855 if (dx || dy) // shifted graphic
1857 if (x < BX1) // object enters playfield from the left
1864 else if (x > BX2) // object enters playfield from the right
1870 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1876 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1878 else if (dx) // general horizontal movement
1879 MarkTileDirty(x + SIGN(dx), y);
1881 if (y < BY1) // object enters playfield from the top
1883 if (cut_mode == CUT_BELOW) // object completely above top border
1891 else if (y > BY2) // object enters playfield from the bottom
1897 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1903 else if (dy > 0 && cut_mode == CUT_ABOVE)
1905 if (y == BY2) // object completely above bottom border
1911 MarkTileDirty(x, y + 1);
1912 } // object leaves playfield to the bottom
1913 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1915 else if (dy) // general vertical movement
1916 MarkTileDirty(x, y + SIGN(dy));
1920 if (!IN_SCR_FIELD(x, y))
1922 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1924 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1930 width = width * TILESIZE_VAR / TILESIZE;
1931 height = height * TILESIZE_VAR / TILESIZE;
1932 cx = cx * TILESIZE_VAR / TILESIZE;
1933 cy = cy * TILESIZE_VAR / TILESIZE;
1934 dx = dx * TILESIZE_VAR / TILESIZE;
1935 dy = dy * TILESIZE_VAR / TILESIZE;
1937 if (width > 0 && height > 0)
1939 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1944 dst_x = FX + x * TILEX_VAR + dx;
1945 dst_y = FY + y * TILEY_VAR + dy;
1947 if (mask_mode == USE_MASKING)
1948 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1951 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1954 MarkTileDirty(x, y);
1958 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1959 int graphic, int frame,
1960 int cut_mode, int mask_mode)
1965 int width = TILEX_VAR, height = TILEY_VAR;
1968 int x2 = x + SIGN(dx);
1969 int y2 = y + SIGN(dy);
1971 // movement with two-tile animations must be sync'ed with movement position,
1972 // not with current GfxFrame (which can be higher when using slow movement)
1973 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1974 int anim_frames = graphic_info[graphic].anim_frames;
1976 // (we also need anim_delay here for movement animations with less frames)
1977 int anim_delay = graphic_info[graphic].anim_delay;
1978 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1980 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1981 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1983 // re-calculate animation frame for two-tile movement animation
1984 frame = getGraphicAnimationFrame(graphic, sync_frame);
1986 // check if movement start graphic inside screen area and should be drawn
1987 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1989 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1991 dst_x = FX + x1 * TILEX_VAR;
1992 dst_y = FY + y1 * TILEY_VAR;
1994 if (mask_mode == USE_MASKING)
1995 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1998 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
2001 MarkTileDirty(x1, y1);
2004 // check if movement end graphic inside screen area and should be drawn
2005 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
2007 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
2009 dst_x = FX + x2 * TILEX_VAR;
2010 dst_y = FY + y2 * TILEY_VAR;
2012 if (mask_mode == USE_MASKING)
2013 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
2016 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
2019 MarkTileDirty(x2, y2);
2023 static void DrawGraphicShifted(int x, int y, int dx, int dy,
2024 int graphic, int frame,
2025 int cut_mode, int mask_mode)
2029 DrawGraphic(x, y, graphic, frame);
2034 if (graphic_info[graphic].double_movement) // EM style movement images
2035 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2037 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2040 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
2041 int graphic, int frame, int cut_mode)
2043 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
2046 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
2047 int cut_mode, int mask_mode)
2049 int lx = LEVELX(x), ly = LEVELY(y);
2053 if (IN_LEV_FIELD(lx, ly))
2055 if (element == EL_EMPTY)
2056 element = GfxElementEmpty[lx][ly];
2058 SetRandomAnimationValue(lx, ly);
2060 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2061 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2063 // do not use double (EM style) movement graphic when not moving
2064 if (graphic_info[graphic].double_movement && !dx && !dy)
2066 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2067 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2070 if (game.use_masked_elements && (dx || dy))
2071 mask_mode = USE_MASKING;
2073 else // border element
2075 graphic = el2img(element);
2076 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2079 if (element == EL_EXPANDABLE_WALL)
2081 boolean left_stopped = FALSE, right_stopped = FALSE;
2083 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2084 left_stopped = TRUE;
2085 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2086 right_stopped = TRUE;
2088 if (left_stopped && right_stopped)
2090 else if (left_stopped)
2092 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2093 frame = graphic_info[graphic].anim_frames - 1;
2095 else if (right_stopped)
2097 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2098 frame = graphic_info[graphic].anim_frames - 1;
2103 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2104 else if (mask_mode == USE_MASKING)
2105 DrawGraphicThruMask(x, y, graphic, frame);
2107 DrawGraphic(x, y, graphic, frame);
2110 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2111 int cut_mode, int mask_mode)
2113 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2114 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2115 cut_mode, mask_mode);
2118 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2121 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2124 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2127 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2130 void DrawLevelElementThruMask(int x, int y, int element)
2132 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2135 void DrawLevelFieldThruMask(int x, int y)
2137 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2140 // !!! implementation of quicksand is totally broken !!!
2141 #define IS_CRUMBLED_TILE(x, y, e) \
2142 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2143 !IS_MOVING(x, y) || \
2144 (e) == EL_QUICKSAND_EMPTYING || \
2145 (e) == EL_QUICKSAND_FAST_EMPTYING))
2147 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2152 int width, height, cx, cy;
2153 int sx = SCREENX(x), sy = SCREENY(y);
2154 int crumbled_border_size = graphic_info[graphic].border_size;
2155 int crumbled_tile_size = graphic_info[graphic].tile_size;
2156 int crumbled_border_size_var =
2157 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2160 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2162 for (i = 1; i < 4; i++)
2164 int dxx = (i & 1 ? dx : 0);
2165 int dyy = (i & 2 ? dy : 0);
2168 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2171 // check if neighbour field is of same crumble type
2172 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2173 graphic_info[graphic].class ==
2174 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2176 // return if check prevents inner corner
2177 if (same == (dxx == dx && dyy == dy))
2181 // if we reach this point, we have an inner corner
2183 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2185 width = crumbled_border_size_var;
2186 height = crumbled_border_size_var;
2187 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2188 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2190 if (game.use_masked_elements)
2192 int graphic0 = el2img(EL_EMPTY);
2193 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2194 Bitmap *src_bitmap0;
2197 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2199 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2201 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2203 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2205 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2208 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2210 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2213 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2218 int width, height, bx, by, cx, cy;
2219 int sx = SCREENX(x), sy = SCREENY(y);
2220 int crumbled_border_size = graphic_info[graphic].border_size;
2221 int crumbled_tile_size = graphic_info[graphic].tile_size;
2222 int crumbled_border_size_var =
2223 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2224 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2227 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2229 // only needed when using masked elements
2230 int graphic0 = el2img(EL_EMPTY);
2231 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2232 Bitmap *src_bitmap0;
2235 if (game.use_masked_elements)
2236 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2238 // draw simple, sloppy, non-corner-accurate crumbled border
2240 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2241 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2242 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2243 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2245 if (game.use_masked_elements)
2247 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2249 FX + sx * TILEX_VAR + cx,
2250 FY + sy * TILEY_VAR + cy);
2252 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2254 FX + sx * TILEX_VAR + cx,
2255 FY + sy * TILEY_VAR + cy);
2258 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2260 FX + sx * TILEX_VAR + cx,
2261 FY + sy * TILEY_VAR + cy);
2263 // (remaining middle border part must be at least as big as corner part)
2264 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2265 crumbled_border_size_var >= TILESIZE_VAR / 3)
2268 // correct corners of crumbled border, if needed
2270 for (i = -1; i <= 1; i += 2)
2272 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2273 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2274 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2277 // check if neighbour field is of same crumble type
2278 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2279 graphic_info[graphic].class ==
2280 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2282 // no crumbled corner, but continued crumbled border
2284 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2285 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2286 int b1 = (i == 1 ? crumbled_border_size_var :
2287 TILESIZE_VAR - 2 * crumbled_border_size_var);
2289 width = crumbled_border_size_var;
2290 height = crumbled_border_size_var;
2292 if (dir == 1 || dir == 2)
2307 if (game.use_masked_elements)
2309 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2311 FX + sx * TILEX_VAR + cx,
2312 FY + sy * TILEY_VAR + cy);
2314 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2316 FX + sx * TILEX_VAR + cx,
2317 FY + sy * TILEY_VAR + cy);
2320 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2322 FX + sx * TILEX_VAR + cx,
2323 FY + sy * TILEY_VAR + cy);
2328 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2330 int sx = SCREENX(x), sy = SCREENY(y);
2333 struct XY *xy = xy_topdown;
2335 if (!IN_LEV_FIELD(x, y))
2338 element = TILE_GFX_ELEMENT(x, y);
2340 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2342 if (!IN_SCR_FIELD(sx, sy))
2345 // crumble field borders towards direct neighbour fields
2346 for (i = 0; i < 4; i++)
2348 int xx = x + xy[i].x;
2349 int yy = y + xy[i].y;
2351 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2354 // check if neighbour field is of same crumble type
2355 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2356 graphic_info[graphic].class ==
2357 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2360 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2363 // crumble inner field corners towards corner neighbour fields
2364 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2365 graphic_info[graphic].anim_frames == 2)
2367 for (i = 0; i < 4; i++)
2369 int dx = (i & 1 ? +1 : -1);
2370 int dy = (i & 2 ? +1 : -1);
2372 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2376 MarkTileDirty(sx, sy);
2378 else // center field is not crumbled -- crumble neighbour fields
2380 // crumble field borders of direct neighbour fields
2381 for (i = 0; i < 4; i++)
2383 int xx = x + xy[i].x;
2384 int yy = y + xy[i].y;
2385 int sxx = sx + xy[i].x;
2386 int syy = sy + xy[i].y;
2388 if (!IN_LEV_FIELD(xx, yy) ||
2389 !IN_SCR_FIELD(sxx, syy))
2392 // do not crumble fields that are being digged or snapped
2393 if (Tile[xx][yy] == EL_EMPTY ||
2394 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2397 element = TILE_GFX_ELEMENT(xx, yy);
2399 if (!IS_CRUMBLED_TILE(xx, yy, element))
2402 graphic = el_act2crm(element, ACTION_DEFAULT);
2404 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2406 MarkTileDirty(sxx, syy);
2409 // crumble inner field corners of corner neighbour fields
2410 for (i = 0; i < 4; i++)
2412 int dx = (i & 1 ? +1 : -1);
2413 int dy = (i & 2 ? +1 : -1);
2419 if (!IN_LEV_FIELD(xx, yy) ||
2420 !IN_SCR_FIELD(sxx, syy))
2423 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2426 element = TILE_GFX_ELEMENT(xx, yy);
2428 if (!IS_CRUMBLED_TILE(xx, yy, element))
2431 graphic = el_act2crm(element, ACTION_DEFAULT);
2433 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2434 graphic_info[graphic].anim_frames == 2)
2435 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2437 MarkTileDirty(sxx, syy);
2442 void DrawLevelFieldCrumbled(int x, int y)
2446 if (!IN_LEV_FIELD(x, y))
2449 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2450 GfxElement[x][y] != EL_UNDEFINED &&
2451 GFX_CRUMBLED(GfxElement[x][y]))
2453 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2458 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2460 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2463 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2466 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2467 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2468 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2469 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2470 int sx = SCREENX(x), sy = SCREENY(y);
2472 DrawScreenGraphic(sx, sy, graphic1, frame1);
2473 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2476 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2478 int sx = SCREENX(x), sy = SCREENY(y);
2479 struct XY *xy = xy_topdown;
2482 // crumble direct neighbour fields (required for field borders)
2483 for (i = 0; i < 4; i++)
2485 int xx = x + xy[i].x;
2486 int yy = y + xy[i].y;
2487 int sxx = sx + xy[i].x;
2488 int syy = sy + xy[i].y;
2490 if (!IN_LEV_FIELD(xx, yy) ||
2491 !IN_SCR_FIELD(sxx, syy) ||
2492 !GFX_CRUMBLED(Tile[xx][yy]) ||
2496 DrawLevelField(xx, yy);
2499 // crumble corner neighbour fields (required for inner field corners)
2500 for (i = 0; i < 4; i++)
2502 int dx = (i & 1 ? +1 : -1);
2503 int dy = (i & 2 ? +1 : -1);
2509 if (!IN_LEV_FIELD(xx, yy) ||
2510 !IN_SCR_FIELD(sxx, syy) ||
2511 !GFX_CRUMBLED(Tile[xx][yy]) ||
2515 int element = TILE_GFX_ELEMENT(xx, yy);
2516 int graphic = el_act2crm(element, ACTION_DEFAULT);
2518 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2519 graphic_info[graphic].anim_frames == 2)
2520 DrawLevelField(xx, yy);
2524 static int getBorderElement(int x, int y)
2528 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2529 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2530 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2531 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2532 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2533 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2534 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2536 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2537 int steel_position = (x == -1 && y == -1 ? 0 :
2538 x == lev_fieldx && y == -1 ? 1 :
2539 x == -1 && y == lev_fieldy ? 2 :
2540 x == lev_fieldx && y == lev_fieldy ? 3 :
2541 x == -1 || x == lev_fieldx ? 4 :
2542 y == -1 || y == lev_fieldy ? 5 : 6);
2544 return border[steel_position][steel_type];
2547 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2549 if (game.use_masked_elements)
2551 if (graphic != el2img(EL_EMPTY))
2552 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2554 DrawGraphicThruMask(x, y, graphic, frame);
2558 DrawGraphic(x, y, graphic, frame);
2562 void DrawLevelGraphic(int x, int y, int graphic, int frame)
2564 DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2567 void DrawScreenElement(int x, int y, int element)
2569 int mask_mode = NO_MASKING;
2571 if (game.use_masked_elements)
2573 int lx = LEVELX(x), ly = LEVELY(y);
2575 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2577 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2579 mask_mode = USE_MASKING;
2583 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2584 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2587 void DrawLevelElement(int x, int y, int element)
2589 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2590 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2593 void DrawScreenField(int x, int y)
2595 int lx = LEVELX(x), ly = LEVELY(y);
2596 int element, content;
2598 if (!IN_LEV_FIELD(lx, ly))
2600 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2603 element = getBorderElement(lx, ly);
2605 DrawScreenElement(x, y, element);
2610 element = Tile[lx][ly];
2611 content = Store[lx][ly];
2613 if (IS_MOVING(lx, ly))
2615 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2616 boolean cut_mode = NO_CUTTING;
2618 if (element == EL_QUICKSAND_EMPTYING ||
2619 element == EL_QUICKSAND_FAST_EMPTYING ||
2620 element == EL_MAGIC_WALL_EMPTYING ||
2621 element == EL_BD_MAGIC_WALL_EMPTYING ||
2622 element == EL_DC_MAGIC_WALL_EMPTYING ||
2623 element == EL_AMOEBA_DROPPING)
2624 cut_mode = CUT_ABOVE;
2625 else if (element == EL_QUICKSAND_FILLING ||
2626 element == EL_QUICKSAND_FAST_FILLING ||
2627 element == EL_MAGIC_WALL_FILLING ||
2628 element == EL_BD_MAGIC_WALL_FILLING ||
2629 element == EL_DC_MAGIC_WALL_FILLING)
2630 cut_mode = CUT_BELOW;
2632 if (cut_mode == CUT_ABOVE)
2633 DrawScreenElement(x, y, element);
2635 DrawScreenElement(x, y, EL_EMPTY);
2637 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2639 int dir = MovDir[lx][ly];
2640 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2641 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2643 if (IN_SCR_FIELD(newx, newy))
2644 DrawScreenElement(newx, newy, EL_EMPTY);
2648 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2649 else if (cut_mode == NO_CUTTING)
2650 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2653 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2655 if (cut_mode == CUT_BELOW &&
2656 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2657 DrawLevelElement(lx, ly + 1, element);
2660 if (content == EL_ACID)
2662 int dir = MovDir[lx][ly];
2663 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2664 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2666 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2668 // prevent target field from being drawn again (but without masking)
2669 // (this would happen if target field is scanned after moving element)
2670 Stop[newlx][newly] = TRUE;
2673 else if (IS_BLOCKED(lx, ly))
2678 boolean cut_mode = NO_CUTTING;
2679 int element_old, content_old;
2681 Blocked2Moving(lx, ly, &oldx, &oldy);
2684 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2685 MovDir[oldx][oldy] == MV_RIGHT);
2687 element_old = Tile[oldx][oldy];
2688 content_old = Store[oldx][oldy];
2690 if (element_old == EL_QUICKSAND_EMPTYING ||
2691 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2692 element_old == EL_MAGIC_WALL_EMPTYING ||
2693 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2694 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2695 element_old == EL_AMOEBA_DROPPING)
2696 cut_mode = CUT_ABOVE;
2698 DrawScreenElement(x, y, EL_EMPTY);
2701 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2703 else if (cut_mode == NO_CUTTING)
2704 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2707 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2710 else if (IS_DRAWABLE(element))
2711 DrawScreenElement(x, y, element);
2713 DrawScreenElement(x, y, EL_EMPTY);
2716 void DrawLevelField(int x, int y)
2718 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2719 DrawScreenField(SCREENX(x), SCREENY(y));
2720 else if (IS_MOVING(x, y))
2724 Moving2Blocked(x, y, &newx, &newy);
2725 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2726 DrawScreenField(SCREENX(newx), SCREENY(newy));
2728 else if (IS_BLOCKED(x, y))
2732 Blocked2Moving(x, y, &oldx, &oldy);
2733 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2734 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2738 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2739 int (*el2img_function)(int), boolean masked,
2740 int element_bits_draw)
2742 int element_base = map_mm_wall_element(element);
2743 int element_bits = (IS_DF_WALL(element) ?
2744 element - EL_DF_WALL_START :
2745 IS_MM_WALL(element) ?
2746 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2747 int graphic = el2img_function(element_base);
2748 int tilesize_draw = tilesize / 2;
2753 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2755 for (i = 0; i < 4; i++)
2757 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2758 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2760 if (!(element_bits_draw & (1 << i)))
2763 if (element_bits & (1 << i))
2766 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2767 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2769 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2770 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2775 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2776 tilesize_draw, tilesize_draw);
2781 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2782 boolean masked, int element_bits_draw)
2784 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2785 element, tilesize, el2edimg, masked, element_bits_draw);
2788 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2789 int (*el2img_function)(int))
2791 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2795 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2798 if (IS_MM_WALL(element))
2800 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2801 element, tilesize, el2edimg, masked, 0x000f);
2805 int graphic = el2edimg(element);
2808 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2810 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2814 void DrawSizedElement(int x, int y, int element, int tilesize)
2816 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2819 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2821 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2824 void DrawMiniElement(int x, int y, int element)
2828 graphic = el2edimg(element);
2829 DrawMiniGraphic(x, y, graphic);
2832 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2835 int x = sx + scroll_x, y = sy + scroll_y;
2837 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2838 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2839 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2840 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2842 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2845 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2847 int x = sx + scroll_x, y = sy + scroll_y;
2849 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2850 DrawMiniElement(sx, sy, EL_EMPTY);
2851 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2852 DrawMiniElement(sx, sy, Tile[x][y]);
2854 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2857 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2858 int x, int y, int xsize, int ysize,
2859 int tile_width, int tile_height)
2863 int dst_x = startx + x * tile_width;
2864 int dst_y = starty + y * tile_height;
2865 int width = graphic_info[graphic].width;
2866 int height = graphic_info[graphic].height;
2867 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2868 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2869 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2870 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2871 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2872 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2873 boolean draw_masked = graphic_info[graphic].draw_masked;
2875 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2877 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2879 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2883 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2884 inner_sx + (x - 1) * tile_width % inner_width);
2885 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2886 inner_sy + (y - 1) * tile_height % inner_height);
2889 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2892 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2896 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2897 int x, int y, int xsize, int ysize,
2900 int font_width = getFontWidth(font_nr);
2901 int font_height = getFontHeight(font_nr);
2903 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2904 font_width, font_height);
2907 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2909 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2910 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2911 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2912 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2913 boolean no_delay = (tape.warp_forward);
2914 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2915 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2916 DelayCounter anim_delay = { anim_delay_value };
2917 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2918 int font_width = getFontWidth(font_nr);
2919 int font_height = getFontHeight(font_nr);
2920 int max_xsize = level.envelope[envelope_nr].xsize;
2921 int max_ysize = level.envelope[envelope_nr].ysize;
2922 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2923 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2924 int xend = max_xsize;
2925 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2926 int xstep = (xstart < xend ? 1 : 0);
2927 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2929 int end = MAX(xend - xstart, yend - ystart);
2932 for (i = start; i <= end; i++)
2934 int last_frame = end; // last frame of this "for" loop
2935 int x = xstart + i * xstep;
2936 int y = ystart + i * ystep;
2937 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2938 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2939 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2940 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2943 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2945 BlitScreenToBitmap(backbuffer);
2947 SetDrawtoField(DRAW_TO_BACKBUFFER);
2949 for (yy = 0; yy < ysize; yy++)
2950 for (xx = 0; xx < xsize; xx++)
2951 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2953 DrawTextArea(sx + font_width, sy + font_height,
2954 level.envelope[envelope_nr].text, font_nr, max_xsize,
2955 xsize - 2, ysize - 2, 0, mask_mode,
2956 level.envelope[envelope_nr].autowrap,
2957 level.envelope[envelope_nr].centered, FALSE);
2959 redraw_mask |= REDRAW_FIELD;
2962 SkipUntilDelayReached(&anim_delay, &i, last_frame);
2965 ClearAutoRepeatKeyEvents();
2968 void ShowEnvelope(int envelope_nr)
2970 int element = EL_ENVELOPE_1 + envelope_nr;
2971 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2972 int sound_opening = element_info[element].sound[ACTION_OPENING];
2973 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2974 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2975 boolean no_delay = (tape.warp_forward);
2976 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2977 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2978 int anim_mode = graphic_info[graphic].anim_mode;
2979 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2980 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2981 boolean overlay_enabled = GetOverlayEnabled();
2983 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2985 SetOverlayEnabled(FALSE);
2988 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2990 if (anim_mode == ANIM_DEFAULT)
2991 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2993 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2996 Delay_WithScreenUpdates(wait_delay_value);
2998 WaitForEventToContinue();
3001 SetOverlayEnabled(overlay_enabled);
3003 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3005 if (anim_mode != ANIM_NONE)
3006 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
3008 if (anim_mode == ANIM_DEFAULT)
3009 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
3011 game.envelope_active = FALSE;
3013 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3015 redraw_mask |= REDRAW_FIELD;
3019 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
3020 int xsize, int ysize)
3022 if (!global.use_envelope_request)
3025 if (request.bitmap == NULL ||
3026 xsize > request.xsize ||
3027 ysize > request.ysize)
3029 if (request.bitmap != NULL)
3030 FreeBitmap(request.bitmap);
3032 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
3034 SDL_Surface *surface = request.bitmap->surface;
3036 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
3037 Fail("SDLGetNativeSurface() failed");
3040 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3042 // create masked surface for request bitmap, if needed
3043 if (graphic_info[IMG_BACKGROUND_REQUEST].draw_masked)
3045 SDL_Surface *surface = request.bitmap->surface;
3046 SDL_Surface *surface_masked = request.bitmap->surface_masked;
3048 SDLBlitSurface(surface, surface_masked, 0, 0, xsize, ysize, 0, 0);
3049 SDL_SetColorKey(surface_masked, SET_TRANSPARENT_PIXEL,
3050 SDL_MapRGB(surface_masked->format, 0x00, 0x00, 0x00));
3053 SDLFreeBitmapTextures(request.bitmap);
3054 SDLCreateBitmapTextures(request.bitmap);
3056 // set envelope request run-time values
3059 request.xsize = xsize;
3060 request.ysize = ysize;
3063 void DrawEnvelopeRequestToScreen(int drawing_target)
3065 if (global.use_envelope_request &&
3066 game.request_active_or_moving &&
3067 drawing_target == DRAW_TO_SCREEN)
3069 if (graphic_info[IMG_BACKGROUND_REQUEST].draw_masked)
3070 BlitToScreenMasked(request.bitmap, 0, 0, request.xsize, request.ysize,
3071 request.sx, request.sy);
3073 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3074 request.sx, request.sy);
3078 static void setRequestBasePosition(int *x, int *y)
3080 int sx_base, sy_base;
3082 if (request.x != -1)
3083 sx_base = request.x;
3084 else if (request.align == ALIGN_LEFT)
3086 else if (request.align == ALIGN_RIGHT)
3087 sx_base = SX + SXSIZE;
3089 sx_base = SX + SXSIZE / 2;
3091 if (request.y != -1)
3092 sy_base = request.y;
3093 else if (request.valign == VALIGN_TOP)
3095 else if (request.valign == VALIGN_BOTTOM)
3096 sy_base = SY + SYSIZE;
3098 sy_base = SY + SYSIZE / 2;
3104 static void setRequestPositionExt(int *x, int *y, int width, int height,
3105 boolean add_border_size)
3107 int border_size = request.border_size;
3108 int sx_base, sy_base;
3111 setRequestBasePosition(&sx_base, &sy_base);
3113 if (request.align == ALIGN_LEFT)
3115 else if (request.align == ALIGN_RIGHT)
3116 sx = sx_base - width;
3118 sx = sx_base - width / 2;
3120 if (request.valign == VALIGN_TOP)
3122 else if (request.valign == VALIGN_BOTTOM)
3123 sy = sy_base - height;
3125 sy = sy_base - height / 2;
3127 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3128 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3130 if (add_border_size)
3140 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3142 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3145 static void DrawEnvelopeRequest(char *text, 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 envelope request to temporary bitmap
3207 drawto = bitmap_db_store_2;
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 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3251 if (text_door_style)
3252 free(text_door_style);
3255 static void AnimateEnvelopeRequest(int anim_mode, int action)
3257 int delay_value_normal = request.step_delay;
3258 int delay_value_fast = delay_value_normal / 2;
3259 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3260 boolean no_delay = (tape.warp_forward);
3261 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3262 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3263 DelayCounter anim_delay = { anim_delay_value };
3265 int tile_size = MAX(request.step_offset, 1);
3266 int max_xsize = request.width / tile_size;
3267 int max_ysize = request.height / tile_size;
3268 int max_xsize_inner = max_xsize - 2;
3269 int max_ysize_inner = max_ysize - 2;
3271 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3272 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3273 int xend = max_xsize_inner;
3274 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3275 int xstep = (xstart < xend ? 1 : 0);
3276 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3278 int end = MAX(xend - xstart, yend - ystart);
3281 if (setup.quick_doors)
3288 for (i = start; i <= end; i++)
3290 int last_frame = end; // last frame of this "for" loop
3291 int x = xstart + i * xstep;
3292 int y = ystart + i * ystep;
3293 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3294 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3295 int xsize_size_left = (xsize - 1) * tile_size;
3296 int ysize_size_top = (ysize - 1) * tile_size;
3297 int max_xsize_pos = (max_xsize - 1) * tile_size;
3298 int max_ysize_pos = (max_ysize - 1) * tile_size;
3299 int width = xsize * tile_size;
3300 int height = ysize * tile_size;
3305 setRequestPosition(&src_x, &src_y, FALSE);
3306 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3308 for (yy = 0; yy < 2; yy++)
3310 for (xx = 0; xx < 2; xx++)
3312 int src_xx = src_x + xx * max_xsize_pos;
3313 int src_yy = src_y + yy * max_ysize_pos;
3314 int dst_xx = dst_x + xx * xsize_size_left;
3315 int dst_yy = dst_y + yy * ysize_size_top;
3316 int xx_size = (xx ? tile_size : xsize_size_left);
3317 int yy_size = (yy ? tile_size : ysize_size_top);
3319 BlitBitmap(bitmap_db_store_2, bitmap_db_store_1,
3320 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3324 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, dst_x, dst_y,
3327 redraw_mask |= REDRAW_FIELD;
3331 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3334 ClearAutoRepeatKeyEvents();
3337 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3339 int graphic = IMG_BACKGROUND_REQUEST;
3340 int sound_opening = SND_REQUEST_OPENING;
3341 int sound_closing = SND_REQUEST_CLOSING;
3342 int anim_mode_1 = request.anim_mode; // (higher priority)
3343 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3344 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3345 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3346 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3348 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3350 if (action == ACTION_OPENING)
3352 DrawEnvelopeRequest(text, req_state);
3354 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3356 if (anim_mode == ANIM_DEFAULT)
3357 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3359 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3363 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3365 if (anim_mode != ANIM_NONE)
3366 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3368 if (anim_mode == ANIM_DEFAULT)
3369 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3372 game.envelope_active = FALSE;
3375 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3377 if (IS_MM_WALL(element))
3379 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3385 int graphic = el2preimg(element);
3387 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3388 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3393 void DrawLevel(int draw_background_mask)
3397 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3398 SetDrawBackgroundMask(draw_background_mask);
3402 for (x = BX1; x <= BX2; x++)
3403 for (y = BY1; y <= BY2; y++)
3404 DrawScreenField(x, y);
3406 redraw_mask |= REDRAW_FIELD;
3409 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3414 for (x = 0; x < size_x; x++)
3415 for (y = 0; y < size_y; y++)
3416 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3418 redraw_mask |= REDRAW_FIELD;
3421 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3425 for (x = 0; x < size_x; x++)
3426 for (y = 0; y < size_y; y++)
3427 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3429 redraw_mask |= REDRAW_FIELD;
3432 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3434 boolean show_level_border = (BorderElement != EL_EMPTY);
3435 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3436 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3437 int tile_size = preview.tile_size;
3438 int preview_width = preview.xsize * tile_size;
3439 int preview_height = preview.ysize * tile_size;
3440 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3441 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3442 int real_preview_width = real_preview_xsize * tile_size;
3443 int real_preview_height = real_preview_ysize * tile_size;
3444 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3445 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3448 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3451 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3453 dst_x += (preview_width - real_preview_width) / 2;
3454 dst_y += (preview_height - real_preview_height) / 2;
3456 for (x = 0; x < real_preview_xsize; x++)
3458 for (y = 0; y < real_preview_ysize; y++)
3460 int lx = from_x + x + (show_level_border ? -1 : 0);
3461 int ly = from_y + y + (show_level_border ? -1 : 0);
3462 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3463 getBorderElement(lx, ly));
3465 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3466 element, tile_size);
3470 redraw_mask |= REDRAW_FIELD;
3473 #define MICROLABEL_EMPTY 0
3474 #define MICROLABEL_LEVEL_NAME 1
3475 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3476 #define MICROLABEL_LEVEL_AUTHOR 3
3477 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3478 #define MICROLABEL_IMPORTED_FROM 5
3479 #define MICROLABEL_IMPORTED_BY_HEAD 6
3480 #define MICROLABEL_IMPORTED_BY 7
3482 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3484 int max_text_width = SXSIZE;
3485 int font_width = getFontWidth(font_nr);
3487 if (pos->align == ALIGN_CENTER)
3488 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3489 else if (pos->align == ALIGN_RIGHT)
3490 max_text_width = pos->x;
3492 max_text_width = SXSIZE - pos->x;
3494 return max_text_width / font_width;
3497 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3499 char label_text[MAX_OUTPUT_LINESIZE + 1];
3500 int max_len_label_text;
3501 int font_nr = pos->font;
3504 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3507 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3508 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3509 mode == MICROLABEL_IMPORTED_BY_HEAD)
3510 font_nr = pos->font_alt;
3512 max_len_label_text = getMaxTextLength(pos, font_nr);
3514 if (pos->size != -1)
3515 max_len_label_text = pos->size;
3517 for (i = 0; i < max_len_label_text; i++)
3518 label_text[i] = ' ';
3519 label_text[max_len_label_text] = '\0';
3521 if (strlen(label_text) > 0)
3522 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3525 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3526 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3527 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3528 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3529 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3530 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3531 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3532 max_len_label_text);
3533 label_text[max_len_label_text] = '\0';
3535 if (strlen(label_text) > 0)
3536 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3538 redraw_mask |= REDRAW_FIELD;
3541 static void DrawPreviewLevelLabel(int mode)
3543 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3546 static void DrawPreviewLevelInfo(int mode)
3548 if (mode == MICROLABEL_LEVEL_NAME)
3549 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3550 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3551 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3554 static void DrawPreviewLevelExt(boolean restart)
3556 static DelayCounter scroll_delay = { 0 };
3557 static DelayCounter label_delay = { 0 };
3558 static int from_x, from_y, scroll_direction;
3559 static int label_state, label_counter;
3560 boolean show_level_border = (BorderElement != EL_EMPTY);
3561 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3562 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3564 scroll_delay.value = preview.step_delay;
3565 label_delay.value = MICROLEVEL_LABEL_DELAY;
3572 if (preview.anim_mode == ANIM_CENTERED)
3574 if (level_xsize > preview.xsize)
3575 from_x = (level_xsize - preview.xsize) / 2;
3576 if (level_ysize > preview.ysize)
3577 from_y = (level_ysize - preview.ysize) / 2;
3580 from_x += preview.xoffset;
3581 from_y += preview.yoffset;
3583 scroll_direction = MV_RIGHT;
3587 DrawPreviewLevelPlayfield(from_x, from_y);
3588 DrawPreviewLevelLabel(label_state);
3590 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3591 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3593 // initialize delay counters
3594 ResetDelayCounter(&scroll_delay);
3595 ResetDelayCounter(&label_delay);
3597 if (leveldir_current->name)
3599 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3600 char label_text[MAX_OUTPUT_LINESIZE + 1];
3601 int font_nr = pos->font;
3602 int max_len_label_text = getMaxTextLength(pos, font_nr);
3604 if (pos->size != -1)
3605 max_len_label_text = pos->size;
3607 strncpy(label_text, leveldir_current->name, max_len_label_text);
3608 label_text[max_len_label_text] = '\0';
3610 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3611 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3617 // scroll preview level, if needed
3618 if (preview.anim_mode != ANIM_NONE &&
3619 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3620 DelayReached(&scroll_delay))
3622 switch (scroll_direction)
3627 from_x -= preview.step_offset;
3628 from_x = (from_x < 0 ? 0 : from_x);
3631 scroll_direction = MV_UP;
3635 if (from_x < level_xsize - preview.xsize)
3637 from_x += preview.step_offset;
3638 from_x = (from_x > level_xsize - preview.xsize ?
3639 level_xsize - preview.xsize : from_x);
3642 scroll_direction = MV_DOWN;
3648 from_y -= preview.step_offset;
3649 from_y = (from_y < 0 ? 0 : from_y);
3652 scroll_direction = MV_RIGHT;
3656 if (from_y < level_ysize - preview.ysize)
3658 from_y += preview.step_offset;
3659 from_y = (from_y > level_ysize - preview.ysize ?
3660 level_ysize - preview.ysize : from_y);
3663 scroll_direction = MV_LEFT;
3670 DrawPreviewLevelPlayfield(from_x, from_y);
3673 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3674 // redraw micro level label, if needed
3675 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3676 !strEqual(level.author, ANONYMOUS_NAME) &&
3677 !strEqual(level.author, leveldir_current->name) &&
3678 DelayReached(&label_delay))
3680 int max_label_counter = 23;
3682 if (leveldir_current->imported_from != NULL &&
3683 strlen(leveldir_current->imported_from) > 0)
3684 max_label_counter += 14;
3685 if (leveldir_current->imported_by != NULL &&
3686 strlen(leveldir_current->imported_by) > 0)
3687 max_label_counter += 14;
3689 label_counter = (label_counter + 1) % max_label_counter;
3690 label_state = (label_counter >= 0 && label_counter <= 7 ?
3691 MICROLABEL_LEVEL_NAME :
3692 label_counter >= 9 && label_counter <= 12 ?
3693 MICROLABEL_LEVEL_AUTHOR_HEAD :
3694 label_counter >= 14 && label_counter <= 21 ?
3695 MICROLABEL_LEVEL_AUTHOR :
3696 label_counter >= 23 && label_counter <= 26 ?
3697 MICROLABEL_IMPORTED_FROM_HEAD :
3698 label_counter >= 28 && label_counter <= 35 ?
3699 MICROLABEL_IMPORTED_FROM :
3700 label_counter >= 37 && label_counter <= 40 ?
3701 MICROLABEL_IMPORTED_BY_HEAD :
3702 label_counter >= 42 && label_counter <= 49 ?
3703 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3705 if (leveldir_current->imported_from == NULL &&
3706 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3707 label_state == MICROLABEL_IMPORTED_FROM))
3708 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3709 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3711 DrawPreviewLevelLabel(label_state);
3715 void DrawPreviewPlayers(void)
3717 if (game_status != GAME_MODE_MAIN)
3720 // do not draw preview players if level preview redefined, but players aren't
3721 if (preview.redefined && !menu.main.preview_players.redefined)
3724 boolean player_found[MAX_PLAYERS];
3725 int num_players = 0;
3728 for (i = 0; i < MAX_PLAYERS; i++)
3729 player_found[i] = FALSE;
3731 // check which players can be found in the level (simple approach)
3732 for (x = 0; x < lev_fieldx; x++)
3734 for (y = 0; y < lev_fieldy; y++)
3736 int element = level.field[x][y];
3738 if (IS_PLAYER_ELEMENT(element))
3740 int player_nr = GET_PLAYER_NR(element);
3742 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3744 if (!player_found[player_nr])
3747 player_found[player_nr] = TRUE;
3752 struct TextPosInfo *pos = &menu.main.preview_players;
3753 int tile_size = pos->tile_size;
3754 int border_size = pos->border_size;
3755 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3756 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3757 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3758 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3759 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3760 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3761 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3762 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3763 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3764 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3765 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3766 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3768 // clear area in which the players will be drawn
3769 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3770 max_players_width, max_players_height);
3772 if (!network.enabled && !setup.team_mode)
3775 // only draw players if level is suited for team mode
3776 if (num_players < 2)
3779 // draw all players that were found in the level
3780 for (i = 0; i < MAX_PLAYERS; i++)
3782 if (player_found[i])
3784 int graphic = el2img(EL_PLAYER_1 + i);
3786 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3788 xpos += player_xoffset;
3789 ypos += player_yoffset;
3794 void DrawPreviewLevelInitial(void)
3796 DrawPreviewLevelExt(TRUE);
3797 DrawPreviewPlayers();
3800 void DrawPreviewLevelAnimation(void)
3802 DrawPreviewLevelExt(FALSE);
3805 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3806 int border_size, int font_nr)
3808 int graphic = el2img(EL_PLAYER_1 + player_nr);
3809 int font_height = getFontHeight(font_nr);
3810 int player_height = MAX(tile_size, font_height);
3811 int xoffset_text = tile_size + border_size;
3812 int yoffset_text = (player_height - font_height) / 2;
3813 int yoffset_graphic = (player_height - tile_size) / 2;
3814 char *player_name = getNetworkPlayerName(player_nr + 1);
3816 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3818 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3821 static void DrawNetworkPlayersExt(boolean force)
3823 if (game_status != GAME_MODE_MAIN)
3826 if (!network.connected && !force)
3829 // do not draw network players if level preview redefined, but players aren't
3830 if (preview.redefined && !menu.main.network_players.redefined)
3833 int num_players = 0;
3836 for (i = 0; i < MAX_PLAYERS; i++)
3837 if (stored_player[i].connected_network)
3840 struct TextPosInfo *pos = &menu.main.network_players;
3841 int tile_size = pos->tile_size;
3842 int border_size = pos->border_size;
3843 int xoffset_text = tile_size + border_size;
3844 int font_nr = pos->font;
3845 int font_width = getFontWidth(font_nr);
3846 int font_height = getFontHeight(font_nr);
3847 int player_height = MAX(tile_size, font_height);
3848 int player_yoffset = player_height + border_size;
3849 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3850 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3851 int all_players_height = num_players * player_yoffset - border_size;
3852 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3853 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3854 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3856 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3857 max_players_width, max_players_height);
3859 // first draw local network player ...
3860 for (i = 0; i < MAX_PLAYERS; i++)
3862 if (stored_player[i].connected_network &&
3863 stored_player[i].connected_locally)
3865 char *player_name = getNetworkPlayerName(i + 1);
3866 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3867 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3869 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3871 ypos += player_yoffset;
3875 // ... then draw all other network players
3876 for (i = 0; i < MAX_PLAYERS; i++)
3878 if (stored_player[i].connected_network &&
3879 !stored_player[i].connected_locally)
3881 char *player_name = getNetworkPlayerName(i + 1);
3882 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3883 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3885 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3887 ypos += player_yoffset;
3892 void DrawNetworkPlayers(void)
3894 DrawNetworkPlayersExt(FALSE);
3897 void ClearNetworkPlayers(void)
3899 DrawNetworkPlayersExt(TRUE);
3902 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3903 int graphic, int lx, int ly,
3906 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3908 if (mask_mode == USE_MASKING)
3909 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3911 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3914 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3915 int graphic, int sync_frame, int mask_mode)
3917 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3919 if (mask_mode == USE_MASKING)
3920 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3922 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3925 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3926 int graphic, int sync_frame, int tilesize,
3929 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3931 if (mask_mode == USE_MASKING)
3932 DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
3934 DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
3937 static void DrawGraphicAnimation(int x, int y, int graphic)
3939 int lx = LEVELX(x), ly = LEVELY(y);
3940 int mask_mode = NO_MASKING;
3942 if (!IN_SCR_FIELD(x, y))
3945 if (game.use_masked_elements)
3947 if (Tile[lx][ly] != EL_EMPTY)
3949 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3951 mask_mode = USE_MASKING;
3955 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3956 graphic, lx, ly, mask_mode);
3958 MarkTileDirty(x, y);
3961 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3963 int lx = LEVELX(x), ly = LEVELY(y);
3964 int mask_mode = NO_MASKING;
3966 if (!IN_SCR_FIELD(x, y))
3969 if (game.use_masked_elements)
3971 if (Tile[lx][ly] != EL_EMPTY)
3973 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3975 mask_mode = USE_MASKING;
3979 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3980 graphic, lx, ly, mask_mode);
3982 MarkTileDirty(x, y);
3985 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3987 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3990 void DrawLevelElementAnimation(int x, int y, int element)
3992 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3994 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3997 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3999 int sx = SCREENX(x), sy = SCREENY(y);
4001 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4004 if (Tile[x][y] == EL_EMPTY)
4005 graphic = el2img(GfxElementEmpty[x][y]);
4007 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4010 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4013 DrawGraphicAnimation(sx, sy, graphic);
4016 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4017 DrawLevelFieldCrumbled(x, y);
4019 if (GFX_CRUMBLED(Tile[x][y]))
4020 DrawLevelFieldCrumbled(x, y);
4024 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4026 int sx = SCREENX(x), sy = SCREENY(y);
4029 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4032 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4034 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4037 DrawGraphicAnimation(sx, sy, graphic);
4039 if (GFX_CRUMBLED(element))
4040 DrawLevelFieldCrumbled(x, y);
4043 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4045 if (player->use_murphy)
4047 // this works only because currently only one player can be "murphy" ...
4048 static int last_horizontal_dir = MV_LEFT;
4049 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4051 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4052 last_horizontal_dir = move_dir;
4054 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4056 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4058 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4064 return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4067 static boolean equalGraphics(int graphic1, int graphic2)
4069 struct GraphicInfo *g1 = &graphic_info[graphic1];
4070 struct GraphicInfo *g2 = &graphic_info[graphic2];
4072 return (g1->bitmap == g2->bitmap &&
4073 g1->src_x == g2->src_x &&
4074 g1->src_y == g2->src_y &&
4075 g1->anim_frames == g2->anim_frames &&
4076 g1->anim_delay == g2->anim_delay &&
4077 g1->anim_mode == g2->anim_mode);
4080 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4084 DRAW_PLAYER_STAGE_INIT = 0,
4085 DRAW_PLAYER_STAGE_LAST_FIELD,
4086 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4087 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4088 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4089 DRAW_PLAYER_STAGE_PLAYER,
4091 DRAW_PLAYER_STAGE_PLAYER,
4092 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4094 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4095 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4097 NUM_DRAW_PLAYER_STAGES
4100 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4102 static int static_last_player_graphic[MAX_PLAYERS];
4103 static int static_last_player_frame[MAX_PLAYERS];
4104 static boolean static_player_is_opaque[MAX_PLAYERS];
4105 static boolean draw_player[MAX_PLAYERS];
4106 int pnr = player->index_nr;
4108 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4110 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4111 static_last_player_frame[pnr] = player->Frame;
4112 static_player_is_opaque[pnr] = FALSE;
4114 draw_player[pnr] = TRUE;
4117 if (!draw_player[pnr])
4121 if (!IN_LEV_FIELD(player->jx, player->jy))
4123 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4124 Debug("draw:DrawPlayerExt", "This should never happen!");
4126 draw_player[pnr] = FALSE;
4132 int last_player_graphic = static_last_player_graphic[pnr];
4133 int last_player_frame = static_last_player_frame[pnr];
4134 boolean player_is_opaque = static_player_is_opaque[pnr];
4136 int jx = player->jx;
4137 int jy = player->jy;
4138 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4139 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4140 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4141 int last_jx = (player->is_moving ? jx - dx : jx);
4142 int last_jy = (player->is_moving ? jy - dy : jy);
4143 int next_jx = jx + dx;
4144 int next_jy = jy + dy;
4145 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4146 int sx = SCREENX(jx);
4147 int sy = SCREENY(jy);
4148 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4149 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4150 int element = Tile[jx][jy];
4151 int last_element = Tile[last_jx][last_jy];
4152 int action = (player->is_pushing ? ACTION_PUSHING :
4153 player->is_digging ? ACTION_DIGGING :
4154 player->is_collecting ? ACTION_COLLECTING :
4155 player->is_moving ? ACTION_MOVING :
4156 player->is_snapping ? ACTION_SNAPPING :
4157 player->is_dropping ? ACTION_DROPPING :
4158 player->is_waiting ? player->action_waiting :
4161 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4163 // ------------------------------------------------------------------------
4164 // initialize drawing the player
4165 // ------------------------------------------------------------------------
4167 draw_player[pnr] = FALSE;
4169 // GfxElement[][] is set to the element the player is digging or collecting;
4170 // remove also for off-screen player if the player is not moving anymore
4171 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4172 GfxElement[jx][jy] = EL_UNDEFINED;
4174 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4177 if (element == EL_EXPLOSION)
4180 InitPlayerGfxAnimation(player, action, move_dir);
4182 draw_player[pnr] = TRUE;
4184 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4186 // ------------------------------------------------------------------------
4187 // draw things in the field the player is leaving, if needed
4188 // ------------------------------------------------------------------------
4190 if (!IN_SCR_FIELD(sx, sy))
4191 draw_player[pnr] = FALSE;
4193 if (!player->is_moving)
4196 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4198 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4200 if (last_element == EL_DYNAMITE_ACTIVE ||
4201 last_element == EL_EM_DYNAMITE_ACTIVE ||
4202 last_element == EL_SP_DISK_RED_ACTIVE)
4203 DrawDynamite(last_jx, last_jy);
4205 DrawLevelFieldThruMask(last_jx, last_jy);
4207 else 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 DrawLevelField(last_jx, last_jy);
4214 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4216 // ------------------------------------------------------------------------
4217 // draw things behind the player, if needed
4218 // ------------------------------------------------------------------------
4222 DrawLevelElement(jx, jy, Back[jx][jy]);
4227 if (IS_ACTIVE_BOMB(element))
4229 DrawLevelElement(jx, jy, EL_EMPTY);
4234 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4236 int old_element = GfxElement[jx][jy];
4237 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4238 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4240 if (GFX_CRUMBLED(old_element))
4241 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4243 DrawScreenGraphic(sx, sy, old_graphic, frame);
4245 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4246 static_player_is_opaque[pnr] = TRUE;
4250 GfxElement[jx][jy] = EL_UNDEFINED;
4252 // make sure that pushed elements are drawn with correct frame rate
4253 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4255 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4256 GfxFrame[jx][jy] = player->StepFrame;
4258 DrawLevelField(jx, jy);
4261 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4263 // ------------------------------------------------------------------------
4264 // draw things the player is pushing, if needed
4265 // ------------------------------------------------------------------------
4267 if (!player->is_pushing || !player->is_moving)
4270 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4273 int gfx_frame = GfxFrame[jx][jy];
4275 if (!IS_MOVING(jx, jy)) // push movement already finished
4277 element = Tile[next_jx][next_jy];
4278 gfx_frame = GfxFrame[next_jx][next_jy];
4281 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4282 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4283 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4285 // draw background element under pushed element (like the Sokoban field)
4286 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4288 // this allows transparent pushing animation over non-black background
4291 DrawLevelElement(jx, jy, Back[jx][jy]);
4293 DrawLevelElement(jx, jy, EL_EMPTY);
4296 if (Back[next_jx][next_jy])
4297 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4299 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4301 int px = SCREENX(jx), py = SCREENY(jy);
4302 int pxx = (TILEX - ABS(sxx)) * dx;
4303 int pyy = (TILEY - ABS(syy)) * dy;
4306 // do not draw (EM style) pushing animation when pushing is finished
4307 // (two-tile animations usually do not contain start and end frame)
4308 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4309 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4311 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4313 // masked drawing is needed for EMC style (double) movement graphics
4314 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4315 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4318 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4320 // ------------------------------------------------------------------------
4321 // draw player himself
4322 // ------------------------------------------------------------------------
4324 int graphic = getPlayerGraphic(player, move_dir);
4326 // in the case of changed player action or direction, prevent the current
4327 // animation frame from being restarted for identical animations
4328 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4329 player->Frame = last_player_frame;
4331 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4333 if (player_is_opaque)
4334 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4336 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4338 if (SHIELD_ON(player))
4340 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4341 IMG_SHIELD_NORMAL_ACTIVE);
4342 frame = getGraphicAnimationFrame(graphic, -1);
4344 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4347 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4349 // ------------------------------------------------------------------------
4350 // draw things in front of player (active dynamite or dynabombs)
4351 // ------------------------------------------------------------------------
4353 if (IS_ACTIVE_BOMB(element))
4355 int graphic = el2img(element);
4356 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4358 if (game.emulation == EMU_SUPAPLEX)
4359 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4361 DrawGraphicThruMask(sx, sy, graphic, frame);
4364 if (player_is_moving && last_element == EL_EXPLOSION)
4366 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4367 GfxElement[last_jx][last_jy] : EL_EMPTY);
4368 int graphic = el_act2img(element, ACTION_EXPLODING);
4369 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4370 int phase = ExplodePhase[last_jx][last_jy] - 1;
4371 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4374 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4377 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4379 // ------------------------------------------------------------------------
4380 // draw elements the player is just walking/passing through/under
4381 // ------------------------------------------------------------------------
4383 if (player_is_moving)
4385 // handle the field the player is leaving ...
4386 if (IS_ACCESSIBLE_INSIDE(last_element))
4387 DrawLevelField(last_jx, last_jy);
4388 else if (IS_ACCESSIBLE_UNDER(last_element))
4389 DrawLevelFieldThruMask(last_jx, last_jy);
4392 // do not redraw accessible elements if the player is just pushing them
4393 if (!player_is_moving || !player->is_pushing)
4395 // ... and the field the player is entering
4396 if (IS_ACCESSIBLE_INSIDE(element))
4397 DrawLevelField(jx, jy);
4398 else if (IS_ACCESSIBLE_UNDER(element))
4399 DrawLevelFieldThruMask(jx, jy);
4402 MarkTileDirty(sx, sy);
4406 void DrawPlayer(struct PlayerInfo *player)
4410 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4411 DrawPlayerExt(player, i);
4414 void DrawAllPlayers(void)
4418 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4419 for (j = 0; j < MAX_PLAYERS; j++)
4420 if (stored_player[j].active)
4421 DrawPlayerExt(&stored_player[j], i);
4424 void DrawPlayerField(int x, int y)
4426 if (!IS_PLAYER(x, y))
4429 DrawPlayer(PLAYERINFO(x, y));
4432 // ----------------------------------------------------------------------------
4434 void WaitForEventToContinue(void)
4436 boolean first_wait = TRUE;
4437 boolean still_wait = TRUE;
4439 if (program.headless)
4442 // simulate releasing mouse button over last gadget, if still pressed
4444 HandleGadgets(-1, -1, 0);
4446 button_status = MB_RELEASED;
4449 ClearPlayerAction();
4455 if (NextValidEvent(&event))
4459 case EVENT_BUTTONPRESS:
4460 case EVENT_FINGERPRESS:
4464 case EVENT_BUTTONRELEASE:
4465 case EVENT_FINGERRELEASE:
4466 still_wait = first_wait;
4469 case EVENT_KEYPRESS:
4470 case SDL_CONTROLLERBUTTONDOWN:
4471 case SDL_JOYBUTTONDOWN:
4476 HandleOtherEvents(&event);
4480 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4485 if (!PendingEvent())
4490 #define MAX_REQUEST_LINES 13
4491 #define MAX_REQUEST_LINE_FONT1_LEN 7
4492 #define MAX_REQUEST_LINE_FONT2_LEN 10
4494 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4496 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4498 int draw_buffer_last = GetDrawtoField();
4499 int width = request.width;
4500 int height = request.height;
4504 // when showing request dialog after game ended, deactivate game panel
4505 if (game_just_ended)
4506 game.panel.active = FALSE;
4508 game.request_active = TRUE;
4510 setRequestPosition(&sx, &sy, FALSE);
4512 button_status = MB_RELEASED;
4514 request_gadget_id = -1;
4519 if (game_just_ended)
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_2;
4569 // this sets 'request_gadget_id'
4570 HandleGadgets(mx, my, button_status);
4572 if (global.use_envelope_request)
4574 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4576 // restore pointer to drawing buffer
4577 drawto = drawto_last;
4580 switch (request_gadget_id)
4582 case TOOL_CTRL_ID_YES:
4583 case TOOL_CTRL_ID_TOUCH_YES:
4586 case TOOL_CTRL_ID_NO:
4587 case TOOL_CTRL_ID_TOUCH_NO:
4590 case TOOL_CTRL_ID_CONFIRM:
4591 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4592 result = TRUE | FALSE;
4595 case TOOL_CTRL_ID_PLAYER_1:
4598 case TOOL_CTRL_ID_PLAYER_2:
4601 case TOOL_CTRL_ID_PLAYER_3:
4604 case TOOL_CTRL_ID_PLAYER_4:
4609 // only check clickable animations if no request gadget clicked
4610 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4617 case SDL_WINDOWEVENT:
4618 HandleWindowEvent((WindowEvent *) &event);
4621 case SDL_APP_WILLENTERBACKGROUND:
4622 case SDL_APP_DIDENTERBACKGROUND:
4623 case SDL_APP_WILLENTERFOREGROUND:
4624 case SDL_APP_DIDENTERFOREGROUND:
4625 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4628 case EVENT_KEYPRESS:
4630 Key key = GetEventKey((KeyEvent *)&event);
4635 if (req_state & REQ_CONFIRM)
4644 #if defined(KSYM_Rewind)
4645 case KSYM_Rewind: // for Amazon Fire TV remote
4654 #if defined(KSYM_FastForward)
4655 case KSYM_FastForward: // for Amazon Fire TV remote
4661 HandleKeysDebug(key, KEY_PRESSED);
4665 if (req_state & REQ_PLAYER)
4667 int old_player_nr = setup.network_player_nr;
4670 result = old_player_nr + 1;
4675 result = old_player_nr + 1;
4706 case EVENT_FINGERRELEASE:
4707 case EVENT_KEYRELEASE:
4708 ClearPlayerAction();
4711 case SDL_CONTROLLERBUTTONDOWN:
4712 switch (event.cbutton.button)
4714 case SDL_CONTROLLER_BUTTON_A:
4715 case SDL_CONTROLLER_BUTTON_X:
4716 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4717 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4721 case SDL_CONTROLLER_BUTTON_B:
4722 case SDL_CONTROLLER_BUTTON_Y:
4723 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4724 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4725 case SDL_CONTROLLER_BUTTON_BACK:
4730 if (req_state & REQ_PLAYER)
4732 int old_player_nr = setup.network_player_nr;
4735 result = old_player_nr + 1;
4737 switch (event.cbutton.button)
4739 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4740 case SDL_CONTROLLER_BUTTON_Y:
4744 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4745 case SDL_CONTROLLER_BUTTON_B:
4749 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4750 case SDL_CONTROLLER_BUTTON_A:
4754 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4755 case SDL_CONTROLLER_BUTTON_X:
4766 case SDL_CONTROLLERBUTTONUP:
4767 HandleJoystickEvent(&event);
4768 ClearPlayerAction();
4772 HandleOtherEvents(&event);
4777 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4779 int joy = AnyJoystick();
4781 if (joy & JOY_BUTTON_1)
4783 else if (joy & JOY_BUTTON_2)
4786 else if (AnyJoystick())
4788 int joy = AnyJoystick();
4790 if (req_state & REQ_PLAYER)
4794 else if (joy & JOY_RIGHT)
4796 else if (joy & JOY_DOWN)
4798 else if (joy & JOY_LEFT)
4806 SetDrawtoField(draw_buffer_last);
4808 game.request_active = FALSE;
4813 static boolean RequestDoor(char *text, unsigned int req_state)
4815 int draw_buffer_last = GetDrawtoField();
4816 unsigned int old_door_state;
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 old_door_state = GetDoorState();
4847 // simulate releasing mouse button over last gadget, if still pressed
4849 HandleGadgets(-1, -1, 0);
4853 // draw released gadget before proceeding
4856 if (old_door_state & DOOR_OPEN_1)
4858 CloseDoor(DOOR_CLOSE_1);
4860 // save old door content
4861 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4862 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4865 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4866 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4868 // clear door drawing field
4869 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4871 // force DOOR font inside door area
4872 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4874 // write text for request
4875 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4877 char text_line[max_request_line_len + 1];
4883 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4885 tc = *(text_ptr + tx);
4886 // if (!tc || tc == ' ')
4887 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4891 if ((tc == '?' || tc == '!') && tl == 0)
4901 strncpy(text_line, text_ptr, tl);
4904 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4905 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4906 text_line, font_nr);
4908 text_ptr += tl + (tc == ' ' ? 1 : 0);
4909 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4914 if (req_state & REQ_ASK)
4916 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4917 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4918 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4919 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4921 else if (req_state & REQ_CONFIRM)
4923 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4924 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4926 else if (req_state & REQ_PLAYER)
4928 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4929 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4930 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4931 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4934 // copy request gadgets to door backbuffer
4935 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4937 OpenDoor(DOOR_OPEN_1);
4939 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4941 if (game_status == GAME_MODE_PLAYING)
4943 SetPanelBackground();
4944 SetDrawBackgroundMask(REDRAW_DOOR_1);
4948 SetDrawBackgroundMask(REDRAW_FIELD);
4954 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4956 // ---------- handle request buttons ----------
4957 result = RequestHandleEvents(req_state, draw_buffer_last);
4961 if (!(req_state & REQ_STAY_OPEN))
4963 CloseDoor(DOOR_CLOSE_1);
4965 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4966 (req_state & REQ_REOPEN))
4967 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4972 if (game_status == GAME_MODE_PLAYING)
4974 SetPanelBackground();
4975 SetDrawBackgroundMask(REDRAW_DOOR_1);
4979 SetDrawBackgroundMask(REDRAW_FIELD);
4982 // continue network game after request
4983 if (network.enabled &&
4984 game_status == GAME_MODE_PLAYING &&
4985 !game.all_players_gone &&
4986 req_state & REQUEST_WAIT_FOR_INPUT)
4987 SendToServer_ContinuePlaying();
4989 // restore deactivated drawing when quick-loading level tape recording
4990 if (tape.playing && tape.deactivate_display)
4991 TapeDeactivateDisplayOn();
4996 static boolean RequestEnvelope(char *text, unsigned int req_state)
4998 int draw_buffer_last = GetDrawtoField();
5001 if (game_status == GAME_MODE_PLAYING)
5002 BlitScreenToBitmap(backbuffer);
5004 // disable deactivated drawing when quick-loading level tape recording
5005 if (tape.playing && tape.deactivate_display)
5006 TapeDeactivateDisplayOff(TRUE);
5008 SetMouseCursor(CURSOR_DEFAULT);
5010 // pause network game while waiting for request to answer
5011 if (network.enabled &&
5012 game_status == GAME_MODE_PLAYING &&
5013 !game.all_players_gone &&
5014 req_state & REQUEST_WAIT_FOR_INPUT)
5015 SendToServer_PausePlaying();
5017 // simulate releasing mouse button over last gadget, if still pressed
5019 HandleGadgets(-1, -1, 0);
5023 // (replace with setting corresponding request background)
5024 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5025 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5027 // clear door drawing field
5028 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5030 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5032 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5034 if (game_status == GAME_MODE_PLAYING)
5036 SetPanelBackground();
5037 SetDrawBackgroundMask(REDRAW_DOOR_1);
5041 SetDrawBackgroundMask(REDRAW_FIELD);
5047 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5049 // ---------- handle request buttons ----------
5050 result = RequestHandleEvents(req_state, draw_buffer_last);
5054 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5058 if (game_status == GAME_MODE_PLAYING)
5060 SetPanelBackground();
5061 SetDrawBackgroundMask(REDRAW_DOOR_1);
5065 SetDrawBackgroundMask(REDRAW_FIELD);
5068 // continue network game after request
5069 if (network.enabled &&
5070 game_status == GAME_MODE_PLAYING &&
5071 !game.all_players_gone &&
5072 req_state & REQUEST_WAIT_FOR_INPUT)
5073 SendToServer_ContinuePlaying();
5075 // restore deactivated drawing when quick-loading level tape recording
5076 if (tape.playing && tape.deactivate_display)
5077 TapeDeactivateDisplayOn();
5082 boolean Request(char *text, unsigned int req_state)
5084 boolean overlay_enabled = GetOverlayEnabled();
5087 game.request_active_or_moving = TRUE;
5089 SetOverlayEnabled(FALSE);
5091 if (global.use_envelope_request)
5092 result = RequestEnvelope(text, req_state);
5094 result = RequestDoor(text, req_state);
5096 SetOverlayEnabled(overlay_enabled);
5098 game.request_active_or_moving = FALSE;
5103 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5105 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5106 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5109 if (dpo1->sort_priority != dpo2->sort_priority)
5110 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5112 compare_result = dpo1->nr - dpo2->nr;
5114 return compare_result;
5117 void InitGraphicCompatibilityInfo_Doors(void)
5123 struct DoorInfo *door;
5127 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5128 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5130 { -1, -1, -1, NULL }
5132 struct Rect door_rect_list[] =
5134 { DX, DY, DXSIZE, DYSIZE },
5135 { VX, VY, VXSIZE, VYSIZE }
5139 for (i = 0; doors[i].door_token != -1; i++)
5141 int door_token = doors[i].door_token;
5142 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5143 int part_1 = doors[i].part_1;
5144 int part_8 = doors[i].part_8;
5145 int part_2 = part_1 + 1;
5146 int part_3 = part_1 + 2;
5147 struct DoorInfo *door = doors[i].door;
5148 struct Rect *door_rect = &door_rect_list[door_index];
5149 boolean door_gfx_redefined = FALSE;
5151 // check if any door part graphic definitions have been redefined
5153 for (j = 0; door_part_controls[j].door_token != -1; j++)
5155 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5156 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5158 if (dpc->door_token == door_token && fi->redefined)
5159 door_gfx_redefined = TRUE;
5162 // check for old-style door graphic/animation modifications
5164 if (!door_gfx_redefined)
5166 if (door->anim_mode & ANIM_STATIC_PANEL)
5168 door->panel.step_xoffset = 0;
5169 door->panel.step_yoffset = 0;
5172 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5174 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5175 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5176 int num_door_steps, num_panel_steps;
5178 // remove door part graphics other than the two default wings
5180 for (j = 0; door_part_controls[j].door_token != -1; j++)
5182 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5183 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5185 if (dpc->graphic >= part_3 &&
5186 dpc->graphic <= part_8)
5190 // set graphics and screen positions of the default wings
5192 g_part_1->width = door_rect->width;
5193 g_part_1->height = door_rect->height;
5194 g_part_2->width = door_rect->width;
5195 g_part_2->height = door_rect->height;
5196 g_part_2->src_x = door_rect->width;
5197 g_part_2->src_y = g_part_1->src_y;
5199 door->part_2.x = door->part_1.x;
5200 door->part_2.y = door->part_1.y;
5202 if (door->width != -1)
5204 g_part_1->width = door->width;
5205 g_part_2->width = door->width;
5207 // special treatment for graphics and screen position of right wing
5208 g_part_2->src_x += door_rect->width - door->width;
5209 door->part_2.x += door_rect->width - door->width;
5212 if (door->height != -1)
5214 g_part_1->height = door->height;
5215 g_part_2->height = door->height;
5217 // special treatment for graphics and screen position of bottom wing
5218 g_part_2->src_y += door_rect->height - door->height;
5219 door->part_2.y += door_rect->height - door->height;
5222 // set animation delays for the default wings and panels
5224 door->part_1.step_delay = door->step_delay;
5225 door->part_2.step_delay = door->step_delay;
5226 door->panel.step_delay = door->step_delay;
5228 // set animation draw order for the default wings
5230 door->part_1.sort_priority = 2; // draw left wing over ...
5231 door->part_2.sort_priority = 1; // ... right wing
5233 // set animation draw offset for the default wings
5235 if (door->anim_mode & ANIM_HORIZONTAL)
5237 door->part_1.step_xoffset = door->step_offset;
5238 door->part_1.step_yoffset = 0;
5239 door->part_2.step_xoffset = door->step_offset * -1;
5240 door->part_2.step_yoffset = 0;
5242 num_door_steps = g_part_1->width / door->step_offset;
5244 else // ANIM_VERTICAL
5246 door->part_1.step_xoffset = 0;
5247 door->part_1.step_yoffset = door->step_offset;
5248 door->part_2.step_xoffset = 0;
5249 door->part_2.step_yoffset = door->step_offset * -1;
5251 num_door_steps = g_part_1->height / door->step_offset;
5254 // set animation draw offset for the default panels
5256 if (door->step_offset > 1)
5258 num_panel_steps = 2 * door_rect->height / door->step_offset;
5259 door->panel.start_step = num_panel_steps - num_door_steps;
5260 door->panel.start_step_closing = door->panel.start_step;
5264 num_panel_steps = door_rect->height / door->step_offset;
5265 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5266 door->panel.start_step_closing = door->panel.start_step;
5267 door->panel.step_delay *= 2;
5274 void InitDoors(void)
5278 for (i = 0; door_part_controls[i].door_token != -1; i++)
5280 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5281 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5283 // initialize "start_step_opening" and "start_step_closing", if needed
5284 if (dpc->pos->start_step_opening == 0 &&
5285 dpc->pos->start_step_closing == 0)
5287 // dpc->pos->start_step_opening = dpc->pos->start_step;
5288 dpc->pos->start_step_closing = dpc->pos->start_step;
5291 // fill structure for door part draw order (sorted below)
5293 dpo->sort_priority = dpc->pos->sort_priority;
5296 // sort door part controls according to sort_priority and graphic number
5297 qsort(door_part_order, MAX_DOOR_PARTS,
5298 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5301 unsigned int OpenDoor(unsigned int door_state)
5303 if (door_state & DOOR_COPY_BACK)
5305 if (door_state & DOOR_OPEN_1)
5306 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5307 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5309 if (door_state & DOOR_OPEN_2)
5310 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5311 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5313 door_state &= ~DOOR_COPY_BACK;
5316 return MoveDoor(door_state);
5319 unsigned int CloseDoor(unsigned int door_state)
5321 unsigned int old_door_state = GetDoorState();
5323 if (!(door_state & DOOR_NO_COPY_BACK))
5325 if (old_door_state & DOOR_OPEN_1)
5326 BlitBitmap(backbuffer, bitmap_db_door_1,
5327 DX, DY, DXSIZE, DYSIZE, 0, 0);
5329 if (old_door_state & DOOR_OPEN_2)
5330 BlitBitmap(backbuffer, bitmap_db_door_2,
5331 VX, VY, VXSIZE, VYSIZE, 0, 0);
5333 door_state &= ~DOOR_NO_COPY_BACK;
5336 return MoveDoor(door_state);
5339 unsigned int GetDoorState(void)
5341 return MoveDoor(DOOR_GET_STATE);
5344 unsigned int SetDoorState(unsigned int door_state)
5346 return MoveDoor(door_state | DOOR_SET_STATE);
5349 static int euclid(int a, int b)
5351 return (b ? euclid(b, a % b) : a);
5354 unsigned int MoveDoor(unsigned int door_state)
5356 struct Rect door_rect_list[] =
5358 { DX, DY, DXSIZE, DYSIZE },
5359 { VX, VY, VXSIZE, VYSIZE }
5361 static int door1 = DOOR_CLOSE_1;
5362 static int door2 = DOOR_CLOSE_2;
5363 DelayCounter door_delay = { 0 };
5366 if (door_state == DOOR_GET_STATE)
5367 return (door1 | door2);
5369 if (door_state & DOOR_SET_STATE)
5371 if (door_state & DOOR_ACTION_1)
5372 door1 = door_state & DOOR_ACTION_1;
5373 if (door_state & DOOR_ACTION_2)
5374 door2 = door_state & DOOR_ACTION_2;
5376 return (door1 | door2);
5379 if (!(door_state & DOOR_FORCE_REDRAW))
5381 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5382 door_state &= ~DOOR_OPEN_1;
5383 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5384 door_state &= ~DOOR_CLOSE_1;
5385 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5386 door_state &= ~DOOR_OPEN_2;
5387 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5388 door_state &= ~DOOR_CLOSE_2;
5391 if (global.autoplay_leveldir)
5393 door_state |= DOOR_NO_DELAY;
5394 door_state &= ~DOOR_CLOSE_ALL;
5397 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5398 door_state |= DOOR_NO_DELAY;
5400 if (door_state & DOOR_ACTION)
5402 boolean door_panel_drawn[NUM_DOORS];
5403 boolean panel_has_doors[NUM_DOORS];
5404 boolean door_part_skip[MAX_DOOR_PARTS];
5405 boolean door_part_done[MAX_DOOR_PARTS];
5406 boolean door_part_done_all;
5407 int num_steps[MAX_DOOR_PARTS];
5408 int max_move_delay = 0; // delay for complete animations of all doors
5409 int max_step_delay = 0; // delay (ms) between two animation frames
5410 int num_move_steps = 0; // number of animation steps for all doors
5411 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5412 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5416 for (i = 0; i < NUM_DOORS; i++)
5417 panel_has_doors[i] = FALSE;
5419 for (i = 0; i < MAX_DOOR_PARTS; i++)
5421 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5422 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5423 int door_token = dpc->door_token;
5425 door_part_done[i] = FALSE;
5426 door_part_skip[i] = (!(door_state & door_token) ||
5430 for (i = 0; i < MAX_DOOR_PARTS; i++)
5432 int nr = door_part_order[i].nr;
5433 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5434 struct DoorPartPosInfo *pos = dpc->pos;
5435 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5436 int door_token = dpc->door_token;
5437 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5438 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5439 int step_xoffset = ABS(pos->step_xoffset);
5440 int step_yoffset = ABS(pos->step_yoffset);
5441 int step_delay = pos->step_delay;
5442 int current_door_state = door_state & door_token;
5443 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5444 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5445 boolean part_opening = (is_panel ? door_closing : door_opening);
5446 int start_step = (part_opening ? pos->start_step_opening :
5447 pos->start_step_closing);
5448 float move_xsize = (step_xoffset ? g->width : 0);
5449 float move_ysize = (step_yoffset ? g->height : 0);
5450 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5451 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5452 int move_steps = (move_xsteps && move_ysteps ?
5453 MIN(move_xsteps, move_ysteps) :
5454 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5455 int move_delay = move_steps * step_delay;
5457 if (door_part_skip[nr])
5460 max_move_delay = MAX(max_move_delay, move_delay);
5461 max_step_delay = (max_step_delay == 0 ? step_delay :
5462 euclid(max_step_delay, step_delay));
5463 num_steps[nr] = move_steps;
5467 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5469 panel_has_doors[door_index] = TRUE;
5473 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5475 num_move_steps = max_move_delay / max_step_delay;
5476 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5478 door_delay.value = max_step_delay;
5480 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5482 start = num_move_steps - 1;
5486 // opening door sound has priority over simultaneously closing door
5487 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5489 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5491 if (door_state & DOOR_OPEN_1)
5492 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5493 if (door_state & DOOR_OPEN_2)
5494 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5496 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5498 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5500 if (door_state & DOOR_CLOSE_1)
5501 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5502 if (door_state & DOOR_CLOSE_2)
5503 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5507 for (k = start; k < num_move_steps; k++)
5509 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5511 door_part_done_all = TRUE;
5513 for (i = 0; i < NUM_DOORS; i++)
5514 door_panel_drawn[i] = FALSE;
5516 for (i = 0; i < MAX_DOOR_PARTS; i++)
5518 int nr = door_part_order[i].nr;
5519 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5520 struct DoorPartPosInfo *pos = dpc->pos;
5521 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5522 int door_token = dpc->door_token;
5523 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5524 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5525 boolean is_panel_and_door_has_closed = FALSE;
5526 struct Rect *door_rect = &door_rect_list[door_index];
5527 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5529 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5530 int current_door_state = door_state & door_token;
5531 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5532 boolean door_closing = !door_opening;
5533 boolean part_opening = (is_panel ? door_closing : door_opening);
5534 boolean part_closing = !part_opening;
5535 int start_step = (part_opening ? pos->start_step_opening :
5536 pos->start_step_closing);
5537 int step_delay = pos->step_delay;
5538 int step_factor = step_delay / max_step_delay;
5539 int k1 = (step_factor ? k / step_factor + 1 : k);
5540 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5541 int kk = MAX(0, k2);
5544 int src_x, src_y, src_xx, src_yy;
5545 int dst_x, dst_y, dst_xx, dst_yy;
5548 if (door_part_skip[nr])
5551 if (!(door_state & door_token))
5559 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5560 int kk_door = MAX(0, k2_door);
5561 int sync_frame = kk_door * door_delay.value;
5562 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5564 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5565 &g_src_x, &g_src_y);
5570 if (!door_panel_drawn[door_index])
5572 ClearRectangle(drawto, door_rect->x, door_rect->y,
5573 door_rect->width, door_rect->height);
5575 door_panel_drawn[door_index] = TRUE;
5578 // draw opening or closing door parts
5580 if (pos->step_xoffset < 0) // door part on right side
5583 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5586 if (dst_xx + width > door_rect->width)
5587 width = door_rect->width - dst_xx;
5589 else // door part on left side
5592 dst_xx = pos->x - kk * pos->step_xoffset;
5596 src_xx = ABS(dst_xx);
5600 width = g->width - src_xx;
5602 if (width > door_rect->width)
5603 width = door_rect->width;
5605 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5608 if (pos->step_yoffset < 0) // door part on bottom side
5611 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5614 if (dst_yy + height > door_rect->height)
5615 height = door_rect->height - dst_yy;
5617 else // door part on top side
5620 dst_yy = pos->y - kk * pos->step_yoffset;
5624 src_yy = ABS(dst_yy);
5628 height = g->height - src_yy;
5631 src_x = g_src_x + src_xx;
5632 src_y = g_src_y + src_yy;
5634 dst_x = door_rect->x + dst_xx;
5635 dst_y = door_rect->y + dst_yy;
5637 is_panel_and_door_has_closed =
5640 panel_has_doors[door_index] &&
5641 k >= num_move_steps_doors_only - 1);
5643 if (width >= 0 && width <= g->width &&
5644 height >= 0 && height <= g->height &&
5645 !is_panel_and_door_has_closed)
5647 if (is_panel || !pos->draw_masked)
5648 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5651 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5655 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5657 if ((part_opening && (width < 0 || height < 0)) ||
5658 (part_closing && (width >= g->width && height >= g->height)))
5659 door_part_done[nr] = TRUE;
5661 // continue door part animations, but not panel after door has closed
5662 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5663 door_part_done_all = FALSE;
5666 if (!(door_state & DOOR_NO_DELAY))
5670 SkipUntilDelayReached(&door_delay, &k, last_frame);
5672 // prevent OS (Windows) from complaining about program not responding
5676 if (door_part_done_all)
5680 if (!(door_state & DOOR_NO_DELAY))
5682 // wait for specified door action post delay
5683 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5684 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5685 else if (door_state & DOOR_ACTION_1)
5686 door_delay.value = door_1.post_delay;
5687 else if (door_state & DOOR_ACTION_2)
5688 door_delay.value = door_2.post_delay;
5690 while (!DelayReached(&door_delay))
5695 if (door_state & DOOR_ACTION_1)
5696 door1 = door_state & DOOR_ACTION_1;
5697 if (door_state & DOOR_ACTION_2)
5698 door2 = door_state & DOOR_ACTION_2;
5700 // draw masked border over door area
5701 DrawMaskedBorder(REDRAW_DOOR_1);
5702 DrawMaskedBorder(REDRAW_DOOR_2);
5704 ClearAutoRepeatKeyEvents();
5706 return (door1 | door2);
5709 static boolean useSpecialEditorDoor(void)
5711 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5712 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5714 // do not draw special editor door if editor border defined or redefined
5715 if (graphic_info[graphic].bitmap != NULL || redefined)
5718 // do not draw special editor door if global border defined to be empty
5719 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5722 // do not draw special editor door if viewport definitions do not match
5726 EY + EYSIZE != VY + VYSIZE)
5732 void DrawSpecialEditorDoor(void)
5734 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5735 int top_border_width = gfx1->width;
5736 int top_border_height = gfx1->height;
5737 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5738 int ex = EX - outer_border;
5739 int ey = EY - outer_border;
5740 int vy = VY - outer_border;
5741 int exsize = EXSIZE + 2 * outer_border;
5743 if (!useSpecialEditorDoor())
5746 // draw bigger level editor toolbox window
5747 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5748 top_border_width, top_border_height, ex, ey - top_border_height);
5749 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5750 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5752 redraw_mask |= REDRAW_ALL;
5755 void UndrawSpecialEditorDoor(void)
5757 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5758 int top_border_width = gfx1->width;
5759 int top_border_height = gfx1->height;
5760 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5761 int ex = EX - outer_border;
5762 int ey = EY - outer_border;
5763 int ey_top = ey - top_border_height;
5764 int exsize = EXSIZE + 2 * outer_border;
5765 int eysize = EYSIZE + 2 * outer_border;
5767 if (!useSpecialEditorDoor())
5770 // draw normal tape recorder window
5771 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5773 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5774 ex, ey_top, top_border_width, top_border_height,
5776 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5777 ex, ey, exsize, eysize, ex, ey);
5781 // if screen background is set to "[NONE]", clear editor toolbox window
5782 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5783 ClearRectangle(drawto, ex, ey, exsize, eysize);
5786 redraw_mask |= REDRAW_ALL;
5790 // ---------- new tool button stuff -------------------------------------------
5795 struct TextPosInfo *pos;
5797 boolean is_touch_button;
5799 } toolbutton_info[NUM_TOOL_BUTTONS] =
5802 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5803 TOOL_CTRL_ID_YES, FALSE, "yes"
5806 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5807 TOOL_CTRL_ID_NO, FALSE, "no"
5810 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5811 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5814 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5815 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5818 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5819 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5822 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5823 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5826 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5827 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5830 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5831 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5834 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5835 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5838 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5839 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5843 void CreateToolButtons(void)
5847 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5849 int graphic = toolbutton_info[i].graphic;
5850 struct GraphicInfo *gfx = &graphic_info[graphic];
5851 struct TextPosInfo *pos = toolbutton_info[i].pos;
5852 struct GadgetInfo *gi;
5853 Bitmap *deco_bitmap = None;
5854 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5855 unsigned int event_mask = GD_EVENT_RELEASED;
5856 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5857 int base_x = (is_touch_button ? 0 : DX);
5858 int base_y = (is_touch_button ? 0 : DY);
5859 int gd_x = gfx->src_x;
5860 int gd_y = gfx->src_y;
5861 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5862 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5867 // do not use touch buttons if overlay touch buttons are disabled
5868 if (is_touch_button && !setup.touch.overlay_buttons)
5871 if (global.use_envelope_request && !is_touch_button)
5873 setRequestPosition(&base_x, &base_y, TRUE);
5875 // check if request buttons are outside of envelope and fix, if needed
5876 if (x < 0 || x + gfx->width > request.width ||
5877 y < 0 || y + gfx->height > request.height)
5879 if (id == TOOL_CTRL_ID_YES)
5882 y = request.height - 2 * request.border_size - gfx->height;
5884 else if (id == TOOL_CTRL_ID_NO)
5886 x = request.width - 2 * request.border_size - gfx->width;
5887 y = request.height - 2 * request.border_size - gfx->height;
5889 else if (id == TOOL_CTRL_ID_CONFIRM)
5891 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5892 y = request.height - 2 * request.border_size - gfx->height;
5894 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5896 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5898 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5899 y = request.height - 2 * request.border_size - gfx->height * 2;
5901 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5902 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5907 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5910 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5912 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5913 pos->size, &deco_bitmap, &deco_x, &deco_y);
5914 deco_xpos = (gfx->width - pos->size) / 2;
5915 deco_ypos = (gfx->height - pos->size) / 2;
5918 gi = CreateGadget(GDI_CUSTOM_ID, id,
5919 GDI_IMAGE_ID, graphic,
5920 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5923 GDI_WIDTH, gfx->width,
5924 GDI_HEIGHT, gfx->height,
5925 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5926 GDI_STATE, GD_BUTTON_UNPRESSED,
5927 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5928 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5929 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5930 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5931 GDI_DECORATION_SIZE, pos->size, pos->size,
5932 GDI_DECORATION_SHIFTING, 1, 1,
5933 GDI_DIRECT_DRAW, FALSE,
5934 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5935 GDI_EVENT_MASK, event_mask,
5936 GDI_CALLBACK_ACTION, HandleToolButtons,
5940 Fail("cannot create gadget");
5942 tool_gadget[id] = gi;
5946 void FreeToolButtons(void)
5950 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5951 FreeGadget(tool_gadget[i]);
5954 static void UnmapToolButtons(void)
5958 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5959 UnmapGadget(tool_gadget[i]);
5962 static void HandleToolButtons(struct GadgetInfo *gi)
5964 request_gadget_id = gi->custom_id;
5967 static struct Mapping_EM_to_RND_object
5970 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5971 boolean is_backside; // backside of moving element
5977 em_object_mapping_list[GAME_TILE_MAX + 1] =
5980 Zborder, FALSE, FALSE,
5984 Zplayer, FALSE, FALSE,
5993 Ztank, FALSE, FALSE,
5997 Zeater, FALSE, FALSE,
6001 Zdynamite, FALSE, FALSE,
6005 Zboom, FALSE, FALSE,
6010 Xchain, FALSE, FALSE,
6011 EL_DEFAULT, ACTION_EXPLODING, -1
6014 Xboom_bug, FALSE, FALSE,
6015 EL_BUG, ACTION_EXPLODING, -1
6018 Xboom_tank, FALSE, FALSE,
6019 EL_SPACESHIP, ACTION_EXPLODING, -1
6022 Xboom_android, FALSE, FALSE,
6023 EL_EMC_ANDROID, ACTION_OTHER, -1
6026 Xboom_1, FALSE, FALSE,
6027 EL_DEFAULT, ACTION_EXPLODING, -1
6030 Xboom_2, FALSE, FALSE,
6031 EL_DEFAULT, ACTION_EXPLODING, -1
6035 Xblank, TRUE, FALSE,
6040 Xsplash_e, FALSE, FALSE,
6041 EL_ACID_SPLASH_RIGHT, -1, -1
6044 Xsplash_w, FALSE, FALSE,
6045 EL_ACID_SPLASH_LEFT, -1, -1
6049 Xplant, TRUE, FALSE,
6050 EL_EMC_PLANT, -1, -1
6053 Yplant, FALSE, FALSE,
6054 EL_EMC_PLANT, -1, -1
6058 Xacid_1, TRUE, FALSE,
6062 Xacid_2, FALSE, FALSE,
6066 Xacid_3, FALSE, FALSE,
6070 Xacid_4, FALSE, FALSE,
6074 Xacid_5, FALSE, FALSE,
6078 Xacid_6, FALSE, FALSE,
6082 Xacid_7, FALSE, FALSE,
6086 Xacid_8, FALSE, FALSE,
6091 Xfake_acid_1, TRUE, FALSE,
6092 EL_EMC_FAKE_ACID, -1, -1
6095 Xfake_acid_2, FALSE, FALSE,
6096 EL_EMC_FAKE_ACID, -1, -1
6099 Xfake_acid_3, FALSE, FALSE,
6100 EL_EMC_FAKE_ACID, -1, -1
6103 Xfake_acid_4, FALSE, FALSE,
6104 EL_EMC_FAKE_ACID, -1, -1
6107 Xfake_acid_5, FALSE, FALSE,
6108 EL_EMC_FAKE_ACID, -1, -1
6111 Xfake_acid_6, FALSE, FALSE,
6112 EL_EMC_FAKE_ACID, -1, -1
6115 Xfake_acid_7, FALSE, FALSE,
6116 EL_EMC_FAKE_ACID, -1, -1
6119 Xfake_acid_8, FALSE, FALSE,
6120 EL_EMC_FAKE_ACID, -1, -1
6124 Xfake_acid_1_player, FALSE, FALSE,
6125 EL_EMC_FAKE_ACID, -1, -1
6128 Xfake_acid_2_player, FALSE, FALSE,
6129 EL_EMC_FAKE_ACID, -1, -1
6132 Xfake_acid_3_player, FALSE, FALSE,
6133 EL_EMC_FAKE_ACID, -1, -1
6136 Xfake_acid_4_player, FALSE, FALSE,
6137 EL_EMC_FAKE_ACID, -1, -1
6140 Xfake_acid_5_player, FALSE, FALSE,
6141 EL_EMC_FAKE_ACID, -1, -1
6144 Xfake_acid_6_player, FALSE, FALSE,
6145 EL_EMC_FAKE_ACID, -1, -1
6148 Xfake_acid_7_player, FALSE, FALSE,
6149 EL_EMC_FAKE_ACID, -1, -1
6152 Xfake_acid_8_player, FALSE, FALSE,
6153 EL_EMC_FAKE_ACID, -1, -1
6157 Xgrass, TRUE, FALSE,
6158 EL_EMC_GRASS, -1, -1
6161 Ygrass_nB, FALSE, FALSE,
6162 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6165 Ygrass_eB, FALSE, FALSE,
6166 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6169 Ygrass_sB, FALSE, FALSE,
6170 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6173 Ygrass_wB, FALSE, FALSE,
6174 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6182 Ydirt_nB, FALSE, FALSE,
6183 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6186 Ydirt_eB, FALSE, FALSE,
6187 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6190 Ydirt_sB, FALSE, FALSE,
6191 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6194 Ydirt_wB, FALSE, FALSE,
6195 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6199 Xandroid, TRUE, FALSE,
6200 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6203 Xandroid_1_n, FALSE, FALSE,
6204 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6207 Xandroid_2_n, FALSE, FALSE,
6208 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6211 Xandroid_1_e, FALSE, FALSE,
6212 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6215 Xandroid_2_e, FALSE, FALSE,
6216 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6219 Xandroid_1_w, FALSE, FALSE,
6220 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6223 Xandroid_2_w, FALSE, FALSE,
6224 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6227 Xandroid_1_s, FALSE, FALSE,
6228 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6231 Xandroid_2_s, FALSE, FALSE,
6232 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6235 Yandroid_n, FALSE, FALSE,
6236 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6239 Yandroid_nB, FALSE, TRUE,
6240 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6243 Yandroid_ne, FALSE, FALSE,
6244 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6247 Yandroid_neB, FALSE, TRUE,
6248 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6251 Yandroid_e, FALSE, FALSE,
6252 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6255 Yandroid_eB, FALSE, TRUE,
6256 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6259 Yandroid_se, FALSE, FALSE,
6260 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6263 Yandroid_seB, FALSE, TRUE,
6264 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6267 Yandroid_s, FALSE, FALSE,
6268 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6271 Yandroid_sB, FALSE, TRUE,
6272 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6275 Yandroid_sw, FALSE, FALSE,
6276 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6279 Yandroid_swB, FALSE, TRUE,
6280 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6283 Yandroid_w, FALSE, FALSE,
6284 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6287 Yandroid_wB, FALSE, TRUE,
6288 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6291 Yandroid_nw, FALSE, FALSE,
6292 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6295 Yandroid_nwB, FALSE, TRUE,
6296 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6300 Xeater_n, TRUE, FALSE,
6301 EL_YAMYAM_UP, -1, -1
6304 Xeater_e, TRUE, FALSE,
6305 EL_YAMYAM_RIGHT, -1, -1
6308 Xeater_w, TRUE, FALSE,
6309 EL_YAMYAM_LEFT, -1, -1
6312 Xeater_s, TRUE, FALSE,
6313 EL_YAMYAM_DOWN, -1, -1
6316 Yeater_n, FALSE, FALSE,
6317 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6320 Yeater_nB, FALSE, TRUE,
6321 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6324 Yeater_e, FALSE, FALSE,
6325 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6328 Yeater_eB, FALSE, TRUE,
6329 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6332 Yeater_s, FALSE, FALSE,
6333 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6336 Yeater_sB, FALSE, TRUE,
6337 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6340 Yeater_w, FALSE, FALSE,
6341 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6344 Yeater_wB, FALSE, TRUE,
6345 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6348 Yeater_stone, FALSE, FALSE,
6349 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6352 Yeater_spring, FALSE, FALSE,
6353 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6357 Xalien, TRUE, FALSE,
6361 Xalien_pause, FALSE, FALSE,
6365 Yalien_n, FALSE, FALSE,
6366 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6369 Yalien_nB, FALSE, TRUE,
6370 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6373 Yalien_e, FALSE, FALSE,
6374 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6377 Yalien_eB, FALSE, TRUE,
6378 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6381 Yalien_s, FALSE, FALSE,
6382 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6385 Yalien_sB, FALSE, TRUE,
6386 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6389 Yalien_w, FALSE, FALSE,
6390 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6393 Yalien_wB, FALSE, TRUE,
6394 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6397 Yalien_stone, FALSE, FALSE,
6398 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6401 Yalien_spring, FALSE, FALSE,
6402 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6406 Xbug_1_n, TRUE, FALSE,
6410 Xbug_1_e, TRUE, FALSE,
6411 EL_BUG_RIGHT, -1, -1
6414 Xbug_1_s, TRUE, FALSE,
6418 Xbug_1_w, TRUE, FALSE,
6422 Xbug_2_n, FALSE, FALSE,
6426 Xbug_2_e, FALSE, FALSE,
6427 EL_BUG_RIGHT, -1, -1
6430 Xbug_2_s, FALSE, FALSE,
6434 Xbug_2_w, FALSE, FALSE,
6438 Ybug_n, FALSE, FALSE,
6439 EL_BUG, ACTION_MOVING, MV_BIT_UP
6442 Ybug_nB, FALSE, TRUE,
6443 EL_BUG, ACTION_MOVING, MV_BIT_UP
6446 Ybug_e, FALSE, FALSE,
6447 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6450 Ybug_eB, FALSE, TRUE,
6451 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6454 Ybug_s, FALSE, FALSE,
6455 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6458 Ybug_sB, FALSE, TRUE,
6459 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6462 Ybug_w, FALSE, FALSE,
6463 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6466 Ybug_wB, FALSE, TRUE,
6467 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6470 Ybug_w_n, FALSE, FALSE,
6471 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6474 Ybug_n_e, FALSE, FALSE,
6475 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6478 Ybug_e_s, FALSE, FALSE,
6479 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6482 Ybug_s_w, FALSE, FALSE,
6483 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6486 Ybug_e_n, FALSE, FALSE,
6487 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6490 Ybug_s_e, FALSE, FALSE,
6491 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6494 Ybug_w_s, FALSE, FALSE,
6495 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6498 Ybug_n_w, FALSE, FALSE,
6499 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6502 Ybug_stone, FALSE, FALSE,
6503 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6506 Ybug_spring, FALSE, FALSE,
6507 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6511 Xtank_1_n, TRUE, FALSE,
6512 EL_SPACESHIP_UP, -1, -1
6515 Xtank_1_e, TRUE, FALSE,
6516 EL_SPACESHIP_RIGHT, -1, -1
6519 Xtank_1_s, TRUE, FALSE,
6520 EL_SPACESHIP_DOWN, -1, -1
6523 Xtank_1_w, TRUE, FALSE,
6524 EL_SPACESHIP_LEFT, -1, -1
6527 Xtank_2_n, FALSE, FALSE,
6528 EL_SPACESHIP_UP, -1, -1
6531 Xtank_2_e, FALSE, FALSE,
6532 EL_SPACESHIP_RIGHT, -1, -1
6535 Xtank_2_s, FALSE, FALSE,
6536 EL_SPACESHIP_DOWN, -1, -1
6539 Xtank_2_w, FALSE, FALSE,
6540 EL_SPACESHIP_LEFT, -1, -1
6543 Ytank_n, FALSE, FALSE,
6544 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6547 Ytank_nB, FALSE, TRUE,
6548 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6551 Ytank_e, FALSE, FALSE,
6552 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6555 Ytank_eB, FALSE, TRUE,
6556 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6559 Ytank_s, FALSE, FALSE,
6560 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6563 Ytank_sB, FALSE, TRUE,
6564 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6567 Ytank_w, FALSE, FALSE,
6568 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6571 Ytank_wB, FALSE, TRUE,
6572 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6575 Ytank_w_n, FALSE, FALSE,
6576 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6579 Ytank_n_e, FALSE, FALSE,
6580 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6583 Ytank_e_s, FALSE, FALSE,
6584 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6587 Ytank_s_w, FALSE, FALSE,
6588 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6591 Ytank_e_n, FALSE, FALSE,
6592 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6595 Ytank_s_e, FALSE, FALSE,
6596 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6599 Ytank_w_s, FALSE, FALSE,
6600 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6603 Ytank_n_w, FALSE, FALSE,
6604 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6607 Ytank_stone, FALSE, FALSE,
6608 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6611 Ytank_spring, FALSE, FALSE,
6612 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6616 Xemerald, TRUE, FALSE,
6620 Xemerald_pause, FALSE, FALSE,
6624 Xemerald_fall, FALSE, FALSE,
6628 Xemerald_shine, FALSE, FALSE,
6629 EL_EMERALD, ACTION_TWINKLING, -1
6632 Yemerald_s, FALSE, FALSE,
6633 EL_EMERALD, ACTION_FALLING, -1
6636 Yemerald_sB, FALSE, TRUE,
6637 EL_EMERALD, ACTION_FALLING, -1
6640 Yemerald_e, FALSE, FALSE,
6641 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6644 Yemerald_eB, FALSE, TRUE,
6645 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6648 Yemerald_w, FALSE, FALSE,
6649 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6652 Yemerald_wB, FALSE, TRUE,
6653 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6656 Yemerald_blank, FALSE, FALSE,
6657 EL_EMERALD, ACTION_COLLECTING, -1
6661 Xdiamond, TRUE, FALSE,
6665 Xdiamond_pause, FALSE, FALSE,
6669 Xdiamond_fall, FALSE, FALSE,
6673 Xdiamond_shine, FALSE, FALSE,
6674 EL_DIAMOND, ACTION_TWINKLING, -1
6677 Ydiamond_s, FALSE, FALSE,
6678 EL_DIAMOND, ACTION_FALLING, -1
6681 Ydiamond_sB, FALSE, TRUE,
6682 EL_DIAMOND, ACTION_FALLING, -1
6685 Ydiamond_e, FALSE, FALSE,
6686 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6689 Ydiamond_eB, FALSE, TRUE,
6690 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6693 Ydiamond_w, FALSE, FALSE,
6694 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6697 Ydiamond_wB, FALSE, TRUE,
6698 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6701 Ydiamond_blank, FALSE, FALSE,
6702 EL_DIAMOND, ACTION_COLLECTING, -1
6705 Ydiamond_stone, FALSE, FALSE,
6706 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6710 Xstone, TRUE, FALSE,
6714 Xstone_pause, FALSE, FALSE,
6718 Xstone_fall, FALSE, FALSE,
6722 Ystone_s, FALSE, FALSE,
6723 EL_ROCK, ACTION_FALLING, -1
6726 Ystone_sB, FALSE, TRUE,
6727 EL_ROCK, ACTION_FALLING, -1
6730 Ystone_e, FALSE, FALSE,
6731 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6734 Ystone_eB, FALSE, TRUE,
6735 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6738 Ystone_w, FALSE, FALSE,
6739 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6742 Ystone_wB, FALSE, TRUE,
6743 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6751 Xbomb_pause, FALSE, FALSE,
6755 Xbomb_fall, FALSE, FALSE,
6759 Ybomb_s, FALSE, FALSE,
6760 EL_BOMB, ACTION_FALLING, -1
6763 Ybomb_sB, FALSE, TRUE,
6764 EL_BOMB, ACTION_FALLING, -1
6767 Ybomb_e, FALSE, FALSE,
6768 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6771 Ybomb_eB, FALSE, TRUE,
6772 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6775 Ybomb_w, FALSE, FALSE,
6776 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6779 Ybomb_wB, FALSE, TRUE,
6780 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6783 Ybomb_blank, FALSE, FALSE,
6784 EL_BOMB, ACTION_ACTIVATING, -1
6792 Xnut_pause, FALSE, FALSE,
6796 Xnut_fall, FALSE, FALSE,
6800 Ynut_s, FALSE, FALSE,
6801 EL_NUT, ACTION_FALLING, -1
6804 Ynut_sB, FALSE, TRUE,
6805 EL_NUT, ACTION_FALLING, -1
6808 Ynut_e, FALSE, FALSE,
6809 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6812 Ynut_eB, FALSE, TRUE,
6813 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6816 Ynut_w, FALSE, FALSE,
6817 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6820 Ynut_wB, FALSE, TRUE,
6821 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6824 Ynut_stone, FALSE, FALSE,
6825 EL_NUT, ACTION_BREAKING, -1
6829 Xspring, TRUE, FALSE,
6833 Xspring_pause, FALSE, FALSE,
6837 Xspring_e, TRUE, FALSE,
6838 EL_SPRING_RIGHT, -1, -1
6841 Xspring_w, TRUE, FALSE,
6842 EL_SPRING_LEFT, -1, -1
6845 Xspring_fall, FALSE, FALSE,
6849 Yspring_s, FALSE, FALSE,
6850 EL_SPRING, ACTION_FALLING, -1
6853 Yspring_sB, FALSE, TRUE,
6854 EL_SPRING, ACTION_FALLING, -1
6857 Yspring_e, FALSE, FALSE,
6858 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6861 Yspring_eB, FALSE, TRUE,
6862 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6865 Yspring_w, FALSE, FALSE,
6866 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6869 Yspring_wB, FALSE, TRUE,
6870 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6873 Yspring_alien_e, FALSE, FALSE,
6874 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6877 Yspring_alien_eB, FALSE, TRUE,
6878 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6881 Yspring_alien_w, FALSE, FALSE,
6882 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6885 Yspring_alien_wB, FALSE, TRUE,
6886 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6890 Xpush_emerald_e, FALSE, FALSE,
6891 EL_EMERALD, -1, MV_BIT_RIGHT
6894 Xpush_emerald_w, FALSE, FALSE,
6895 EL_EMERALD, -1, MV_BIT_LEFT
6898 Xpush_diamond_e, FALSE, FALSE,
6899 EL_DIAMOND, -1, MV_BIT_RIGHT
6902 Xpush_diamond_w, FALSE, FALSE,
6903 EL_DIAMOND, -1, MV_BIT_LEFT
6906 Xpush_stone_e, FALSE, FALSE,
6907 EL_ROCK, -1, MV_BIT_RIGHT
6910 Xpush_stone_w, FALSE, FALSE,
6911 EL_ROCK, -1, MV_BIT_LEFT
6914 Xpush_bomb_e, FALSE, FALSE,
6915 EL_BOMB, -1, MV_BIT_RIGHT
6918 Xpush_bomb_w, FALSE, FALSE,
6919 EL_BOMB, -1, MV_BIT_LEFT
6922 Xpush_nut_e, FALSE, FALSE,
6923 EL_NUT, -1, MV_BIT_RIGHT
6926 Xpush_nut_w, FALSE, FALSE,
6927 EL_NUT, -1, MV_BIT_LEFT
6930 Xpush_spring_e, FALSE, FALSE,
6931 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6934 Xpush_spring_w, FALSE, FALSE,
6935 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6939 Xdynamite, TRUE, FALSE,
6940 EL_EM_DYNAMITE, -1, -1
6943 Ydynamite_blank, FALSE, FALSE,
6944 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6947 Xdynamite_1, TRUE, FALSE,
6948 EL_EM_DYNAMITE_ACTIVE, -1, -1
6951 Xdynamite_2, FALSE, FALSE,
6952 EL_EM_DYNAMITE_ACTIVE, -1, -1
6955 Xdynamite_3, FALSE, FALSE,
6956 EL_EM_DYNAMITE_ACTIVE, -1, -1
6959 Xdynamite_4, FALSE, FALSE,
6960 EL_EM_DYNAMITE_ACTIVE, -1, -1
6964 Xkey_1, TRUE, FALSE,
6968 Xkey_2, TRUE, FALSE,
6972 Xkey_3, TRUE, FALSE,
6976 Xkey_4, TRUE, FALSE,
6980 Xkey_5, TRUE, FALSE,
6981 EL_EMC_KEY_5, -1, -1
6984 Xkey_6, TRUE, FALSE,
6985 EL_EMC_KEY_6, -1, -1
6988 Xkey_7, TRUE, FALSE,
6989 EL_EMC_KEY_7, -1, -1
6992 Xkey_8, TRUE, FALSE,
6993 EL_EMC_KEY_8, -1, -1
6997 Xdoor_1, TRUE, FALSE,
6998 EL_EM_GATE_1, -1, -1
7001 Xdoor_2, TRUE, FALSE,
7002 EL_EM_GATE_2, -1, -1
7005 Xdoor_3, TRUE, FALSE,
7006 EL_EM_GATE_3, -1, -1
7009 Xdoor_4, TRUE, FALSE,
7010 EL_EM_GATE_4, -1, -1
7013 Xdoor_5, TRUE, FALSE,
7014 EL_EMC_GATE_5, -1, -1
7017 Xdoor_6, TRUE, FALSE,
7018 EL_EMC_GATE_6, -1, -1
7021 Xdoor_7, TRUE, FALSE,
7022 EL_EMC_GATE_7, -1, -1
7025 Xdoor_8, TRUE, FALSE,
7026 EL_EMC_GATE_8, -1, -1
7030 Xfake_door_1, TRUE, FALSE,
7031 EL_EM_GATE_1_GRAY, -1, -1
7034 Xfake_door_2, TRUE, FALSE,
7035 EL_EM_GATE_2_GRAY, -1, -1
7038 Xfake_door_3, TRUE, FALSE,
7039 EL_EM_GATE_3_GRAY, -1, -1
7042 Xfake_door_4, TRUE, FALSE,
7043 EL_EM_GATE_4_GRAY, -1, -1
7046 Xfake_door_5, TRUE, FALSE,
7047 EL_EMC_GATE_5_GRAY, -1, -1
7050 Xfake_door_6, TRUE, FALSE,
7051 EL_EMC_GATE_6_GRAY, -1, -1
7054 Xfake_door_7, TRUE, FALSE,
7055 EL_EMC_GATE_7_GRAY, -1, -1
7058 Xfake_door_8, TRUE, FALSE,
7059 EL_EMC_GATE_8_GRAY, -1, -1
7063 Xballoon, TRUE, FALSE,
7067 Yballoon_n, FALSE, FALSE,
7068 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7071 Yballoon_nB, FALSE, TRUE,
7072 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7075 Yballoon_e, FALSE, FALSE,
7076 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7079 Yballoon_eB, FALSE, TRUE,
7080 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7083 Yballoon_s, FALSE, FALSE,
7084 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7087 Yballoon_sB, FALSE, TRUE,
7088 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7091 Yballoon_w, FALSE, FALSE,
7092 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7095 Yballoon_wB, FALSE, TRUE,
7096 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7100 Xball_1, TRUE, FALSE,
7101 EL_EMC_MAGIC_BALL, -1, -1
7104 Yball_1, FALSE, FALSE,
7105 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7108 Xball_2, FALSE, FALSE,
7109 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7112 Yball_2, FALSE, FALSE,
7113 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7116 Yball_blank, FALSE, FALSE,
7117 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7121 Xamoeba_1, TRUE, FALSE,
7122 EL_AMOEBA_DRY, ACTION_OTHER, -1
7125 Xamoeba_2, FALSE, FALSE,
7126 EL_AMOEBA_DRY, ACTION_OTHER, -1
7129 Xamoeba_3, FALSE, FALSE,
7130 EL_AMOEBA_DRY, ACTION_OTHER, -1
7133 Xamoeba_4, FALSE, FALSE,
7134 EL_AMOEBA_DRY, ACTION_OTHER, -1
7137 Xamoeba_5, TRUE, FALSE,
7138 EL_AMOEBA_WET, ACTION_OTHER, -1
7141 Xamoeba_6, FALSE, FALSE,
7142 EL_AMOEBA_WET, ACTION_OTHER, -1
7145 Xamoeba_7, FALSE, FALSE,
7146 EL_AMOEBA_WET, ACTION_OTHER, -1
7149 Xamoeba_8, FALSE, FALSE,
7150 EL_AMOEBA_WET, ACTION_OTHER, -1
7155 EL_AMOEBA_DROP, ACTION_GROWING, -1
7158 Xdrip_fall, FALSE, FALSE,
7159 EL_AMOEBA_DROP, -1, -1
7162 Xdrip_stretch, FALSE, FALSE,
7163 EL_AMOEBA_DROP, ACTION_FALLING, -1
7166 Xdrip_stretchB, FALSE, TRUE,
7167 EL_AMOEBA_DROP, ACTION_FALLING, -1
7170 Ydrip_1_s, FALSE, FALSE,
7171 EL_AMOEBA_DROP, ACTION_FALLING, -1
7174 Ydrip_1_sB, FALSE, TRUE,
7175 EL_AMOEBA_DROP, ACTION_FALLING, -1
7178 Ydrip_2_s, FALSE, FALSE,
7179 EL_AMOEBA_DROP, ACTION_FALLING, -1
7182 Ydrip_2_sB, FALSE, TRUE,
7183 EL_AMOEBA_DROP, ACTION_FALLING, -1
7187 Xwonderwall, TRUE, FALSE,
7188 EL_MAGIC_WALL, -1, -1
7191 Ywonderwall, FALSE, FALSE,
7192 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7196 Xwheel, TRUE, FALSE,
7197 EL_ROBOT_WHEEL, -1, -1
7200 Ywheel, FALSE, FALSE,
7201 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7205 Xswitch, TRUE, FALSE,
7206 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7209 Yswitch, FALSE, FALSE,
7210 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7214 Xbumper, TRUE, FALSE,
7215 EL_EMC_SPRING_BUMPER, -1, -1
7218 Ybumper, FALSE, FALSE,
7219 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7223 Xacid_nw, TRUE, FALSE,
7224 EL_ACID_POOL_TOPLEFT, -1, -1
7227 Xacid_ne, TRUE, FALSE,
7228 EL_ACID_POOL_TOPRIGHT, -1, -1
7231 Xacid_sw, TRUE, FALSE,
7232 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7235 Xacid_s, TRUE, FALSE,
7236 EL_ACID_POOL_BOTTOM, -1, -1
7239 Xacid_se, TRUE, FALSE,
7240 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7244 Xfake_blank, TRUE, FALSE,
7245 EL_INVISIBLE_WALL, -1, -1
7248 Yfake_blank, FALSE, FALSE,
7249 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7253 Xfake_grass, TRUE, FALSE,
7254 EL_EMC_FAKE_GRASS, -1, -1
7257 Yfake_grass, FALSE, FALSE,
7258 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7262 Xfake_amoeba, TRUE, FALSE,
7263 EL_EMC_DRIPPER, -1, -1
7266 Yfake_amoeba, FALSE, FALSE,
7267 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7271 Xlenses, TRUE, FALSE,
7272 EL_EMC_LENSES, -1, -1
7276 Xmagnify, TRUE, FALSE,
7277 EL_EMC_MAGNIFIER, -1, -1
7282 EL_QUICKSAND_EMPTY, -1, -1
7285 Xsand_stone, TRUE, FALSE,
7286 EL_QUICKSAND_FULL, -1, -1
7289 Xsand_stonein_1, FALSE, TRUE,
7290 EL_ROCK, ACTION_FILLING, -1
7293 Xsand_stonein_2, FALSE, TRUE,
7294 EL_ROCK, ACTION_FILLING, -1
7297 Xsand_stonein_3, FALSE, TRUE,
7298 EL_ROCK, ACTION_FILLING, -1
7301 Xsand_stonein_4, FALSE, TRUE,
7302 EL_ROCK, ACTION_FILLING, -1
7305 Xsand_sandstone_1, FALSE, FALSE,
7306 EL_QUICKSAND_FILLING, -1, -1
7309 Xsand_sandstone_2, FALSE, FALSE,
7310 EL_QUICKSAND_FILLING, -1, -1
7313 Xsand_sandstone_3, FALSE, FALSE,
7314 EL_QUICKSAND_FILLING, -1, -1
7317 Xsand_sandstone_4, FALSE, FALSE,
7318 EL_QUICKSAND_FILLING, -1, -1
7321 Xsand_stonesand_1, FALSE, FALSE,
7322 EL_QUICKSAND_EMPTYING, -1, -1
7325 Xsand_stonesand_2, FALSE, FALSE,
7326 EL_QUICKSAND_EMPTYING, -1, -1
7329 Xsand_stonesand_3, FALSE, FALSE,
7330 EL_QUICKSAND_EMPTYING, -1, -1
7333 Xsand_stonesand_4, FALSE, FALSE,
7334 EL_QUICKSAND_EMPTYING, -1, -1
7337 Xsand_stoneout_1, FALSE, FALSE,
7338 EL_ROCK, ACTION_EMPTYING, -1
7341 Xsand_stoneout_2, FALSE, FALSE,
7342 EL_ROCK, ACTION_EMPTYING, -1
7345 Xsand_stonesand_quickout_1, FALSE, FALSE,
7346 EL_QUICKSAND_EMPTYING, -1, -1
7349 Xsand_stonesand_quickout_2, FALSE, FALSE,
7350 EL_QUICKSAND_EMPTYING, -1, -1
7354 Xslide_ns, TRUE, FALSE,
7355 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7358 Yslide_ns_blank, FALSE, FALSE,
7359 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7362 Xslide_ew, TRUE, FALSE,
7363 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7366 Yslide_ew_blank, FALSE, FALSE,
7367 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7371 Xwind_n, TRUE, FALSE,
7372 EL_BALLOON_SWITCH_UP, -1, -1
7375 Xwind_e, TRUE, FALSE,
7376 EL_BALLOON_SWITCH_RIGHT, -1, -1
7379 Xwind_s, TRUE, FALSE,
7380 EL_BALLOON_SWITCH_DOWN, -1, -1
7383 Xwind_w, TRUE, FALSE,
7384 EL_BALLOON_SWITCH_LEFT, -1, -1
7387 Xwind_any, TRUE, FALSE,
7388 EL_BALLOON_SWITCH_ANY, -1, -1
7391 Xwind_stop, TRUE, FALSE,
7392 EL_BALLOON_SWITCH_NONE, -1, -1
7397 EL_EM_EXIT_CLOSED, -1, -1
7400 Xexit_1, TRUE, FALSE,
7401 EL_EM_EXIT_OPEN, -1, -1
7404 Xexit_2, FALSE, FALSE,
7405 EL_EM_EXIT_OPEN, -1, -1
7408 Xexit_3, FALSE, FALSE,
7409 EL_EM_EXIT_OPEN, -1, -1
7413 Xpause, FALSE, FALSE,
7418 Xwall_1, TRUE, FALSE,
7422 Xwall_2, TRUE, FALSE,
7423 EL_EMC_WALL_14, -1, -1
7426 Xwall_3, TRUE, FALSE,
7427 EL_EMC_WALL_15, -1, -1
7430 Xwall_4, TRUE, FALSE,
7431 EL_EMC_WALL_16, -1, -1
7435 Xroundwall_1, TRUE, FALSE,
7436 EL_WALL_SLIPPERY, -1, -1
7439 Xroundwall_2, TRUE, FALSE,
7440 EL_EMC_WALL_SLIPPERY_2, -1, -1
7443 Xroundwall_3, TRUE, FALSE,
7444 EL_EMC_WALL_SLIPPERY_3, -1, -1
7447 Xroundwall_4, TRUE, FALSE,
7448 EL_EMC_WALL_SLIPPERY_4, -1, -1
7452 Xsteel_1, TRUE, FALSE,
7453 EL_STEELWALL, -1, -1
7456 Xsteel_2, TRUE, FALSE,
7457 EL_EMC_STEELWALL_2, -1, -1
7460 Xsteel_3, TRUE, FALSE,
7461 EL_EMC_STEELWALL_3, -1, -1
7464 Xsteel_4, TRUE, FALSE,
7465 EL_EMC_STEELWALL_4, -1, -1
7469 Xdecor_1, TRUE, FALSE,
7470 EL_EMC_WALL_8, -1, -1
7473 Xdecor_2, TRUE, FALSE,
7474 EL_EMC_WALL_6, -1, -1
7477 Xdecor_3, TRUE, FALSE,
7478 EL_EMC_WALL_4, -1, -1
7481 Xdecor_4, TRUE, FALSE,
7482 EL_EMC_WALL_7, -1, -1
7485 Xdecor_5, TRUE, FALSE,
7486 EL_EMC_WALL_5, -1, -1
7489 Xdecor_6, TRUE, FALSE,
7490 EL_EMC_WALL_9, -1, -1
7493 Xdecor_7, TRUE, FALSE,
7494 EL_EMC_WALL_10, -1, -1
7497 Xdecor_8, TRUE, FALSE,
7498 EL_EMC_WALL_1, -1, -1
7501 Xdecor_9, TRUE, FALSE,
7502 EL_EMC_WALL_2, -1, -1
7505 Xdecor_10, TRUE, FALSE,
7506 EL_EMC_WALL_3, -1, -1
7509 Xdecor_11, TRUE, FALSE,
7510 EL_EMC_WALL_11, -1, -1
7513 Xdecor_12, TRUE, FALSE,
7514 EL_EMC_WALL_12, -1, -1
7518 Xalpha_0, TRUE, FALSE,
7519 EL_CHAR('0'), -1, -1
7522 Xalpha_1, TRUE, FALSE,
7523 EL_CHAR('1'), -1, -1
7526 Xalpha_2, TRUE, FALSE,
7527 EL_CHAR('2'), -1, -1
7530 Xalpha_3, TRUE, FALSE,
7531 EL_CHAR('3'), -1, -1
7534 Xalpha_4, TRUE, FALSE,
7535 EL_CHAR('4'), -1, -1
7538 Xalpha_5, TRUE, FALSE,
7539 EL_CHAR('5'), -1, -1
7542 Xalpha_6, TRUE, FALSE,
7543 EL_CHAR('6'), -1, -1
7546 Xalpha_7, TRUE, FALSE,
7547 EL_CHAR('7'), -1, -1
7550 Xalpha_8, TRUE, FALSE,
7551 EL_CHAR('8'), -1, -1
7554 Xalpha_9, TRUE, FALSE,
7555 EL_CHAR('9'), -1, -1
7558 Xalpha_excla, TRUE, FALSE,
7559 EL_CHAR('!'), -1, -1
7562 Xalpha_apost, TRUE, FALSE,
7563 EL_CHAR('\''), -1, -1
7566 Xalpha_comma, TRUE, FALSE,
7567 EL_CHAR(','), -1, -1
7570 Xalpha_minus, TRUE, FALSE,
7571 EL_CHAR('-'), -1, -1
7574 Xalpha_perio, TRUE, FALSE,
7575 EL_CHAR('.'), -1, -1
7578 Xalpha_colon, TRUE, FALSE,
7579 EL_CHAR(':'), -1, -1
7582 Xalpha_quest, TRUE, FALSE,
7583 EL_CHAR('?'), -1, -1
7586 Xalpha_a, TRUE, FALSE,
7587 EL_CHAR('A'), -1, -1
7590 Xalpha_b, TRUE, FALSE,
7591 EL_CHAR('B'), -1, -1
7594 Xalpha_c, TRUE, FALSE,
7595 EL_CHAR('C'), -1, -1
7598 Xalpha_d, TRUE, FALSE,
7599 EL_CHAR('D'), -1, -1
7602 Xalpha_e, TRUE, FALSE,
7603 EL_CHAR('E'), -1, -1
7606 Xalpha_f, TRUE, FALSE,
7607 EL_CHAR('F'), -1, -1
7610 Xalpha_g, TRUE, FALSE,
7611 EL_CHAR('G'), -1, -1
7614 Xalpha_h, TRUE, FALSE,
7615 EL_CHAR('H'), -1, -1
7618 Xalpha_i, TRUE, FALSE,
7619 EL_CHAR('I'), -1, -1
7622 Xalpha_j, TRUE, FALSE,
7623 EL_CHAR('J'), -1, -1
7626 Xalpha_k, TRUE, FALSE,
7627 EL_CHAR('K'), -1, -1
7630 Xalpha_l, TRUE, FALSE,
7631 EL_CHAR('L'), -1, -1
7634 Xalpha_m, TRUE, FALSE,
7635 EL_CHAR('M'), -1, -1
7638 Xalpha_n, TRUE, FALSE,
7639 EL_CHAR('N'), -1, -1
7642 Xalpha_o, TRUE, FALSE,
7643 EL_CHAR('O'), -1, -1
7646 Xalpha_p, TRUE, FALSE,
7647 EL_CHAR('P'), -1, -1
7650 Xalpha_q, TRUE, FALSE,
7651 EL_CHAR('Q'), -1, -1
7654 Xalpha_r, TRUE, FALSE,
7655 EL_CHAR('R'), -1, -1
7658 Xalpha_s, TRUE, FALSE,
7659 EL_CHAR('S'), -1, -1
7662 Xalpha_t, TRUE, FALSE,
7663 EL_CHAR('T'), -1, -1
7666 Xalpha_u, TRUE, FALSE,
7667 EL_CHAR('U'), -1, -1
7670 Xalpha_v, TRUE, FALSE,
7671 EL_CHAR('V'), -1, -1
7674 Xalpha_w, TRUE, FALSE,
7675 EL_CHAR('W'), -1, -1
7678 Xalpha_x, TRUE, FALSE,
7679 EL_CHAR('X'), -1, -1
7682 Xalpha_y, TRUE, FALSE,
7683 EL_CHAR('Y'), -1, -1
7686 Xalpha_z, TRUE, FALSE,
7687 EL_CHAR('Z'), -1, -1
7690 Xalpha_arrow_e, TRUE, FALSE,
7691 EL_CHAR('>'), -1, -1
7694 Xalpha_arrow_w, TRUE, FALSE,
7695 EL_CHAR('<'), -1, -1
7698 Xalpha_copyr, TRUE, FALSE,
7699 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7703 Ykey_1_blank, FALSE, FALSE,
7704 EL_EM_KEY_1, ACTION_COLLECTING, -1
7707 Ykey_2_blank, FALSE, FALSE,
7708 EL_EM_KEY_2, ACTION_COLLECTING, -1
7711 Ykey_3_blank, FALSE, FALSE,
7712 EL_EM_KEY_3, ACTION_COLLECTING, -1
7715 Ykey_4_blank, FALSE, FALSE,
7716 EL_EM_KEY_4, ACTION_COLLECTING, -1
7719 Ykey_5_blank, FALSE, FALSE,
7720 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7723 Ykey_6_blank, FALSE, FALSE,
7724 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7727 Ykey_7_blank, FALSE, FALSE,
7728 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7731 Ykey_8_blank, FALSE, FALSE,
7732 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7735 Ylenses_blank, FALSE, FALSE,
7736 EL_EMC_LENSES, ACTION_COLLECTING, -1
7739 Ymagnify_blank, FALSE, FALSE,
7740 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7743 Ygrass_blank, FALSE, FALSE,
7744 EL_EMC_GRASS, ACTION_SNAPPING, -1
7747 Ydirt_blank, FALSE, FALSE,
7748 EL_SAND, ACTION_SNAPPING, -1
7757 static struct Mapping_EM_to_RND_player
7766 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7770 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7774 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7778 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7782 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7786 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7790 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7794 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7798 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7802 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7806 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7810 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7814 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7818 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7822 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7826 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7830 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7834 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7838 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7842 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7846 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7850 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7854 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7858 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7862 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7866 EL_PLAYER_1, ACTION_DEFAULT, -1,
7870 EL_PLAYER_2, ACTION_DEFAULT, -1,
7874 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7878 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7882 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7886 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7890 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7894 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7898 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7902 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7906 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7910 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7914 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7918 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7922 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7926 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7930 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7934 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7938 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7942 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7946 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7950 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7954 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7958 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7962 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7966 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7970 EL_PLAYER_3, ACTION_DEFAULT, -1,
7974 EL_PLAYER_4, ACTION_DEFAULT, -1,
7983 int map_element_RND_to_EM_cave(int element_rnd)
7985 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7986 static boolean mapping_initialized = FALSE;
7988 if (!mapping_initialized)
7992 // return "Xalpha_quest" for all undefined elements in mapping array
7993 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7994 mapping_RND_to_EM[i] = Xalpha_quest;
7996 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7997 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7998 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7999 em_object_mapping_list[i].element_em;
8001 mapping_initialized = TRUE;
8004 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
8006 Warn("invalid RND level element %d", element_rnd);
8011 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
8014 int map_element_EM_to_RND_cave(int element_em_cave)
8016 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8017 static boolean mapping_initialized = FALSE;
8019 if (!mapping_initialized)
8023 // return "EL_UNKNOWN" for all undefined elements in mapping array
8024 for (i = 0; i < GAME_TILE_MAX; i++)
8025 mapping_EM_to_RND[i] = EL_UNKNOWN;
8027 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8028 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8029 em_object_mapping_list[i].element_rnd;
8031 mapping_initialized = TRUE;
8034 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8036 Warn("invalid EM cave element %d", element_em_cave);
8041 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8044 int map_element_EM_to_RND_game(int element_em_game)
8046 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8047 static boolean mapping_initialized = FALSE;
8049 if (!mapping_initialized)
8053 // return "EL_UNKNOWN" for all undefined elements in mapping array
8054 for (i = 0; i < GAME_TILE_MAX; i++)
8055 mapping_EM_to_RND[i] = EL_UNKNOWN;
8057 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8058 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8059 em_object_mapping_list[i].element_rnd;
8061 mapping_initialized = TRUE;
8064 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8066 Warn("invalid EM game element %d", element_em_game);
8071 return mapping_EM_to_RND[element_em_game];
8074 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8076 struct LevelInfo_EM *level_em = level->native_em_level;
8077 struct CAVE *cav = level_em->cav;
8080 for (i = 0; i < GAME_TILE_MAX; i++)
8081 cav->android_array[i] = Cblank;
8083 for (i = 0; i < level->num_android_clone_elements; i++)
8085 int element_rnd = level->android_clone_element[i];
8086 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8088 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8089 if (em_object_mapping_list[j].element_rnd == element_rnd)
8090 cav->android_array[em_object_mapping_list[j].element_em] =
8095 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8097 struct LevelInfo_EM *level_em = level->native_em_level;
8098 struct CAVE *cav = level_em->cav;
8101 level->num_android_clone_elements = 0;
8103 for (i = 0; i < GAME_TILE_MAX; i++)
8105 int element_em_cave = cav->android_array[i];
8107 boolean element_found = FALSE;
8109 if (element_em_cave == Cblank)
8112 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8114 for (j = 0; j < level->num_android_clone_elements; j++)
8115 if (level->android_clone_element[j] == element_rnd)
8116 element_found = TRUE;
8120 level->android_clone_element[level->num_android_clone_elements++] =
8123 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8128 if (level->num_android_clone_elements == 0)
8130 level->num_android_clone_elements = 1;
8131 level->android_clone_element[0] = EL_EMPTY;
8135 int map_direction_RND_to_EM(int direction)
8137 return (direction == MV_UP ? 0 :
8138 direction == MV_RIGHT ? 1 :
8139 direction == MV_DOWN ? 2 :
8140 direction == MV_LEFT ? 3 :
8144 int map_direction_EM_to_RND(int direction)
8146 return (direction == 0 ? MV_UP :
8147 direction == 1 ? MV_RIGHT :
8148 direction == 2 ? MV_DOWN :
8149 direction == 3 ? MV_LEFT :
8153 int map_element_RND_to_SP(int element_rnd)
8155 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8157 if (element_rnd >= EL_SP_START &&
8158 element_rnd <= EL_SP_END)
8159 element_sp = element_rnd - EL_SP_START;
8160 else if (element_rnd == EL_EMPTY_SPACE)
8162 else if (element_rnd == EL_INVISIBLE_WALL)
8168 int map_element_SP_to_RND(int element_sp)
8170 int element_rnd = EL_UNKNOWN;
8172 if (element_sp >= 0x00 &&
8174 element_rnd = EL_SP_START + element_sp;
8175 else if (element_sp == 0x28)
8176 element_rnd = EL_INVISIBLE_WALL;
8181 int map_action_SP_to_RND(int action_sp)
8185 case actActive: return ACTION_ACTIVE;
8186 case actImpact: return ACTION_IMPACT;
8187 case actExploding: return ACTION_EXPLODING;
8188 case actDigging: return ACTION_DIGGING;
8189 case actSnapping: return ACTION_SNAPPING;
8190 case actCollecting: return ACTION_COLLECTING;
8191 case actPassing: return ACTION_PASSING;
8192 case actPushing: return ACTION_PUSHING;
8193 case actDropping: return ACTION_DROPPING;
8195 default: return ACTION_DEFAULT;
8199 int map_element_RND_to_MM(int element_rnd)
8201 return (element_rnd >= EL_MM_START_1 &&
8202 element_rnd <= EL_MM_END_1 ?
8203 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8205 element_rnd >= EL_MM_START_2 &&
8206 element_rnd <= EL_MM_END_2 ?
8207 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8209 element_rnd >= EL_MM_START_3 &&
8210 element_rnd <= EL_MM_END_3 ?
8211 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
8213 element_rnd >= EL_CHAR_START &&
8214 element_rnd <= EL_CHAR_END ?
8215 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8217 element_rnd >= EL_MM_RUNTIME_START &&
8218 element_rnd <= EL_MM_RUNTIME_END ?
8219 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8221 EL_MM_EMPTY_NATIVE);
8224 int map_element_MM_to_RND(int element_mm)
8226 return (element_mm == EL_MM_EMPTY_NATIVE ||
8227 element_mm == EL_DF_EMPTY_NATIVE ?
8230 element_mm >= EL_MM_START_1_NATIVE &&
8231 element_mm <= EL_MM_END_1_NATIVE ?
8232 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8234 element_mm >= EL_MM_START_2_NATIVE &&
8235 element_mm <= EL_MM_END_2_NATIVE ?
8236 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8238 element_mm >= EL_MM_START_3_NATIVE &&
8239 element_mm <= EL_MM_END_3_NATIVE ?
8240 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
8242 element_mm >= EL_MM_CHAR_START_NATIVE &&
8243 element_mm <= EL_MM_CHAR_END_NATIVE ?
8244 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8246 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8247 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8248 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8253 int map_action_MM_to_RND(int action_mm)
8255 // all MM actions are defined to exactly match their RND counterparts
8259 int map_sound_MM_to_RND(int sound_mm)
8263 case SND_MM_GAME_LEVELTIME_CHARGING:
8264 return SND_GAME_LEVELTIME_CHARGING;
8266 case SND_MM_GAME_HEALTH_CHARGING:
8267 return SND_GAME_HEALTH_CHARGING;
8270 return SND_UNDEFINED;
8274 int map_mm_wall_element(int element)
8276 return (element >= EL_MM_STEEL_WALL_START &&
8277 element <= EL_MM_STEEL_WALL_END ?
8280 element >= EL_MM_WOODEN_WALL_START &&
8281 element <= EL_MM_WOODEN_WALL_END ?
8284 element >= EL_MM_ICE_WALL_START &&
8285 element <= EL_MM_ICE_WALL_END ?
8288 element >= EL_MM_AMOEBA_WALL_START &&
8289 element <= EL_MM_AMOEBA_WALL_END ?
8292 element >= EL_DF_STEEL_WALL_START &&
8293 element <= EL_DF_STEEL_WALL_END ?
8296 element >= EL_DF_WOODEN_WALL_START &&
8297 element <= EL_DF_WOODEN_WALL_END ?
8303 int map_mm_wall_element_editor(int element)
8307 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8308 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8309 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8310 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8311 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8312 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8314 default: return element;
8318 int get_next_element(int element)
8322 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8323 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8324 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8325 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8326 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8327 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8328 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8329 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8330 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8331 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8332 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8334 default: return element;
8338 int el2img_mm(int element_mm)
8340 return el2img(map_element_MM_to_RND(element_mm));
8343 int el_act2img_mm(int element_mm, int action)
8345 return el_act2img(map_element_MM_to_RND(element_mm), action);
8348 int el_act_dir2img(int element, int action, int direction)
8350 element = GFX_ELEMENT(element);
8351 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8353 // direction_graphic[][] == graphic[] for undefined direction graphics
8354 return element_info[element].direction_graphic[action][direction];
8357 static int el_act_dir2crm(int element, int action, int direction)
8359 element = GFX_ELEMENT(element);
8360 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8362 // direction_graphic[][] == graphic[] for undefined direction graphics
8363 return element_info[element].direction_crumbled[action][direction];
8366 int el_act2img(int element, int action)
8368 element = GFX_ELEMENT(element);
8370 return element_info[element].graphic[action];
8373 int el_act2crm(int element, int action)
8375 element = GFX_ELEMENT(element);
8377 return element_info[element].crumbled[action];
8380 int el_dir2img(int element, int direction)
8382 element = GFX_ELEMENT(element);
8384 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8387 int el2baseimg(int element)
8389 return element_info[element].graphic[ACTION_DEFAULT];
8392 int el2img(int element)
8394 element = GFX_ELEMENT(element);
8396 return element_info[element].graphic[ACTION_DEFAULT];
8399 int el2edimg(int element)
8401 element = GFX_ELEMENT(element);
8403 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8406 int el2preimg(int element)
8408 element = GFX_ELEMENT(element);
8410 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8413 int el2panelimg(int element)
8415 element = GFX_ELEMENT(element);
8417 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8420 int font2baseimg(int font_nr)
8422 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8425 int getBeltNrFromBeltElement(int element)
8427 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8428 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8429 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8432 int getBeltNrFromBeltActiveElement(int element)
8434 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8435 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8436 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8439 int getBeltNrFromBeltSwitchElement(int element)
8441 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8442 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8443 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8446 int getBeltDirNrFromBeltElement(int element)
8448 static int belt_base_element[4] =
8450 EL_CONVEYOR_BELT_1_LEFT,
8451 EL_CONVEYOR_BELT_2_LEFT,
8452 EL_CONVEYOR_BELT_3_LEFT,
8453 EL_CONVEYOR_BELT_4_LEFT
8456 int belt_nr = getBeltNrFromBeltElement(element);
8457 int belt_dir_nr = element - belt_base_element[belt_nr];
8459 return (belt_dir_nr % 3);
8462 int getBeltDirNrFromBeltSwitchElement(int element)
8464 static int belt_base_element[4] =
8466 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8467 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8468 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8469 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8472 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8473 int belt_dir_nr = element - belt_base_element[belt_nr];
8475 return (belt_dir_nr % 3);
8478 int getBeltDirFromBeltElement(int element)
8480 static int belt_move_dir[3] =
8487 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8489 return belt_move_dir[belt_dir_nr];
8492 int getBeltDirFromBeltSwitchElement(int element)
8494 static int belt_move_dir[3] =
8501 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8503 return belt_move_dir[belt_dir_nr];
8506 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8508 static int belt_base_element[4] =
8510 EL_CONVEYOR_BELT_1_LEFT,
8511 EL_CONVEYOR_BELT_2_LEFT,
8512 EL_CONVEYOR_BELT_3_LEFT,
8513 EL_CONVEYOR_BELT_4_LEFT
8516 return belt_base_element[belt_nr] + belt_dir_nr;
8519 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8521 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8523 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8526 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8528 static int belt_base_element[4] =
8530 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8531 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8532 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8533 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8536 return belt_base_element[belt_nr] + belt_dir_nr;
8539 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8541 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8543 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8546 boolean swapTiles_EM(boolean is_pre_emc_cave)
8548 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8551 boolean getTeamMode_EM(void)
8553 return game.team_mode || network_playing;
8556 boolean isActivePlayer_EM(int player_nr)
8558 return stored_player[player_nr].active;
8561 unsigned int InitRND(int seed)
8563 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8564 return InitEngineRandom_EM(seed);
8565 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8566 return InitEngineRandom_SP(seed);
8567 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8568 return InitEngineRandom_MM(seed);
8570 return InitEngineRandom_RND(seed);
8573 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8574 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8576 static int get_effective_element_EM(int tile, int frame_em)
8578 int element = object_mapping[tile].element_rnd;
8579 int action = object_mapping[tile].action;
8580 boolean is_backside = object_mapping[tile].is_backside;
8581 boolean action_removing = (action == ACTION_DIGGING ||
8582 action == ACTION_SNAPPING ||
8583 action == ACTION_COLLECTING);
8591 return (frame_em > 5 ? EL_EMPTY : element);
8597 else // frame_em == 7
8608 case Ydiamond_stone:
8612 case Xdrip_stretchB:
8628 case Ymagnify_blank:
8631 case Xsand_stonein_1:
8632 case Xsand_stonein_2:
8633 case Xsand_stonein_3:
8634 case Xsand_stonein_4:
8638 return (is_backside || action_removing ? EL_EMPTY : element);
8643 static boolean check_linear_animation_EM(int tile)
8647 case Xsand_stonesand_1:
8648 case Xsand_stonesand_quickout_1:
8649 case Xsand_sandstone_1:
8650 case Xsand_stonein_1:
8651 case Xsand_stoneout_1:
8679 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8680 boolean has_crumbled_graphics,
8681 int crumbled, int sync_frame)
8683 // if element can be crumbled, but certain action graphics are just empty
8684 // space (like instantly snapping sand to empty space in 1 frame), do not
8685 // treat these empty space graphics as crumbled graphics in EMC engine
8686 if (crumbled == IMG_EMPTY_SPACE)
8687 has_crumbled_graphics = FALSE;
8689 if (has_crumbled_graphics)
8691 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8692 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8693 g_crumbled->anim_delay,
8694 g_crumbled->anim_mode,
8695 g_crumbled->anim_start_frame,
8698 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8699 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8701 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8702 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8704 g_em->has_crumbled_graphics = TRUE;
8708 g_em->crumbled_bitmap = NULL;
8709 g_em->crumbled_src_x = 0;
8710 g_em->crumbled_src_y = 0;
8711 g_em->crumbled_border_size = 0;
8712 g_em->crumbled_tile_size = 0;
8714 g_em->has_crumbled_graphics = FALSE;
8719 void ResetGfxAnimation_EM(int x, int y, int tile)
8725 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8726 int tile, int frame_em, int x, int y)
8728 int action = object_mapping[tile].action;
8729 int direction = object_mapping[tile].direction;
8730 int effective_element = get_effective_element_EM(tile, frame_em);
8731 int graphic = (direction == MV_NONE ?
8732 el_act2img(effective_element, action) :
8733 el_act_dir2img(effective_element, action, direction));
8734 struct GraphicInfo *g = &graphic_info[graphic];
8736 boolean action_removing = (action == ACTION_DIGGING ||
8737 action == ACTION_SNAPPING ||
8738 action == ACTION_COLLECTING);
8739 boolean action_moving = (action == ACTION_FALLING ||
8740 action == ACTION_MOVING ||
8741 action == ACTION_PUSHING ||
8742 action == ACTION_EATING ||
8743 action == ACTION_FILLING ||
8744 action == ACTION_EMPTYING);
8745 boolean action_falling = (action == ACTION_FALLING ||
8746 action == ACTION_FILLING ||
8747 action == ACTION_EMPTYING);
8749 // special case: graphic uses "2nd movement tile" and has defined
8750 // 7 frames for movement animation (or less) => use default graphic
8751 // for last (8th) frame which ends the movement animation
8752 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8754 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8755 graphic = (direction == MV_NONE ?
8756 el_act2img(effective_element, action) :
8757 el_act_dir2img(effective_element, action, direction));
8759 g = &graphic_info[graphic];
8762 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8766 else if (action_moving)
8768 boolean is_backside = object_mapping[tile].is_backside;
8772 int direction = object_mapping[tile].direction;
8773 int move_dir = (action_falling ? MV_DOWN : direction);
8778 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8779 if (g->double_movement && frame_em == 0)
8783 if (move_dir == MV_LEFT)
8784 GfxFrame[x - 1][y] = GfxFrame[x][y];
8785 else if (move_dir == MV_RIGHT)
8786 GfxFrame[x + 1][y] = GfxFrame[x][y];
8787 else if (move_dir == MV_UP)
8788 GfxFrame[x][y - 1] = GfxFrame[x][y];
8789 else if (move_dir == MV_DOWN)
8790 GfxFrame[x][y + 1] = GfxFrame[x][y];
8797 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8798 if (tile == Xsand_stonesand_quickout_1 ||
8799 tile == Xsand_stonesand_quickout_2)
8803 if (graphic_info[graphic].anim_global_sync)
8804 sync_frame = FrameCounter;
8805 else if (graphic_info[graphic].anim_global_anim_sync)
8806 sync_frame = getGlobalAnimSyncFrame();
8807 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8808 sync_frame = GfxFrame[x][y];
8810 sync_frame = 0; // playfield border (pseudo steel)
8812 SetRandomAnimationValue(x, y);
8814 int frame = getAnimationFrame(g->anim_frames,
8817 g->anim_start_frame,
8820 g_em->unique_identifier =
8821 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8824 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8825 int tile, int frame_em, int x, int y)
8827 int action = object_mapping[tile].action;
8828 int direction = object_mapping[tile].direction;
8829 boolean is_backside = object_mapping[tile].is_backside;
8830 int effective_element = get_effective_element_EM(tile, frame_em);
8831 int effective_action = action;
8832 int graphic = (direction == MV_NONE ?
8833 el_act2img(effective_element, effective_action) :
8834 el_act_dir2img(effective_element, effective_action,
8836 int crumbled = (direction == MV_NONE ?
8837 el_act2crm(effective_element, effective_action) :
8838 el_act_dir2crm(effective_element, effective_action,
8840 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8841 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8842 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8843 struct GraphicInfo *g = &graphic_info[graphic];
8846 // special case: graphic uses "2nd movement tile" and has defined
8847 // 7 frames for movement animation (or less) => use default graphic
8848 // for last (8th) frame which ends the movement animation
8849 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8851 effective_action = ACTION_DEFAULT;
8852 graphic = (direction == MV_NONE ?
8853 el_act2img(effective_element, effective_action) :
8854 el_act_dir2img(effective_element, effective_action,
8856 crumbled = (direction == MV_NONE ?
8857 el_act2crm(effective_element, effective_action) :
8858 el_act_dir2crm(effective_element, effective_action,
8861 g = &graphic_info[graphic];
8864 if (graphic_info[graphic].anim_global_sync)
8865 sync_frame = FrameCounter;
8866 else if (graphic_info[graphic].anim_global_anim_sync)
8867 sync_frame = getGlobalAnimSyncFrame();
8868 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8869 sync_frame = GfxFrame[x][y];
8871 sync_frame = 0; // playfield border (pseudo steel)
8873 SetRandomAnimationValue(x, y);
8875 int frame = getAnimationFrame(g->anim_frames,
8878 g->anim_start_frame,
8881 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8882 g->double_movement && is_backside);
8884 // (updating the "crumbled" graphic definitions is probably not really needed,
8885 // as animations for crumbled graphics can't be longer than one EMC cycle)
8886 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8890 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8891 int player_nr, int anim, int frame_em)
8893 int element = player_mapping[player_nr][anim].element_rnd;
8894 int action = player_mapping[player_nr][anim].action;
8895 int direction = player_mapping[player_nr][anim].direction;
8896 int graphic = (direction == MV_NONE ?
8897 el_act2img(element, action) :
8898 el_act_dir2img(element, action, direction));
8899 struct GraphicInfo *g = &graphic_info[graphic];
8902 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8904 stored_player[player_nr].StepFrame = frame_em;
8906 sync_frame = stored_player[player_nr].Frame;
8908 int frame = getAnimationFrame(g->anim_frames,
8911 g->anim_start_frame,
8914 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8915 &g_em->src_x, &g_em->src_y, FALSE);
8918 void InitGraphicInfo_EM(void)
8922 // always start with reliable default values
8923 for (i = 0; i < GAME_TILE_MAX; i++)
8925 object_mapping[i].element_rnd = EL_UNKNOWN;
8926 object_mapping[i].is_backside = FALSE;
8927 object_mapping[i].action = ACTION_DEFAULT;
8928 object_mapping[i].direction = MV_NONE;
8931 // always start with reliable default values
8932 for (p = 0; p < MAX_PLAYERS; p++)
8934 for (i = 0; i < PLY_MAX; i++)
8936 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8937 player_mapping[p][i].action = ACTION_DEFAULT;
8938 player_mapping[p][i].direction = MV_NONE;
8942 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8944 int e = em_object_mapping_list[i].element_em;
8946 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8947 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8949 if (em_object_mapping_list[i].action != -1)
8950 object_mapping[e].action = em_object_mapping_list[i].action;
8952 if (em_object_mapping_list[i].direction != -1)
8953 object_mapping[e].direction =
8954 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8957 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8959 int a = em_player_mapping_list[i].action_em;
8960 int p = em_player_mapping_list[i].player_nr;
8962 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8964 if (em_player_mapping_list[i].action != -1)
8965 player_mapping[p][a].action = em_player_mapping_list[i].action;
8967 if (em_player_mapping_list[i].direction != -1)
8968 player_mapping[p][a].direction =
8969 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8972 for (i = 0; i < GAME_TILE_MAX; i++)
8974 int element = object_mapping[i].element_rnd;
8975 int action = object_mapping[i].action;
8976 int direction = object_mapping[i].direction;
8977 boolean is_backside = object_mapping[i].is_backside;
8978 boolean action_exploding = ((action == ACTION_EXPLODING ||
8979 action == ACTION_SMASHED_BY_ROCK ||
8980 action == ACTION_SMASHED_BY_SPRING) &&
8981 element != EL_DIAMOND);
8982 boolean action_active = (action == ACTION_ACTIVE);
8983 boolean action_other = (action == ACTION_OTHER);
8985 for (j = 0; j < 8; j++)
8987 int effective_element = get_effective_element_EM(i, j);
8988 int effective_action = (j < 7 ? action :
8989 i == Xdrip_stretch ? action :
8990 i == Xdrip_stretchB ? action :
8991 i == Ydrip_1_s ? action :
8992 i == Ydrip_1_sB ? action :
8993 i == Yball_1 ? action :
8994 i == Xball_2 ? action :
8995 i == Yball_2 ? action :
8996 i == Yball_blank ? action :
8997 i == Ykey_1_blank ? action :
8998 i == Ykey_2_blank ? action :
8999 i == Ykey_3_blank ? action :
9000 i == Ykey_4_blank ? action :
9001 i == Ykey_5_blank ? action :
9002 i == Ykey_6_blank ? action :
9003 i == Ykey_7_blank ? action :
9004 i == Ykey_8_blank ? action :
9005 i == Ylenses_blank ? action :
9006 i == Ymagnify_blank ? action :
9007 i == Ygrass_blank ? action :
9008 i == Ydirt_blank ? action :
9009 i == Xsand_stonein_1 ? action :
9010 i == Xsand_stonein_2 ? action :
9011 i == Xsand_stonein_3 ? action :
9012 i == Xsand_stonein_4 ? action :
9013 i == Xsand_stoneout_1 ? action :
9014 i == Xsand_stoneout_2 ? action :
9015 i == Xboom_android ? ACTION_EXPLODING :
9016 action_exploding ? ACTION_EXPLODING :
9017 action_active ? action :
9018 action_other ? action :
9020 int graphic = (el_act_dir2img(effective_element, effective_action,
9022 int crumbled = (el_act_dir2crm(effective_element, effective_action,
9024 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
9025 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
9026 boolean has_action_graphics = (graphic != base_graphic);
9027 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9028 struct GraphicInfo *g = &graphic_info[graphic];
9029 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9032 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9033 boolean special_animation = (action != ACTION_DEFAULT &&
9034 g->anim_frames == 3 &&
9035 g->anim_delay == 2 &&
9036 g->anim_mode & ANIM_LINEAR);
9037 int sync_frame = (i == Xdrip_stretch ? 7 :
9038 i == Xdrip_stretchB ? 7 :
9039 i == Ydrip_2_s ? j + 8 :
9040 i == Ydrip_2_sB ? j + 8 :
9049 i == Xfake_acid_1 ? 0 :
9050 i == Xfake_acid_2 ? 10 :
9051 i == Xfake_acid_3 ? 20 :
9052 i == Xfake_acid_4 ? 30 :
9053 i == Xfake_acid_5 ? 40 :
9054 i == Xfake_acid_6 ? 50 :
9055 i == Xfake_acid_7 ? 60 :
9056 i == Xfake_acid_8 ? 70 :
9057 i == Xfake_acid_1_player ? 0 :
9058 i == Xfake_acid_2_player ? 10 :
9059 i == Xfake_acid_3_player ? 20 :
9060 i == Xfake_acid_4_player ? 30 :
9061 i == Xfake_acid_5_player ? 40 :
9062 i == Xfake_acid_6_player ? 50 :
9063 i == Xfake_acid_7_player ? 60 :
9064 i == Xfake_acid_8_player ? 70 :
9066 i == Yball_2 ? j + 8 :
9067 i == Yball_blank ? j + 1 :
9068 i == Ykey_1_blank ? j + 1 :
9069 i == Ykey_2_blank ? j + 1 :
9070 i == Ykey_3_blank ? j + 1 :
9071 i == Ykey_4_blank ? j + 1 :
9072 i == Ykey_5_blank ? j + 1 :
9073 i == Ykey_6_blank ? j + 1 :
9074 i == Ykey_7_blank ? j + 1 :
9075 i == Ykey_8_blank ? j + 1 :
9076 i == Ylenses_blank ? j + 1 :
9077 i == Ymagnify_blank ? j + 1 :
9078 i == Ygrass_blank ? j + 1 :
9079 i == Ydirt_blank ? j + 1 :
9080 i == Xamoeba_1 ? 0 :
9081 i == Xamoeba_2 ? 1 :
9082 i == Xamoeba_3 ? 2 :
9083 i == Xamoeba_4 ? 3 :
9084 i == Xamoeba_5 ? 0 :
9085 i == Xamoeba_6 ? 1 :
9086 i == Xamoeba_7 ? 2 :
9087 i == Xamoeba_8 ? 3 :
9088 i == Xexit_2 ? j + 8 :
9089 i == Xexit_3 ? j + 16 :
9090 i == Xdynamite_1 ? 0 :
9091 i == Xdynamite_2 ? 8 :
9092 i == Xdynamite_3 ? 16 :
9093 i == Xdynamite_4 ? 24 :
9094 i == Xsand_stonein_1 ? j + 1 :
9095 i == Xsand_stonein_2 ? j + 9 :
9096 i == Xsand_stonein_3 ? j + 17 :
9097 i == Xsand_stonein_4 ? j + 25 :
9098 i == Xsand_stoneout_1 && j == 0 ? 0 :
9099 i == Xsand_stoneout_1 && j == 1 ? 0 :
9100 i == Xsand_stoneout_1 && j == 2 ? 1 :
9101 i == Xsand_stoneout_1 && j == 3 ? 2 :
9102 i == Xsand_stoneout_1 && j == 4 ? 2 :
9103 i == Xsand_stoneout_1 && j == 5 ? 3 :
9104 i == Xsand_stoneout_1 && j == 6 ? 4 :
9105 i == Xsand_stoneout_1 && j == 7 ? 4 :
9106 i == Xsand_stoneout_2 && j == 0 ? 5 :
9107 i == Xsand_stoneout_2 && j == 1 ? 6 :
9108 i == Xsand_stoneout_2 && j == 2 ? 7 :
9109 i == Xsand_stoneout_2 && j == 3 ? 8 :
9110 i == Xsand_stoneout_2 && j == 4 ? 9 :
9111 i == Xsand_stoneout_2 && j == 5 ? 11 :
9112 i == Xsand_stoneout_2 && j == 6 ? 13 :
9113 i == Xsand_stoneout_2 && j == 7 ? 15 :
9114 i == Xboom_bug && j == 1 ? 2 :
9115 i == Xboom_bug && j == 2 ? 2 :
9116 i == Xboom_bug && j == 3 ? 4 :
9117 i == Xboom_bug && j == 4 ? 4 :
9118 i == Xboom_bug && j == 5 ? 2 :
9119 i == Xboom_bug && j == 6 ? 2 :
9120 i == Xboom_bug && j == 7 ? 0 :
9121 i == Xboom_tank && j == 1 ? 2 :
9122 i == Xboom_tank && j == 2 ? 2 :
9123 i == Xboom_tank && j == 3 ? 4 :
9124 i == Xboom_tank && j == 4 ? 4 :
9125 i == Xboom_tank && j == 5 ? 2 :
9126 i == Xboom_tank && j == 6 ? 2 :
9127 i == Xboom_tank && j == 7 ? 0 :
9128 i == Xboom_android && j == 7 ? 6 :
9129 i == Xboom_1 && j == 1 ? 2 :
9130 i == Xboom_1 && j == 2 ? 2 :
9131 i == Xboom_1 && j == 3 ? 4 :
9132 i == Xboom_1 && j == 4 ? 4 :
9133 i == Xboom_1 && j == 5 ? 6 :
9134 i == Xboom_1 && j == 6 ? 6 :
9135 i == Xboom_1 && j == 7 ? 8 :
9136 i == Xboom_2 && j == 0 ? 8 :
9137 i == Xboom_2 && j == 1 ? 8 :
9138 i == Xboom_2 && j == 2 ? 10 :
9139 i == Xboom_2 && j == 3 ? 10 :
9140 i == Xboom_2 && j == 4 ? 10 :
9141 i == Xboom_2 && j == 5 ? 12 :
9142 i == Xboom_2 && j == 6 ? 12 :
9143 i == Xboom_2 && j == 7 ? 12 :
9144 special_animation && j == 4 ? 3 :
9145 effective_action != action ? 0 :
9147 int frame = getAnimationFrame(g->anim_frames,
9150 g->anim_start_frame,
9153 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9154 g->double_movement && is_backside);
9156 g_em->bitmap = src_bitmap;
9157 g_em->src_x = src_x;
9158 g_em->src_y = src_y;
9159 g_em->src_offset_x = 0;
9160 g_em->src_offset_y = 0;
9161 g_em->dst_offset_x = 0;
9162 g_em->dst_offset_y = 0;
9163 g_em->width = TILEX;
9164 g_em->height = TILEY;
9166 g_em->preserve_background = FALSE;
9168 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9171 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9172 effective_action == ACTION_MOVING ||
9173 effective_action == ACTION_PUSHING ||
9174 effective_action == ACTION_EATING)) ||
9175 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9176 effective_action == ACTION_EMPTYING)))
9179 (effective_action == ACTION_FALLING ||
9180 effective_action == ACTION_FILLING ||
9181 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9182 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9183 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9184 int num_steps = (i == Ydrip_1_s ? 16 :
9185 i == Ydrip_1_sB ? 16 :
9186 i == Ydrip_2_s ? 16 :
9187 i == Ydrip_2_sB ? 16 :
9188 i == Xsand_stonein_1 ? 32 :
9189 i == Xsand_stonein_2 ? 32 :
9190 i == Xsand_stonein_3 ? 32 :
9191 i == Xsand_stonein_4 ? 32 :
9192 i == Xsand_stoneout_1 ? 16 :
9193 i == Xsand_stoneout_2 ? 16 : 8);
9194 int cx = ABS(dx) * (TILEX / num_steps);
9195 int cy = ABS(dy) * (TILEY / num_steps);
9196 int step_frame = (i == Ydrip_2_s ? j + 8 :
9197 i == Ydrip_2_sB ? j + 8 :
9198 i == Xsand_stonein_2 ? j + 8 :
9199 i == Xsand_stonein_3 ? j + 16 :
9200 i == Xsand_stonein_4 ? j + 24 :
9201 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9202 int step = (is_backside ? step_frame : num_steps - step_frame);
9204 if (is_backside) // tile where movement starts
9206 if (dx < 0 || dy < 0)
9208 g_em->src_offset_x = cx * step;
9209 g_em->src_offset_y = cy * step;
9213 g_em->dst_offset_x = cx * step;
9214 g_em->dst_offset_y = cy * step;
9217 else // tile where movement ends
9219 if (dx < 0 || dy < 0)
9221 g_em->dst_offset_x = cx * step;
9222 g_em->dst_offset_y = cy * step;
9226 g_em->src_offset_x = cx * step;
9227 g_em->src_offset_y = cy * step;
9231 g_em->width = TILEX - cx * step;
9232 g_em->height = TILEY - cy * step;
9235 // create unique graphic identifier to decide if tile must be redrawn
9236 /* bit 31 - 16 (16 bit): EM style graphic
9237 bit 15 - 12 ( 4 bit): EM style frame
9238 bit 11 - 6 ( 6 bit): graphic width
9239 bit 5 - 0 ( 6 bit): graphic height */
9240 g_em->unique_identifier =
9241 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9245 for (i = 0; i < GAME_TILE_MAX; i++)
9247 for (j = 0; j < 8; j++)
9249 int element = object_mapping[i].element_rnd;
9250 int action = object_mapping[i].action;
9251 int direction = object_mapping[i].direction;
9252 boolean is_backside = object_mapping[i].is_backside;
9253 int graphic_action = el_act_dir2img(element, action, direction);
9254 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9256 if ((action == ACTION_SMASHED_BY_ROCK ||
9257 action == ACTION_SMASHED_BY_SPRING ||
9258 action == ACTION_EATING) &&
9259 graphic_action == graphic_default)
9261 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9262 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9263 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9264 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9267 // no separate animation for "smashed by rock" -- use rock instead
9268 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9269 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9271 g_em->bitmap = g_xx->bitmap;
9272 g_em->src_x = g_xx->src_x;
9273 g_em->src_y = g_xx->src_y;
9274 g_em->src_offset_x = g_xx->src_offset_x;
9275 g_em->src_offset_y = g_xx->src_offset_y;
9276 g_em->dst_offset_x = g_xx->dst_offset_x;
9277 g_em->dst_offset_y = g_xx->dst_offset_y;
9278 g_em->width = g_xx->width;
9279 g_em->height = g_xx->height;
9280 g_em->unique_identifier = g_xx->unique_identifier;
9283 g_em->preserve_background = TRUE;
9288 for (p = 0; p < MAX_PLAYERS; p++)
9290 for (i = 0; i < PLY_MAX; i++)
9292 int element = player_mapping[p][i].element_rnd;
9293 int action = player_mapping[p][i].action;
9294 int direction = player_mapping[p][i].direction;
9296 for (j = 0; j < 8; j++)
9298 int effective_element = element;
9299 int effective_action = action;
9300 int graphic = (direction == MV_NONE ?
9301 el_act2img(effective_element, effective_action) :
9302 el_act_dir2img(effective_element, effective_action,
9304 struct GraphicInfo *g = &graphic_info[graphic];
9305 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9309 int frame = getAnimationFrame(g->anim_frames,
9312 g->anim_start_frame,
9315 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9317 g_em->bitmap = src_bitmap;
9318 g_em->src_x = src_x;
9319 g_em->src_y = src_y;
9320 g_em->src_offset_x = 0;
9321 g_em->src_offset_y = 0;
9322 g_em->dst_offset_x = 0;
9323 g_em->dst_offset_y = 0;
9324 g_em->width = TILEX;
9325 g_em->height = TILEY;
9331 static void CheckSaveEngineSnapshot_EM(int frame,
9332 boolean any_player_moving,
9333 boolean any_player_snapping,
9334 boolean any_player_dropping)
9336 if (frame == 7 && !any_player_dropping)
9338 if (!local_player->was_waiting)
9340 if (!CheckSaveEngineSnapshotToList())
9343 local_player->was_waiting = TRUE;
9346 else if (any_player_moving || any_player_snapping || any_player_dropping)
9348 local_player->was_waiting = FALSE;
9352 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9353 boolean murphy_is_dropping)
9355 if (murphy_is_waiting)
9357 if (!local_player->was_waiting)
9359 if (!CheckSaveEngineSnapshotToList())
9362 local_player->was_waiting = TRUE;
9367 local_player->was_waiting = FALSE;
9371 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9372 boolean button_released)
9374 if (button_released)
9376 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9377 CheckSaveEngineSnapshotToList();
9379 else if (element_clicked)
9381 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9382 CheckSaveEngineSnapshotToList();
9384 game.snapshot.changed_action = TRUE;
9388 boolean CheckSingleStepMode_EM(int frame,
9389 boolean any_player_moving,
9390 boolean any_player_snapping,
9391 boolean any_player_dropping)
9393 if (tape.single_step && tape.recording && !tape.pausing)
9394 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9395 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9397 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9398 any_player_snapping, any_player_dropping);
9400 return tape.pausing;
9403 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9404 boolean murphy_is_dropping)
9406 boolean murphy_starts_dropping = FALSE;
9409 for (i = 0; i < MAX_PLAYERS; i++)
9410 if (stored_player[i].force_dropping)
9411 murphy_starts_dropping = TRUE;
9413 if (tape.single_step && tape.recording && !tape.pausing)
9414 if (murphy_is_waiting && !murphy_starts_dropping)
9415 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9417 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9420 void CheckSingleStepMode_MM(boolean element_clicked,
9421 boolean button_released)
9423 if (tape.single_step && tape.recording && !tape.pausing)
9424 if (button_released)
9425 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9427 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9430 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9431 int graphic, int sync_frame)
9433 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9435 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9438 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9440 return (IS_NEXT_FRAME(sync_frame, graphic));
9443 int getGraphicInfo_Delay(int graphic)
9445 return graphic_info[graphic].anim_delay;
9448 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9450 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9453 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9459 void PlayMenuSoundExt(int sound)
9461 if (sound == SND_UNDEFINED)
9464 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9465 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9468 if (IS_LOOP_SOUND(sound))
9469 PlaySoundLoop(sound);
9474 void PlayMenuSound(void)
9476 PlayMenuSoundExt(menu.sound[game_status]);
9479 void PlayMenuSoundStereo(int sound, int stereo_position)
9481 if (sound == SND_UNDEFINED)
9484 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9485 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9488 if (IS_LOOP_SOUND(sound))
9489 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9491 PlaySoundStereo(sound, stereo_position);
9494 void PlayMenuSoundIfLoopExt(int sound)
9496 if (sound == SND_UNDEFINED)
9499 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9500 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9503 if (IS_LOOP_SOUND(sound))
9504 PlaySoundLoop(sound);
9507 void PlayMenuSoundIfLoop(void)
9509 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9512 void PlayMenuMusicExt(int music)
9514 if (music == MUS_UNDEFINED)
9517 if (!setup.sound_music)
9520 if (IS_LOOP_MUSIC(music))
9521 PlayMusicLoop(music);
9526 void PlayMenuMusic(void)
9528 char *curr_music = getCurrentlyPlayingMusicFilename();
9529 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9531 if (!strEqual(curr_music, next_music))
9532 PlayMenuMusicExt(menu.music[game_status]);
9535 void PlayMenuSoundsAndMusic(void)
9541 static void FadeMenuSounds(void)
9546 static void FadeMenuMusic(void)
9548 char *curr_music = getCurrentlyPlayingMusicFilename();
9549 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9551 if (!strEqual(curr_music, next_music))
9555 void FadeMenuSoundsAndMusic(void)
9561 void PlaySoundActivating(void)
9564 PlaySound(SND_MENU_ITEM_ACTIVATING);
9568 void PlaySoundSelecting(void)
9571 PlaySound(SND_MENU_ITEM_SELECTING);
9575 void ToggleFullscreenIfNeeded(void)
9577 // if setup and video fullscreen state are already matching, nothing do do
9578 if (setup.fullscreen == video.fullscreen_enabled ||
9579 !video.fullscreen_available)
9582 SDLSetWindowFullscreen(setup.fullscreen);
9584 // set setup value according to successfully changed fullscreen mode
9585 setup.fullscreen = video.fullscreen_enabled;
9588 void ChangeWindowScalingIfNeeded(void)
9590 // if setup and video window scaling are already matching, nothing do do
9591 if (setup.window_scaling_percent == video.window_scaling_percent ||
9592 video.fullscreen_enabled)
9595 SDLSetWindowScaling(setup.window_scaling_percent);
9597 // set setup value according to successfully changed window scaling
9598 setup.window_scaling_percent = video.window_scaling_percent;
9601 void ChangeVsyncModeIfNeeded(void)
9603 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9604 int video_vsync_mode = video.vsync_mode;
9606 // if setup and video vsync mode are already matching, nothing do do
9607 if (setup_vsync_mode == video_vsync_mode)
9610 // if renderer is using OpenGL, vsync mode can directly be changed
9611 SDLSetScreenVsyncMode(setup.vsync_mode);
9613 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9614 if (video.vsync_mode == video_vsync_mode)
9616 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9618 // save backbuffer content which gets lost when re-creating screen
9619 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9621 // force re-creating screen and renderer to set new vsync mode
9622 video.fullscreen_enabled = !setup.fullscreen;
9624 // when creating new renderer, destroy textures linked to old renderer
9625 FreeAllImageTextures(); // needs old renderer to free the textures
9627 // re-create screen and renderer (including change of vsync mode)
9628 ChangeVideoModeIfNeeded(setup.fullscreen);
9630 // set setup value according to successfully changed fullscreen mode
9631 setup.fullscreen = video.fullscreen_enabled;
9633 // restore backbuffer content from temporary backbuffer backup bitmap
9634 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9635 FreeBitmap(tmp_backbuffer);
9637 // update visible window/screen
9638 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9640 // when changing vsync mode, re-create textures for new renderer
9641 InitImageTextures();
9644 // set setup value according to successfully changed vsync mode
9645 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9648 static void JoinRectangles(int *x, int *y, int *width, int *height,
9649 int x2, int y2, int width2, int height2)
9651 // do not join with "off-screen" rectangle
9652 if (x2 == -1 || y2 == -1)
9657 *width = MAX(*width, width2);
9658 *height = MAX(*height, height2);
9661 void SetAnimStatus(int anim_status_new)
9663 if (anim_status_new == GAME_MODE_MAIN)
9664 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9665 else if (anim_status_new == GAME_MODE_NAMES)
9666 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9667 else if (anim_status_new == GAME_MODE_SCORES)
9668 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9670 global.anim_status_next = anim_status_new;
9672 // directly set screen modes that are entered without fading
9673 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9674 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9675 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9676 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9677 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9678 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9679 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9680 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9681 global.anim_status = global.anim_status_next;
9684 void SetGameStatus(int game_status_new)
9686 if (game_status_new != game_status)
9687 game_status_last_screen = game_status;
9689 game_status = game_status_new;
9691 SetAnimStatus(game_status_new);
9694 void SetFontStatus(int game_status_new)
9696 static int last_game_status = -1;
9698 if (game_status_new != -1)
9700 // set game status for font use after storing last game status
9701 last_game_status = game_status;
9702 game_status = game_status_new;
9706 // reset game status after font use from last stored game status
9707 game_status = last_game_status;
9711 void ResetFontStatus(void)
9716 void SetLevelSetInfo(char *identifier, int level_nr)
9718 setString(&levelset.identifier, identifier);
9720 levelset.level_nr = level_nr;
9723 boolean CheckIfAllViewportsHaveChanged(void)
9725 // if game status has not changed, viewports have not changed either
9726 if (game_status == game_status_last)
9729 // check if all viewports have changed with current game status
9731 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9732 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9733 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9734 int new_real_sx = vp_playfield->x;
9735 int new_real_sy = vp_playfield->y;
9736 int new_full_sxsize = vp_playfield->width;
9737 int new_full_sysize = vp_playfield->height;
9738 int new_dx = vp_door_1->x;
9739 int new_dy = vp_door_1->y;
9740 int new_dxsize = vp_door_1->width;
9741 int new_dysize = vp_door_1->height;
9742 int new_vx = vp_door_2->x;
9743 int new_vy = vp_door_2->y;
9744 int new_vxsize = vp_door_2->width;
9745 int new_vysize = vp_door_2->height;
9747 boolean playfield_viewport_has_changed =
9748 (new_real_sx != REAL_SX ||
9749 new_real_sy != REAL_SY ||
9750 new_full_sxsize != FULL_SXSIZE ||
9751 new_full_sysize != FULL_SYSIZE);
9753 boolean door_1_viewport_has_changed =
9756 new_dxsize != DXSIZE ||
9757 new_dysize != DYSIZE);
9759 boolean door_2_viewport_has_changed =
9762 new_vxsize != VXSIZE ||
9763 new_vysize != VYSIZE ||
9764 game_status_last == GAME_MODE_EDITOR);
9766 return (playfield_viewport_has_changed &&
9767 door_1_viewport_has_changed &&
9768 door_2_viewport_has_changed);
9771 boolean CheckFadeAll(void)
9773 return (CheckIfGlobalBorderHasChanged() ||
9774 CheckIfAllViewportsHaveChanged());
9777 void ChangeViewportPropertiesIfNeeded(void)
9779 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9780 FALSE : setup.small_game_graphics);
9781 int gfx_game_mode = getGlobalGameStatus(game_status);
9782 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9784 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9785 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9786 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9787 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9788 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9789 int new_win_xsize = vp_window->width;
9790 int new_win_ysize = vp_window->height;
9791 int border_left = vp_playfield->border_left;
9792 int border_right = vp_playfield->border_right;
9793 int border_top = vp_playfield->border_top;
9794 int border_bottom = vp_playfield->border_bottom;
9795 int new_sx = vp_playfield->x + border_left;
9796 int new_sy = vp_playfield->y + border_top;
9797 int new_sxsize = vp_playfield->width - border_left - border_right;
9798 int new_sysize = vp_playfield->height - border_top - border_bottom;
9799 int new_real_sx = vp_playfield->x;
9800 int new_real_sy = vp_playfield->y;
9801 int new_full_sxsize = vp_playfield->width;
9802 int new_full_sysize = vp_playfield->height;
9803 int new_dx = vp_door_1->x;
9804 int new_dy = vp_door_1->y;
9805 int new_dxsize = vp_door_1->width;
9806 int new_dysize = vp_door_1->height;
9807 int new_vx = vp_door_2->x;
9808 int new_vy = vp_door_2->y;
9809 int new_vxsize = vp_door_2->width;
9810 int new_vysize = vp_door_2->height;
9811 int new_ex = vp_door_3->x;
9812 int new_ey = vp_door_3->y;
9813 int new_exsize = vp_door_3->width;
9814 int new_eysize = vp_door_3->height;
9815 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9816 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9817 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9818 int new_scr_fieldx = new_sxsize / tilesize;
9819 int new_scr_fieldy = new_sysize / tilesize;
9820 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9821 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9822 boolean init_gfx_buffers = FALSE;
9823 boolean init_video_buffer = FALSE;
9824 boolean init_gadgets_and_anims = FALSE;
9825 boolean init_em_graphics = FALSE;
9827 if (new_win_xsize != WIN_XSIZE ||
9828 new_win_ysize != WIN_YSIZE)
9830 WIN_XSIZE = new_win_xsize;
9831 WIN_YSIZE = new_win_ysize;
9833 init_video_buffer = TRUE;
9834 init_gfx_buffers = TRUE;
9835 init_gadgets_and_anims = TRUE;
9837 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9840 if (new_scr_fieldx != SCR_FIELDX ||
9841 new_scr_fieldy != SCR_FIELDY)
9843 // this always toggles between MAIN and GAME when using small tile size
9845 SCR_FIELDX = new_scr_fieldx;
9846 SCR_FIELDY = new_scr_fieldy;
9848 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9859 new_sxsize != SXSIZE ||
9860 new_sysize != SYSIZE ||
9861 new_dxsize != DXSIZE ||
9862 new_dysize != DYSIZE ||
9863 new_vxsize != VXSIZE ||
9864 new_vysize != VYSIZE ||
9865 new_exsize != EXSIZE ||
9866 new_eysize != EYSIZE ||
9867 new_real_sx != REAL_SX ||
9868 new_real_sy != REAL_SY ||
9869 new_full_sxsize != FULL_SXSIZE ||
9870 new_full_sysize != FULL_SYSIZE ||
9871 new_tilesize_var != TILESIZE_VAR
9874 // ------------------------------------------------------------------------
9875 // determine next fading area for changed viewport definitions
9876 // ------------------------------------------------------------------------
9878 // start with current playfield area (default fading area)
9881 FADE_SXSIZE = FULL_SXSIZE;
9882 FADE_SYSIZE = FULL_SYSIZE;
9884 // add new playfield area if position or size has changed
9885 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9886 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9888 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9889 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9892 // add current and new door 1 area if position or size has changed
9893 if (new_dx != DX || new_dy != DY ||
9894 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9896 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9897 DX, DY, DXSIZE, DYSIZE);
9898 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9899 new_dx, new_dy, new_dxsize, new_dysize);
9902 // add current and new door 2 area if position or size has changed
9903 if (new_vx != VX || new_vy != VY ||
9904 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9906 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9907 VX, VY, VXSIZE, VYSIZE);
9908 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9909 new_vx, new_vy, new_vxsize, new_vysize);
9912 // ------------------------------------------------------------------------
9913 // handle changed tile size
9914 // ------------------------------------------------------------------------
9916 if (new_tilesize_var != TILESIZE_VAR)
9918 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9920 // changing tile size invalidates scroll values of engine snapshots
9921 FreeEngineSnapshotSingle();
9923 // changing tile size requires update of graphic mapping for EM engine
9924 init_em_graphics = TRUE;
9935 SXSIZE = new_sxsize;
9936 SYSIZE = new_sysize;
9937 DXSIZE = new_dxsize;
9938 DYSIZE = new_dysize;
9939 VXSIZE = new_vxsize;
9940 VYSIZE = new_vysize;
9941 EXSIZE = new_exsize;
9942 EYSIZE = new_eysize;
9943 REAL_SX = new_real_sx;
9944 REAL_SY = new_real_sy;
9945 FULL_SXSIZE = new_full_sxsize;
9946 FULL_SYSIZE = new_full_sysize;
9947 TILESIZE_VAR = new_tilesize_var;
9949 init_gfx_buffers = TRUE;
9950 init_gadgets_and_anims = TRUE;
9952 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9953 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9956 if (init_gfx_buffers)
9958 // Debug("tools:viewport", "init_gfx_buffers");
9960 SCR_FIELDX = new_scr_fieldx_buffers;
9961 SCR_FIELDY = new_scr_fieldy_buffers;
9965 SCR_FIELDX = new_scr_fieldx;
9966 SCR_FIELDY = new_scr_fieldy;
9968 SetDrawDeactivationMask(REDRAW_NONE);
9969 SetDrawBackgroundMask(REDRAW_FIELD);
9972 if (init_video_buffer)
9974 // Debug("tools:viewport", "init_video_buffer");
9976 FreeAllImageTextures(); // needs old renderer to free the textures
9978 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9979 InitImageTextures();
9982 if (init_gadgets_and_anims)
9984 // Debug("tools:viewport", "init_gadgets_and_anims");
9987 InitGlobalAnimations();
9990 if (init_em_graphics)
9992 InitGraphicInfo_EM();
9996 void OpenURL(char *url)
9998 #if SDL_VERSION_ATLEAST(2,0,14)
10001 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
10002 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
10003 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
10007 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
10009 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
10013 // ============================================================================
10015 // ============================================================================
10017 #if defined(PLATFORM_WINDOWS)
10018 /* FILETIME of Jan 1 1970 00:00:00. */
10019 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
10022 * timezone information is stored outside the kernel so tzp isn't used anymore.
10024 * Note: this function is not for Win32 high precision timing purpose. See
10027 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
10029 FILETIME file_time;
10030 SYSTEMTIME system_time;
10031 ULARGE_INTEGER ularge;
10033 GetSystemTime(&system_time);
10034 SystemTimeToFileTime(&system_time, &file_time);
10035 ularge.LowPart = file_time.dwLowDateTime;
10036 ularge.HighPart = file_time.dwHighDateTime;
10038 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10039 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10045 static char *test_init_uuid_random_function_simple(void)
10047 static char seed_text[100];
10048 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10050 sprintf(seed_text, "%d", seed);
10055 static char *test_init_uuid_random_function_better(void)
10057 static char seed_text[100];
10058 struct timeval current_time;
10060 gettimeofday(¤t_time, NULL);
10062 prng_seed_bytes(¤t_time, sizeof(current_time));
10064 sprintf(seed_text, "%ld.%ld",
10065 (long)current_time.tv_sec,
10066 (long)current_time.tv_usec);
10071 #if defined(PLATFORM_WINDOWS)
10072 static char *test_init_uuid_random_function_better_windows(void)
10074 static char seed_text[100];
10075 struct timeval current_time;
10077 gettimeofday_windows(¤t_time, NULL);
10079 prng_seed_bytes(¤t_time, sizeof(current_time));
10081 sprintf(seed_text, "%ld.%ld",
10082 (long)current_time.tv_sec,
10083 (long)current_time.tv_usec);
10089 static unsigned int test_uuid_random_function_simple(int max)
10091 return GetSimpleRandom(max);
10094 static unsigned int test_uuid_random_function_better(int max)
10096 return (max > 0 ? prng_get_uint() % max : 0);
10099 #if defined(PLATFORM_WINDOWS)
10100 #define NUM_UUID_TESTS 3
10102 #define NUM_UUID_TESTS 2
10105 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10107 struct hashtable *hash_seeds =
10108 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10109 struct hashtable *hash_uuids =
10110 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10111 static char message[100];
10114 char *random_name = (nr == 0 ? "simple" : "better");
10115 char *random_type = (always_seed ? "always" : "only once");
10116 char *(*init_random_function)(void) =
10118 test_init_uuid_random_function_simple :
10119 test_init_uuid_random_function_better);
10120 unsigned int (*random_function)(int) =
10122 test_uuid_random_function_simple :
10123 test_uuid_random_function_better);
10126 #if defined(PLATFORM_WINDOWS)
10129 random_name = "windows";
10130 init_random_function = test_init_uuid_random_function_better_windows;
10136 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10137 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10139 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10140 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10141 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10143 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10147 // always initialize random number generator at least once
10148 init_random_function();
10150 unsigned int time_start = SDL_GetTicks();
10152 for (i = 0; i < num_uuids; i++)
10156 char *seed = getStringCopy(init_random_function());
10158 hashtable_remove(hash_seeds, seed);
10159 hashtable_insert(hash_seeds, seed, "1");
10162 char *uuid = getStringCopy(getUUIDExt(random_function));
10164 hashtable_remove(hash_uuids, uuid);
10165 hashtable_insert(hash_uuids, uuid, "1");
10168 int num_unique_seeds = hashtable_count(hash_seeds);
10169 int num_unique_uuids = hashtable_count(hash_uuids);
10171 unsigned int time_needed = SDL_GetTicks() - time_start;
10173 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10175 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10178 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10180 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10181 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10183 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10185 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10187 Request(message, REQ_CONFIRM);
10189 hashtable_destroy(hash_seeds, 0);
10190 hashtable_destroy(hash_uuids, 0);
10193 void TestGeneratingUUIDs(void)
10195 int num_uuids = 1000000;
10198 for (i = 0; i < NUM_UUID_TESTS; i++)
10199 for (j = 0; j < 2; j++)
10200 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10202 CloseAllAndExit(0);