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 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
639 global.border_status = last_border_status;
640 gfx.masked_border_bitmap_ptr = backbuffer;
644 void DrawTileCursor(int draw_target)
646 DrawTileCursor_MM(draw_target, game_status == GAME_MODE_PLAYING);
649 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
651 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
654 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
656 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
657 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
659 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
662 void BlitScreenToBitmap(Bitmap *target_bitmap)
664 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
665 BlitScreenToBitmap_EM(target_bitmap);
666 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
667 BlitScreenToBitmap_SP(target_bitmap);
668 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
669 BlitScreenToBitmap_MM(target_bitmap);
670 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
671 BlitScreenToBitmap_RND(target_bitmap);
673 redraw_mask |= REDRAW_FIELD;
676 static void DrawFramesPerSecond(void)
679 int font_nr = FONT_TEXT_2;
680 int font_width = getFontWidth(font_nr);
681 int draw_deactivation_mask = GetDrawDeactivationMask();
682 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
684 // draw FPS with leading space (needed if field buffer deactivated)
685 sprintf(text, " %04.1f fps", global.frames_per_second);
687 // override draw deactivation mask (required for invisible warp mode)
688 SetDrawDeactivationMask(REDRAW_NONE);
690 // draw opaque FPS if field buffer deactivated, else draw masked FPS
691 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
692 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
694 // set draw deactivation mask to previous value
695 SetDrawDeactivationMask(draw_deactivation_mask);
697 // force full-screen redraw in this frame
698 redraw_mask = REDRAW_ALL;
702 static void PrintFrameTimeDebugging(void)
704 static unsigned int last_counter = 0;
705 unsigned int counter = Counter();
706 int diff_1 = counter - last_counter;
707 int diff_2 = diff_1 - GAME_FRAME_DELAY;
709 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
710 char diff_bar[2 * diff_2_max + 5];
714 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
716 for (i = 0; i < diff_2_max; i++)
717 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
718 i >= diff_2_max - diff_2_cut ? '-' : ' ');
720 diff_bar[pos++] = '|';
722 for (i = 0; i < diff_2_max; i++)
723 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
725 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
727 diff_bar[pos++] = '\0';
729 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
732 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
735 last_counter = counter;
739 static int unifiedRedrawMask(int mask)
741 if (mask & REDRAW_ALL)
744 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
750 static boolean equalRedrawMasks(int mask_1, int mask_2)
752 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
755 void BackToFront(void)
757 static int last_redraw_mask = REDRAW_NONE;
759 // force screen redraw in every frame to continue drawing global animations
760 // (but always use the last redraw mask to prevent unwanted side effects)
761 if (redraw_mask == REDRAW_NONE)
762 redraw_mask = last_redraw_mask;
764 last_redraw_mask = redraw_mask;
767 // masked border now drawn immediately when blitting backbuffer to window
769 // draw masked border to all viewports, if defined
770 DrawMaskedBorder(redraw_mask);
773 // draw frames per second (only if debug mode is enabled)
774 if (redraw_mask & REDRAW_FPS)
775 DrawFramesPerSecond();
777 // remove playfield redraw before potentially merging with doors redraw
778 if (DrawingDeactivated(REAL_SX, REAL_SY))
779 redraw_mask &= ~REDRAW_FIELD;
781 // redraw complete window if both playfield and (some) doors need redraw
782 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
783 redraw_mask = REDRAW_ALL;
785 /* although redrawing the whole window would be fine for normal gameplay,
786 being able to only redraw the playfield is required for deactivating
787 certain drawing areas (mainly playfield) to work, which is needed for
788 warp-forward to be fast enough (by skipping redraw of most frames) */
790 if (redraw_mask & REDRAW_ALL)
792 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
794 else if (redraw_mask & REDRAW_FIELD)
796 BlitBitmap(backbuffer, window,
797 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
799 else if (redraw_mask & REDRAW_DOORS)
801 // merge door areas to prevent calling screen redraw more than once
807 if (redraw_mask & REDRAW_DOOR_1)
811 x2 = MAX(x2, DX + DXSIZE);
812 y2 = MAX(y2, DY + DYSIZE);
815 if (redraw_mask & REDRAW_DOOR_2)
819 x2 = MAX(x2, VX + VXSIZE);
820 y2 = MAX(y2, VY + VYSIZE);
823 if (redraw_mask & REDRAW_DOOR_3)
827 x2 = MAX(x2, EX + EXSIZE);
828 y2 = MAX(y2, EY + EYSIZE);
831 // make sure that at least one pixel is blitted, and inside the screen
832 // (else nothing is blitted, causing the animations not to be updated)
833 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
834 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
835 x2 = MIN(MAX(1, x2), WIN_XSIZE);
836 y2 = MIN(MAX(1, y2), WIN_YSIZE);
838 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
841 redraw_mask = REDRAW_NONE;
844 PrintFrameTimeDebugging();
848 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
850 unsigned int frame_delay_value_old = GetVideoFrameDelay();
852 SetVideoFrameDelay(frame_delay_value);
856 SetVideoFrameDelay(frame_delay_value_old);
859 static int fade_type_skip = FADE_TYPE_NONE;
861 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
863 void (*draw_border_function)(void) = NULL;
864 int x, y, width, height;
865 int fade_delay, post_delay;
867 if (fade_type == FADE_TYPE_FADE_OUT)
869 if (fade_type_skip != FADE_TYPE_NONE)
871 // skip all fade operations until specified fade operation
872 if (fade_type & fade_type_skip)
873 fade_type_skip = FADE_TYPE_NONE;
878 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
882 redraw_mask |= fade_mask;
884 if (fade_type == FADE_TYPE_SKIP)
886 fade_type_skip = fade_mode;
891 fade_delay = fading.fade_delay;
892 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
894 if (fade_type_skip != FADE_TYPE_NONE)
896 // skip all fade operations until specified fade operation
897 if (fade_type & fade_type_skip)
898 fade_type_skip = FADE_TYPE_NONE;
903 if (global.autoplay_leveldir)
908 if (fade_mask == REDRAW_FIELD)
913 height = FADE_SYSIZE;
915 if (border.draw_masked_when_fading)
916 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
918 DrawMaskedBorder_FIELD(); // draw once
928 // when switching screens without fading, set fade delay to zero
929 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
932 // do not display black frame when fading out without fade delay
933 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
936 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
937 draw_border_function);
939 redraw_mask &= ~fade_mask;
941 ClearAutoRepeatKeyEvents();
944 static void SetScreenStates_BeforeFadingIn(void)
946 // temporarily set screen mode for animations to screen after fading in
947 global.anim_status = global.anim_status_next;
949 // store backbuffer with all animations that will be started after fading in
950 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
952 // set screen mode for animations back to fading
953 global.anim_status = GAME_MODE_PSEUDO_FADING;
956 static void SetScreenStates_AfterFadingIn(void)
958 // store new source screen (to use correct masked border for fading)
959 gfx.fade_border_source_status = global.border_status;
961 global.anim_status = global.anim_status_next;
964 static void SetScreenStates_BeforeFadingOut(void)
966 // store new target screen (to use correct masked border for fading)
967 gfx.fade_border_target_status = game_status;
969 // set screen mode for animations to fading
970 global.anim_status = GAME_MODE_PSEUDO_FADING;
972 // store backbuffer with all animations that will be stopped for fading out
973 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
976 static void SetScreenStates_AfterFadingOut(void)
978 global.border_status = game_status;
981 void FadeIn(int fade_mask)
983 SetScreenStates_BeforeFadingIn();
986 DrawMaskedBorder(REDRAW_ALL);
989 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
990 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
992 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
996 FADE_SXSIZE = FULL_SXSIZE;
997 FADE_SYSIZE = FULL_SYSIZE;
999 // activate virtual buttons depending on upcoming game status
1000 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1001 game_status == GAME_MODE_PLAYING && !tape.playing)
1002 SetOverlayActive(TRUE);
1004 SetScreenStates_AfterFadingIn();
1006 // force update of global animation status in case of rapid screen changes
1007 redraw_mask = REDRAW_ALL;
1011 void FadeOut(int fade_mask)
1013 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1014 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1015 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1018 SetScreenStates_BeforeFadingOut();
1020 SetTileCursorActive(FALSE);
1021 SetOverlayActive(FALSE);
1024 DrawMaskedBorder(REDRAW_ALL);
1027 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1028 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1030 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1032 SetScreenStates_AfterFadingOut();
1035 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1037 static struct TitleFadingInfo fading_leave_stored;
1040 fading_leave_stored = fading_leave;
1042 fading = fading_leave_stored;
1045 void FadeSetEnterMenu(void)
1047 fading = menu.enter_menu;
1049 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1052 void FadeSetLeaveMenu(void)
1054 fading = menu.leave_menu;
1056 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1059 void FadeSetEnterScreen(void)
1061 fading = menu.enter_screen[game_status];
1063 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1066 void FadeSetNextScreen(void)
1068 fading = menu.next_screen[game_status];
1070 // (do not overwrite fade mode set by FadeSetEnterScreen)
1071 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1074 void FadeSetLeaveScreen(void)
1076 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1079 void FadeSetFromType(int type)
1081 if (type & TYPE_ENTER_SCREEN)
1082 FadeSetEnterScreen();
1083 else if (type & TYPE_ENTER)
1085 else if (type & TYPE_LEAVE)
1089 void FadeSetDisabled(void)
1091 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1093 fading = fading_none;
1096 void FadeSkipNextFadeIn(void)
1098 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1101 void FadeSkipNextFadeOut(void)
1103 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1106 static int getGlobalGameStatus(int status)
1108 return (status == GAME_MODE_PSEUDO_TYPENAME ? GAME_MODE_MAIN :
1109 status == GAME_MODE_SCOREINFO ? GAME_MODE_SCORES :
1113 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1115 if (graphic == IMG_UNDEFINED)
1118 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1120 return (graphic_info[graphic].bitmap != NULL || redefined ?
1121 graphic_info[graphic].bitmap :
1122 graphic_info[default_graphic].bitmap);
1125 static Bitmap *getBackgroundBitmap(int graphic)
1127 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1130 static Bitmap *getGlobalBorderBitmap(int graphic)
1132 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1135 Bitmap *getGlobalBorderBitmapFromStatus(int status_raw)
1137 int status = getGlobalGameStatus(status_raw);
1139 (status == GAME_MODE_MAIN ? IMG_GLOBAL_BORDER_MAIN :
1140 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1141 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1142 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1145 return getGlobalBorderBitmap(graphic);
1148 void SetWindowBackgroundImageIfDefined(int graphic)
1150 if (graphic_info[graphic].bitmap)
1151 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1154 void SetMainBackgroundImageIfDefined(int graphic)
1156 if (graphic_info[graphic].bitmap)
1157 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1160 void SetDoorBackgroundImageIfDefined(int graphic)
1162 if (graphic_info[graphic].bitmap)
1163 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1166 void SetWindowBackgroundImage(int graphic)
1168 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1171 void SetMainBackgroundImage(int graphic)
1173 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1176 void SetDoorBackgroundImage(int graphic)
1178 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1181 void SetPanelBackground(void)
1183 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1185 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1186 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1188 SetDoorBackgroundBitmap(bitmap_db_panel);
1191 void DrawBackground(int x, int y, int width, int height)
1193 // "drawto" might still point to playfield buffer here (hall of fame)
1194 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1196 if (IN_GFX_FIELD_FULL(x, y))
1197 redraw_mask |= REDRAW_FIELD;
1198 else if (IN_GFX_DOOR_1(x, y))
1199 redraw_mask |= REDRAW_DOOR_1;
1200 else if (IN_GFX_DOOR_2(x, y))
1201 redraw_mask |= REDRAW_DOOR_2;
1202 else if (IN_GFX_DOOR_3(x, y))
1203 redraw_mask |= REDRAW_DOOR_3;
1206 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1208 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1210 if (font->bitmap == NULL)
1213 DrawBackground(x, y, width, height);
1216 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1218 struct GraphicInfo *g = &graphic_info[graphic];
1220 if (g->bitmap == NULL)
1223 DrawBackground(x, y, width, height);
1226 static int game_status_last = -1;
1227 static Bitmap *global_border_bitmap_last = NULL;
1228 static Bitmap *global_border_bitmap = NULL;
1229 static int real_sx_last = -1, real_sy_last = -1;
1230 static int full_sxsize_last = -1, full_sysize_last = -1;
1231 static int dx_last = -1, dy_last = -1;
1232 static int dxsize_last = -1, dysize_last = -1;
1233 static int vx_last = -1, vy_last = -1;
1234 static int vxsize_last = -1, vysize_last = -1;
1235 static int ex_last = -1, ey_last = -1;
1236 static int exsize_last = -1, eysize_last = -1;
1238 boolean CheckIfGlobalBorderHasChanged(void)
1240 // if game status has not changed, global border has not changed either
1241 if (game_status == game_status_last)
1244 // determine and store new global border bitmap for current game status
1245 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1247 return (global_border_bitmap_last != global_border_bitmap);
1250 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1252 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1253 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1255 // if game status has not changed, nothing has to be redrawn
1256 if (game_status == game_status_last)
1259 // redraw if last screen was title screen
1260 if (game_status_last == GAME_MODE_TITLE)
1263 // redraw if global screen border has changed
1264 if (CheckIfGlobalBorderHasChanged())
1267 // redraw if position or size of playfield area has changed
1268 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1269 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1272 // redraw if position or size of door area has changed
1273 if (dx_last != DX || dy_last != DY ||
1274 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1277 // redraw if position or size of tape area has changed
1278 if (vx_last != VX || vy_last != VY ||
1279 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1282 // redraw if position or size of editor area has changed
1283 if (ex_last != EX || ey_last != EY ||
1284 exsize_last != EXSIZE || eysize_last != EYSIZE)
1291 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1294 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1296 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1299 void RedrawGlobalBorder(void)
1301 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1303 RedrawGlobalBorderFromBitmap(bitmap);
1305 redraw_mask = REDRAW_ALL;
1308 static void RedrawGlobalBorderIfNeeded(void)
1310 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1311 if (game_status == game_status_last)
1315 // copy current draw buffer to later copy back areas that have not changed
1316 if (game_status_last != GAME_MODE_TITLE)
1317 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1319 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1320 if (CheckIfGlobalBorderRedrawIsNeeded())
1322 // determine and store new global border bitmap for current game status
1323 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1326 // redraw global screen border (or clear, if defined to be empty)
1327 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1329 if (game_status == GAME_MODE_EDITOR)
1330 DrawSpecialEditorDoor();
1332 // copy previous playfield and door areas, if they are defined on both
1333 // previous and current screen and if they still have the same size
1335 if (real_sx_last != -1 && real_sy_last != -1 &&
1336 REAL_SX != -1 && REAL_SY != -1 &&
1337 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1338 BlitBitmap(bitmap_db_store_1, backbuffer,
1339 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1342 if (dx_last != -1 && dy_last != -1 &&
1343 DX != -1 && DY != -1 &&
1344 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1345 BlitBitmap(bitmap_db_store_1, backbuffer,
1346 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1348 if (game_status != GAME_MODE_EDITOR)
1350 if (vx_last != -1 && vy_last != -1 &&
1351 VX != -1 && VY != -1 &&
1352 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1353 BlitBitmap(bitmap_db_store_1, backbuffer,
1354 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1358 if (ex_last != -1 && ey_last != -1 &&
1359 EX != -1 && EY != -1 &&
1360 exsize_last == EXSIZE && eysize_last == EYSIZE)
1361 BlitBitmap(bitmap_db_store_1, backbuffer,
1362 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1365 redraw_mask = REDRAW_ALL;
1368 game_status_last = game_status;
1370 global_border_bitmap_last = global_border_bitmap;
1372 real_sx_last = REAL_SX;
1373 real_sy_last = REAL_SY;
1374 full_sxsize_last = FULL_SXSIZE;
1375 full_sysize_last = FULL_SYSIZE;
1378 dxsize_last = DXSIZE;
1379 dysize_last = DYSIZE;
1382 vxsize_last = VXSIZE;
1383 vysize_last = VYSIZE;
1386 exsize_last = EXSIZE;
1387 eysize_last = EYSIZE;
1390 void ClearField(void)
1392 RedrawGlobalBorderIfNeeded();
1394 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1395 // (when entering hall of fame after playing)
1396 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1398 // !!! maybe this should be done before clearing the background !!!
1399 if (game_status == GAME_MODE_PLAYING)
1401 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1402 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1406 SetDrawtoField(DRAW_TO_BACKBUFFER);
1410 void MarkTileDirty(int x, int y)
1412 redraw_mask |= REDRAW_FIELD;
1415 void SetBorderElement(void)
1419 BorderElement = EL_EMPTY;
1421 // only the R'n'D game engine may use an additional steelwall border
1422 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1425 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1427 for (x = 0; x < lev_fieldx; x++)
1429 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1430 BorderElement = EL_STEELWALL;
1432 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1438 void FloodFillLevelExt(int start_x, int start_y, int fill_element,
1439 int max_array_fieldx, int max_array_fieldy,
1440 short field[max_array_fieldx][max_array_fieldy],
1441 int max_fieldx, int max_fieldy)
1443 static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY];
1444 struct XY *check = xy_topdown;
1445 int old_element = field[start_x][start_y];
1448 // do nothing if start field already has the desired content
1449 if (old_element == fill_element)
1452 stack_buffer[stack_pos++] = (struct XY){ start_x, start_y };
1454 while (stack_pos > 0)
1456 struct XY current = stack_buffer[--stack_pos];
1459 field[current.x][current.y] = fill_element;
1461 for (i = 0; i < 4; i++)
1463 int x = current.x + check[i].x;
1464 int y = current.y + check[i].y;
1466 // check for stack buffer overflow (should not happen)
1467 if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY)
1468 Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug.");
1470 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1471 stack_buffer[stack_pos++] = (struct XY){ x, y };
1476 void FloodFillLevel(int from_x, int from_y, int fill_element,
1477 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1478 int max_fieldx, int max_fieldy)
1480 FloodFillLevelExt(from_x, from_y, fill_element,
1481 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1482 max_fieldx, max_fieldy);
1485 void SetRandomAnimationValue(int x, int y)
1487 gfx.anim_random_frame = GfxRandom[x][y];
1490 int getGraphicAnimationFrame(int graphic, int sync_frame)
1492 // animation synchronized with global frame counter, not move position
1493 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1494 sync_frame = FrameCounter;
1495 else if (graphic_info[graphic].anim_global_anim_sync)
1496 sync_frame = getGlobalAnimSyncFrame();
1498 return getAnimationFrame(graphic_info[graphic].anim_frames,
1499 graphic_info[graphic].anim_delay,
1500 graphic_info[graphic].anim_mode,
1501 graphic_info[graphic].anim_start_frame,
1505 int getGraphicAnimationFrameXY(int graphic, int lx, int ly)
1507 if (graphic_info[graphic].anim_mode & ANIM_TILED)
1509 struct GraphicInfo *g = &graphic_info[graphic];
1510 int xsize = MAX(1, g->anim_frames_per_line);
1511 int ysize = MAX(1, g->anim_frames / xsize);
1512 int xoffset = g->anim_start_frame % xsize;
1513 int yoffset = g->anim_start_frame % ysize;
1514 // may be needed if screen field is significantly larger than playfield
1515 int x = (lx + xoffset + SCR_FIELDX * xsize) % xsize;
1516 int y = (ly + yoffset + SCR_FIELDY * ysize) % ysize;
1517 int sync_frame = y * xsize + x;
1519 return sync_frame % g->anim_frames;
1521 else if (graphic_info[graphic].anim_mode & ANIM_RANDOM_STATIC)
1523 struct GraphicInfo *g = &graphic_info[graphic];
1524 // may be needed if screen field is significantly larger than playfield
1525 int x = (lx + SCR_FIELDX * lev_fieldx) % lev_fieldx;
1526 int y = (ly + SCR_FIELDY * lev_fieldy) % lev_fieldy;
1527 int sync_frame = GfxRandomStatic[x][y];
1529 return sync_frame % g->anim_frames;
1533 int sync_frame = (IN_LEV_FIELD(lx, ly) ? GfxFrame[lx][ly] : -1);
1535 return getGraphicAnimationFrame(graphic, sync_frame);
1539 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1541 struct GraphicInfo *g = &graphic_info[graphic];
1542 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1544 if (tilesize == gfx.standard_tile_size)
1545 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1546 else if (tilesize == game.tile_size)
1547 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1549 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1552 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1553 boolean get_backside)
1555 struct GraphicInfo *g = &graphic_info[graphic];
1556 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1557 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1559 if (g->offset_y == 0) // frames are ordered horizontally
1561 int max_width = g->anim_frames_per_line * g->width;
1562 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1564 *x = pos % max_width;
1565 *y = src_y % g->height + pos / max_width * g->height;
1567 else if (g->offset_x == 0) // frames are ordered vertically
1569 int max_height = g->anim_frames_per_line * g->height;
1570 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1572 *x = src_x % g->width + pos / max_height * g->width;
1573 *y = pos % max_height;
1575 else // frames are ordered diagonally
1577 *x = src_x + frame * g->offset_x;
1578 *y = src_y + frame * g->offset_y;
1582 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1583 Bitmap **bitmap, int *x, int *y,
1584 boolean get_backside)
1586 struct GraphicInfo *g = &graphic_info[graphic];
1588 // if no graphics defined at all, use fallback graphics
1589 if (g->bitmaps == NULL)
1590 *g = graphic_info[IMG_CHAR_EXCLAM];
1592 // if no in-game graphics defined, always use standard graphic size
1593 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1594 tilesize = TILESIZE;
1596 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1597 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1599 *x = *x * tilesize / g->tile_size;
1600 *y = *y * tilesize / g->tile_size;
1603 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1604 Bitmap **bitmap, int *x, int *y)
1606 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1609 void getFixedGraphicSource(int graphic, int frame,
1610 Bitmap **bitmap, int *x, int *y)
1612 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1615 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1617 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1620 void getGlobalAnimGraphicSource(int graphic, int frame,
1621 Bitmap **bitmap, int *x, int *y)
1623 struct GraphicInfo *g = &graphic_info[graphic];
1625 // if no graphics defined at all, use fallback graphics
1626 if (g->bitmaps == NULL)
1627 *g = graphic_info[IMG_CHAR_EXCLAM];
1629 // use original size graphics, if existing, else use standard size graphics
1630 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1631 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1633 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1635 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1638 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1639 int *x, int *y, boolean get_backside)
1641 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1645 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1647 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1650 void DrawGraphic(int x, int y, int graphic, int frame)
1653 if (!IN_SCR_FIELD(x, y))
1655 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1656 Debug("draw:DrawGraphic", "This should never happen!");
1662 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1665 MarkTileDirty(x, y);
1668 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1671 if (!IN_SCR_FIELD(x, y))
1673 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1675 Debug("draw:DrawFixedGraphic", "This should never happen!");
1681 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1683 MarkTileDirty(x, y);
1686 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1692 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1694 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1697 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1703 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1704 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1707 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1710 if (!IN_SCR_FIELD(x, y))
1712 Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1714 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1720 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1723 MarkTileDirty(x, y);
1726 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1729 if (!IN_SCR_FIELD(x, y))
1731 Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1733 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1739 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1741 MarkTileDirty(x, y);
1744 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1750 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1752 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1756 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1757 int graphic, int frame)
1762 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1764 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1768 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1770 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1772 MarkTileDirty(x / tilesize, y / tilesize);
1775 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1778 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1779 graphic, frame, tilesize);
1780 MarkTileDirty(x / tilesize, y / tilesize);
1783 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1789 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1790 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1793 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1794 int frame, int tilesize)
1799 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1800 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1803 void DrawMiniGraphic(int x, int y, int graphic)
1805 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1806 MarkTileDirty(x / 2, y / 2);
1809 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1814 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1815 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1818 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1819 int graphic, int frame,
1820 int cut_mode, int mask_mode)
1825 int width = TILEX, height = TILEY;
1828 if (dx || dy) // shifted graphic
1830 if (x < BX1) // object enters playfield from the left
1837 else if (x > BX2) // object enters playfield from the right
1843 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1849 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1851 else if (dx) // general horizontal movement
1852 MarkTileDirty(x + SIGN(dx), y);
1854 if (y < BY1) // object enters playfield from the top
1856 if (cut_mode == CUT_BELOW) // object completely above top border
1864 else if (y > BY2) // object enters playfield from the bottom
1870 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1876 else if (dy > 0 && cut_mode == CUT_ABOVE)
1878 if (y == BY2) // object completely above bottom border
1884 MarkTileDirty(x, y + 1);
1885 } // object leaves playfield to the bottom
1886 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1888 else if (dy) // general vertical movement
1889 MarkTileDirty(x, y + SIGN(dy));
1893 if (!IN_SCR_FIELD(x, y))
1895 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1897 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1903 width = width * TILESIZE_VAR / TILESIZE;
1904 height = height * TILESIZE_VAR / TILESIZE;
1905 cx = cx * TILESIZE_VAR / TILESIZE;
1906 cy = cy * TILESIZE_VAR / TILESIZE;
1907 dx = dx * TILESIZE_VAR / TILESIZE;
1908 dy = dy * TILESIZE_VAR / TILESIZE;
1910 if (width > 0 && height > 0)
1912 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1917 dst_x = FX + x * TILEX_VAR + dx;
1918 dst_y = FY + y * TILEY_VAR + dy;
1920 if (mask_mode == USE_MASKING)
1921 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1924 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1927 MarkTileDirty(x, y);
1931 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1932 int graphic, int frame,
1933 int cut_mode, int mask_mode)
1938 int width = TILEX_VAR, height = TILEY_VAR;
1941 int x2 = x + SIGN(dx);
1942 int y2 = y + SIGN(dy);
1944 // movement with two-tile animations must be sync'ed with movement position,
1945 // not with current GfxFrame (which can be higher when using slow movement)
1946 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1947 int anim_frames = graphic_info[graphic].anim_frames;
1949 // (we also need anim_delay here for movement animations with less frames)
1950 int anim_delay = graphic_info[graphic].anim_delay;
1951 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1953 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1954 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1956 // re-calculate animation frame for two-tile movement animation
1957 frame = getGraphicAnimationFrame(graphic, sync_frame);
1959 // check if movement start graphic inside screen area and should be drawn
1960 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1962 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1964 dst_x = FX + x1 * TILEX_VAR;
1965 dst_y = FY + y1 * TILEY_VAR;
1967 if (mask_mode == USE_MASKING)
1968 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1971 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1974 MarkTileDirty(x1, y1);
1977 // check if movement end graphic inside screen area and should be drawn
1978 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1980 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1982 dst_x = FX + x2 * TILEX_VAR;
1983 dst_y = FY + y2 * TILEY_VAR;
1985 if (mask_mode == USE_MASKING)
1986 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1989 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1992 MarkTileDirty(x2, y2);
1996 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1997 int graphic, int frame,
1998 int cut_mode, int mask_mode)
2002 DrawGraphic(x, y, graphic, frame);
2007 if (graphic_info[graphic].double_movement) // EM style movement images
2008 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
2010 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
2013 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
2014 int graphic, int frame, int cut_mode)
2016 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
2019 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
2020 int cut_mode, int mask_mode)
2022 int lx = LEVELX(x), ly = LEVELY(y);
2026 if (IN_LEV_FIELD(lx, ly))
2028 if (element == EL_EMPTY)
2029 element = GfxElementEmpty[lx][ly];
2031 SetRandomAnimationValue(lx, ly);
2033 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2034 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2036 // do not use double (EM style) movement graphic when not moving
2037 if (graphic_info[graphic].double_movement && !dx && !dy)
2039 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2040 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2043 if (game.use_masked_elements && (dx || dy))
2044 mask_mode = USE_MASKING;
2046 else // border element
2048 graphic = el2img(element);
2049 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2052 if (element == EL_EXPANDABLE_WALL)
2054 boolean left_stopped = FALSE, right_stopped = FALSE;
2056 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2057 left_stopped = TRUE;
2058 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2059 right_stopped = TRUE;
2061 if (left_stopped && right_stopped)
2063 else if (left_stopped)
2065 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2066 frame = graphic_info[graphic].anim_frames - 1;
2068 else if (right_stopped)
2070 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2071 frame = graphic_info[graphic].anim_frames - 1;
2076 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2077 else if (mask_mode == USE_MASKING)
2078 DrawGraphicThruMask(x, y, graphic, frame);
2080 DrawGraphic(x, y, graphic, frame);
2083 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2084 int cut_mode, int mask_mode)
2086 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2087 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2088 cut_mode, mask_mode);
2091 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2094 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2097 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2100 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2103 void DrawLevelElementThruMask(int x, int y, int element)
2105 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2108 void DrawLevelFieldThruMask(int x, int y)
2110 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2113 // !!! implementation of quicksand is totally broken !!!
2114 #define IS_CRUMBLED_TILE(x, y, e) \
2115 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2116 !IS_MOVING(x, y) || \
2117 (e) == EL_QUICKSAND_EMPTYING || \
2118 (e) == EL_QUICKSAND_FAST_EMPTYING))
2120 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2125 int width, height, cx, cy;
2126 int sx = SCREENX(x), sy = SCREENY(y);
2127 int crumbled_border_size = graphic_info[graphic].border_size;
2128 int crumbled_tile_size = graphic_info[graphic].tile_size;
2129 int crumbled_border_size_var =
2130 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2133 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2135 for (i = 1; i < 4; i++)
2137 int dxx = (i & 1 ? dx : 0);
2138 int dyy = (i & 2 ? dy : 0);
2141 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2144 // check if neighbour field is of same crumble type
2145 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2146 graphic_info[graphic].class ==
2147 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2149 // return if check prevents inner corner
2150 if (same == (dxx == dx && dyy == dy))
2154 // if we reach this point, we have an inner corner
2156 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2158 width = crumbled_border_size_var;
2159 height = crumbled_border_size_var;
2160 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2161 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2163 if (game.use_masked_elements)
2165 int graphic0 = el2img(EL_EMPTY);
2166 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2167 Bitmap *src_bitmap0;
2170 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2172 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2174 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2176 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2178 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2181 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2183 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2186 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2191 int width, height, bx, by, cx, cy;
2192 int sx = SCREENX(x), sy = SCREENY(y);
2193 int crumbled_border_size = graphic_info[graphic].border_size;
2194 int crumbled_tile_size = graphic_info[graphic].tile_size;
2195 int crumbled_border_size_var =
2196 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2197 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2200 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2202 // only needed when using masked elements
2203 int graphic0 = el2img(EL_EMPTY);
2204 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2205 Bitmap *src_bitmap0;
2208 if (game.use_masked_elements)
2209 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2211 // draw simple, sloppy, non-corner-accurate crumbled border
2213 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2214 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2215 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2216 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2218 if (game.use_masked_elements)
2220 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2222 FX + sx * TILEX_VAR + cx,
2223 FY + sy * TILEY_VAR + cy);
2225 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2227 FX + sx * TILEX_VAR + cx,
2228 FY + sy * TILEY_VAR + cy);
2231 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2233 FX + sx * TILEX_VAR + cx,
2234 FY + sy * TILEY_VAR + cy);
2236 // (remaining middle border part must be at least as big as corner part)
2237 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2238 crumbled_border_size_var >= TILESIZE_VAR / 3)
2241 // correct corners of crumbled border, if needed
2243 for (i = -1; i <= 1; i += 2)
2245 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2246 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2247 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2250 // check if neighbour field is of same crumble type
2251 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2252 graphic_info[graphic].class ==
2253 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2255 // no crumbled corner, but continued crumbled border
2257 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2258 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2259 int b1 = (i == 1 ? crumbled_border_size_var :
2260 TILESIZE_VAR - 2 * crumbled_border_size_var);
2262 width = crumbled_border_size_var;
2263 height = crumbled_border_size_var;
2265 if (dir == 1 || dir == 2)
2280 if (game.use_masked_elements)
2282 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2284 FX + sx * TILEX_VAR + cx,
2285 FY + sy * TILEY_VAR + cy);
2287 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2289 FX + sx * TILEX_VAR + cx,
2290 FY + sy * TILEY_VAR + cy);
2293 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2295 FX + sx * TILEX_VAR + cx,
2296 FY + sy * TILEY_VAR + cy);
2301 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2303 int sx = SCREENX(x), sy = SCREENY(y);
2306 struct XY *xy = xy_topdown;
2308 if (!IN_LEV_FIELD(x, y))
2311 element = TILE_GFX_ELEMENT(x, y);
2313 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2315 if (!IN_SCR_FIELD(sx, sy))
2318 // crumble field borders towards direct neighbour fields
2319 for (i = 0; i < 4; i++)
2321 int xx = x + xy[i].x;
2322 int yy = y + xy[i].y;
2324 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2327 // check if neighbour field is of same crumble type
2328 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2329 graphic_info[graphic].class ==
2330 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2333 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2336 // crumble inner field corners towards corner neighbour fields
2337 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2338 graphic_info[graphic].anim_frames == 2)
2340 for (i = 0; i < 4; i++)
2342 int dx = (i & 1 ? +1 : -1);
2343 int dy = (i & 2 ? +1 : -1);
2345 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2349 MarkTileDirty(sx, sy);
2351 else // center field is not crumbled -- crumble neighbour fields
2353 // crumble field borders of direct neighbour fields
2354 for (i = 0; i < 4; i++)
2356 int xx = x + xy[i].x;
2357 int yy = y + xy[i].y;
2358 int sxx = sx + xy[i].x;
2359 int syy = sy + xy[i].y;
2361 if (!IN_LEV_FIELD(xx, yy) ||
2362 !IN_SCR_FIELD(sxx, syy))
2365 // do not crumble fields that are being digged or snapped
2366 if (Tile[xx][yy] == EL_EMPTY ||
2367 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2370 element = TILE_GFX_ELEMENT(xx, yy);
2372 if (!IS_CRUMBLED_TILE(xx, yy, element))
2375 graphic = el_act2crm(element, ACTION_DEFAULT);
2377 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2379 MarkTileDirty(sxx, syy);
2382 // crumble inner field corners of corner neighbour fields
2383 for (i = 0; i < 4; i++)
2385 int dx = (i & 1 ? +1 : -1);
2386 int dy = (i & 2 ? +1 : -1);
2392 if (!IN_LEV_FIELD(xx, yy) ||
2393 !IN_SCR_FIELD(sxx, syy))
2396 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2399 element = TILE_GFX_ELEMENT(xx, yy);
2401 if (!IS_CRUMBLED_TILE(xx, yy, element))
2404 graphic = el_act2crm(element, ACTION_DEFAULT);
2406 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2407 graphic_info[graphic].anim_frames == 2)
2408 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2410 MarkTileDirty(sxx, syy);
2415 void DrawLevelFieldCrumbled(int x, int y)
2419 if (!IN_LEV_FIELD(x, y))
2422 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2423 GfxElement[x][y] != EL_UNDEFINED &&
2424 GFX_CRUMBLED(GfxElement[x][y]))
2426 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2431 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2433 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2436 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2439 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2440 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2441 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2442 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2443 int sx = SCREENX(x), sy = SCREENY(y);
2445 DrawScreenGraphic(sx, sy, graphic1, frame1);
2446 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2449 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2451 int sx = SCREENX(x), sy = SCREENY(y);
2452 struct XY *xy = xy_topdown;
2455 // crumble direct neighbour fields (required for field borders)
2456 for (i = 0; i < 4; i++)
2458 int xx = x + xy[i].x;
2459 int yy = y + xy[i].y;
2460 int sxx = sx + xy[i].x;
2461 int syy = sy + xy[i].y;
2463 if (!IN_LEV_FIELD(xx, yy) ||
2464 !IN_SCR_FIELD(sxx, syy) ||
2465 !GFX_CRUMBLED(Tile[xx][yy]) ||
2469 DrawLevelField(xx, yy);
2472 // crumble corner neighbour fields (required for inner field corners)
2473 for (i = 0; i < 4; i++)
2475 int dx = (i & 1 ? +1 : -1);
2476 int dy = (i & 2 ? +1 : -1);
2482 if (!IN_LEV_FIELD(xx, yy) ||
2483 !IN_SCR_FIELD(sxx, syy) ||
2484 !GFX_CRUMBLED(Tile[xx][yy]) ||
2488 int element = TILE_GFX_ELEMENT(xx, yy);
2489 int graphic = el_act2crm(element, ACTION_DEFAULT);
2491 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2492 graphic_info[graphic].anim_frames == 2)
2493 DrawLevelField(xx, yy);
2497 static int getBorderElement(int x, int y)
2501 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2502 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2503 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2504 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2505 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2506 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2507 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2509 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2510 int steel_position = (x == -1 && y == -1 ? 0 :
2511 x == lev_fieldx && y == -1 ? 1 :
2512 x == -1 && y == lev_fieldy ? 2 :
2513 x == lev_fieldx && y == lev_fieldy ? 3 :
2514 x == -1 || x == lev_fieldx ? 4 :
2515 y == -1 || y == lev_fieldy ? 5 : 6);
2517 return border[steel_position][steel_type];
2520 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2522 if (game.use_masked_elements)
2524 if (graphic != el2img(EL_EMPTY))
2525 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2527 DrawGraphicThruMask(x, y, graphic, frame);
2531 DrawGraphic(x, y, graphic, frame);
2535 void DrawLevelGraphic(int x, int y, int graphic, int frame)
2537 DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2540 void DrawScreenElement(int x, int y, int element)
2542 int mask_mode = NO_MASKING;
2544 if (game.use_masked_elements)
2546 int lx = LEVELX(x), ly = LEVELY(y);
2548 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2550 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2552 mask_mode = USE_MASKING;
2556 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2557 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2560 void DrawLevelElement(int x, int y, int element)
2562 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2563 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2566 void DrawScreenField(int x, int y)
2568 int lx = LEVELX(x), ly = LEVELY(y);
2569 int element, content;
2571 if (!IN_LEV_FIELD(lx, ly))
2573 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2576 element = getBorderElement(lx, ly);
2578 DrawScreenElement(x, y, element);
2583 element = Tile[lx][ly];
2584 content = Store[lx][ly];
2586 if (IS_MOVING(lx, ly))
2588 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2589 boolean cut_mode = NO_CUTTING;
2591 if (element == EL_QUICKSAND_EMPTYING ||
2592 element == EL_QUICKSAND_FAST_EMPTYING ||
2593 element == EL_MAGIC_WALL_EMPTYING ||
2594 element == EL_BD_MAGIC_WALL_EMPTYING ||
2595 element == EL_DC_MAGIC_WALL_EMPTYING ||
2596 element == EL_AMOEBA_DROPPING)
2597 cut_mode = CUT_ABOVE;
2598 else if (element == EL_QUICKSAND_FILLING ||
2599 element == EL_QUICKSAND_FAST_FILLING ||
2600 element == EL_MAGIC_WALL_FILLING ||
2601 element == EL_BD_MAGIC_WALL_FILLING ||
2602 element == EL_DC_MAGIC_WALL_FILLING)
2603 cut_mode = CUT_BELOW;
2605 if (cut_mode == CUT_ABOVE)
2606 DrawScreenElement(x, y, element);
2608 DrawScreenElement(x, y, EL_EMPTY);
2610 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2612 int dir = MovDir[lx][ly];
2613 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2614 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2616 if (IN_SCR_FIELD(newx, newy))
2617 DrawScreenElement(newx, newy, EL_EMPTY);
2621 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2622 else if (cut_mode == NO_CUTTING)
2623 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2626 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2628 if (cut_mode == CUT_BELOW &&
2629 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2630 DrawLevelElement(lx, ly + 1, element);
2633 if (content == EL_ACID)
2635 int dir = MovDir[lx][ly];
2636 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2637 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2639 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2641 // prevent target field from being drawn again (but without masking)
2642 // (this would happen if target field is scanned after moving element)
2643 Stop[newlx][newly] = TRUE;
2646 else if (IS_BLOCKED(lx, ly))
2651 boolean cut_mode = NO_CUTTING;
2652 int element_old, content_old;
2654 Blocked2Moving(lx, ly, &oldx, &oldy);
2657 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2658 MovDir[oldx][oldy] == MV_RIGHT);
2660 element_old = Tile[oldx][oldy];
2661 content_old = Store[oldx][oldy];
2663 if (element_old == EL_QUICKSAND_EMPTYING ||
2664 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2665 element_old == EL_MAGIC_WALL_EMPTYING ||
2666 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2667 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2668 element_old == EL_AMOEBA_DROPPING)
2669 cut_mode = CUT_ABOVE;
2671 DrawScreenElement(x, y, EL_EMPTY);
2674 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2676 else if (cut_mode == NO_CUTTING)
2677 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2680 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2683 else if (IS_DRAWABLE(element))
2684 DrawScreenElement(x, y, element);
2686 DrawScreenElement(x, y, EL_EMPTY);
2689 void DrawLevelField(int x, int y)
2691 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2692 DrawScreenField(SCREENX(x), SCREENY(y));
2693 else if (IS_MOVING(x, y))
2697 Moving2Blocked(x, y, &newx, &newy);
2698 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2699 DrawScreenField(SCREENX(newx), SCREENY(newy));
2701 else if (IS_BLOCKED(x, y))
2705 Blocked2Moving(x, y, &oldx, &oldy);
2706 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2707 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2711 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2712 int (*el2img_function)(int), boolean masked,
2713 int element_bits_draw)
2715 int element_base = map_mm_wall_element(element);
2716 int element_bits = (IS_DF_WALL(element) ?
2717 element - EL_DF_WALL_START :
2718 IS_MM_WALL(element) ?
2719 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2720 int graphic = el2img_function(element_base);
2721 int tilesize_draw = tilesize / 2;
2726 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2728 for (i = 0; i < 4; i++)
2730 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2731 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2733 if (!(element_bits_draw & (1 << i)))
2736 if (element_bits & (1 << i))
2739 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2740 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2742 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2743 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2748 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2749 tilesize_draw, tilesize_draw);
2754 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2755 boolean masked, int element_bits_draw)
2757 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2758 element, tilesize, el2edimg, masked, element_bits_draw);
2761 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2762 int (*el2img_function)(int))
2764 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2768 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2771 if (IS_MM_WALL(element))
2773 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2774 element, tilesize, el2edimg, masked, 0x000f);
2778 int graphic = el2edimg(element);
2781 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2783 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2787 void DrawSizedElement(int x, int y, int element, int tilesize)
2789 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2792 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2794 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2797 void DrawMiniElement(int x, int y, int element)
2801 graphic = el2edimg(element);
2802 DrawMiniGraphic(x, y, graphic);
2805 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2808 int x = sx + scroll_x, y = sy + scroll_y;
2810 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2811 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2812 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2813 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2815 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2818 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2820 int x = sx + scroll_x, y = sy + scroll_y;
2822 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2823 DrawMiniElement(sx, sy, EL_EMPTY);
2824 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2825 DrawMiniElement(sx, sy, Tile[x][y]);
2827 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2830 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2831 int x, int y, int xsize, int ysize,
2832 int tile_width, int tile_height)
2836 int dst_x = startx + x * tile_width;
2837 int dst_y = starty + y * tile_height;
2838 int width = graphic_info[graphic].width;
2839 int height = graphic_info[graphic].height;
2840 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2841 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2842 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2843 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2844 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2845 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2846 boolean draw_masked = graphic_info[graphic].draw_masked;
2848 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2850 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2852 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2856 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2857 inner_sx + (x - 1) * tile_width % inner_width);
2858 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2859 inner_sy + (y - 1) * tile_height % inner_height);
2862 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2865 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2869 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2870 int x, int y, int xsize, int ysize,
2873 int font_width = getFontWidth(font_nr);
2874 int font_height = getFontHeight(font_nr);
2876 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2877 font_width, font_height);
2880 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2882 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2883 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2884 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2885 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2886 boolean no_delay = (tape.warp_forward);
2887 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2888 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2889 DelayCounter anim_delay = { anim_delay_value };
2890 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2891 int font_width = getFontWidth(font_nr);
2892 int font_height = getFontHeight(font_nr);
2893 int max_xsize = level.envelope[envelope_nr].xsize;
2894 int max_ysize = level.envelope[envelope_nr].ysize;
2895 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2896 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2897 int xend = max_xsize;
2898 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2899 int xstep = (xstart < xend ? 1 : 0);
2900 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2902 int end = MAX(xend - xstart, yend - ystart);
2905 for (i = start; i <= end; i++)
2907 int last_frame = end; // last frame of this "for" loop
2908 int x = xstart + i * xstep;
2909 int y = ystart + i * ystep;
2910 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2911 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2912 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2913 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2916 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2918 BlitScreenToBitmap(backbuffer);
2920 SetDrawtoField(DRAW_TO_BACKBUFFER);
2922 for (yy = 0; yy < ysize; yy++)
2923 for (xx = 0; xx < xsize; xx++)
2924 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2926 DrawTextArea(sx + font_width, sy + font_height,
2927 level.envelope[envelope_nr].text, font_nr, max_xsize,
2928 xsize - 2, ysize - 2, 0, mask_mode,
2929 level.envelope[envelope_nr].autowrap,
2930 level.envelope[envelope_nr].centered, FALSE);
2932 redraw_mask |= REDRAW_FIELD;
2935 SkipUntilDelayReached(&anim_delay, &i, last_frame);
2938 ClearAutoRepeatKeyEvents();
2941 void ShowEnvelope(int envelope_nr)
2943 int element = EL_ENVELOPE_1 + envelope_nr;
2944 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2945 int sound_opening = element_info[element].sound[ACTION_OPENING];
2946 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2947 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2948 boolean no_delay = (tape.warp_forward);
2949 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2950 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2951 int anim_mode = graphic_info[graphic].anim_mode;
2952 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2953 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2954 boolean overlay_enabled = GetOverlayEnabled();
2956 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2958 SetOverlayEnabled(FALSE);
2961 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2963 if (anim_mode == ANIM_DEFAULT)
2964 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2966 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2969 Delay_WithScreenUpdates(wait_delay_value);
2971 WaitForEventToContinue();
2974 SetOverlayEnabled(overlay_enabled);
2976 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2978 if (anim_mode != ANIM_NONE)
2979 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2981 if (anim_mode == ANIM_DEFAULT)
2982 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2984 game.envelope_active = FALSE;
2986 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2988 redraw_mask |= REDRAW_FIELD;
2992 void ShowEnvelope_MM(int envelope_nr)
2994 BlitBitmap(backbuffer, bitmap_db_field, REAL_SX, REAL_SY,
2995 FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
2997 ShowEnvelope(envelope_nr);
2999 SetDrawtoField(DRAW_TO_BACKBUFFER);
3002 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
3003 int xsize, int ysize)
3005 if (!global.use_envelope_request ||
3006 request.sort_priority <= 0)
3009 if (request.bitmap == NULL ||
3010 xsize > request.xsize ||
3011 ysize > request.ysize)
3013 if (request.bitmap != NULL)
3014 FreeBitmap(request.bitmap);
3016 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
3018 SDL_Surface *surface = request.bitmap->surface;
3020 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
3021 Fail("SDLGetNativeSurface() failed");
3024 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3026 SDLFreeBitmapTextures(request.bitmap);
3027 SDLCreateBitmapTextures(request.bitmap);
3029 // set envelope request run-time values
3032 request.xsize = xsize;
3033 request.ysize = ysize;
3036 void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage)
3038 if (global.use_envelope_request &&
3039 game.request_active_or_moving &&
3040 request.sort_priority > 0 &&
3041 drawing_target == DRAW_TO_SCREEN &&
3042 drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
3044 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3045 request.sx, request.sy);
3049 static void setRequestBasePosition(int *x, int *y)
3051 int sx_base, sy_base;
3053 if (request.x != -1)
3054 sx_base = request.x;
3055 else if (request.align == ALIGN_LEFT)
3057 else if (request.align == ALIGN_RIGHT)
3058 sx_base = SX + SXSIZE;
3060 sx_base = SX + SXSIZE / 2;
3062 if (request.y != -1)
3063 sy_base = request.y;
3064 else if (request.valign == VALIGN_TOP)
3066 else if (request.valign == VALIGN_BOTTOM)
3067 sy_base = SY + SYSIZE;
3069 sy_base = SY + SYSIZE / 2;
3075 static void setRequestPositionExt(int *x, int *y, int width, int height,
3076 boolean add_border_size)
3078 int border_size = request.border_size;
3079 int sx_base, sy_base;
3082 setRequestBasePosition(&sx_base, &sy_base);
3084 if (request.align == ALIGN_LEFT)
3086 else if (request.align == ALIGN_RIGHT)
3087 sx = sx_base - width;
3089 sx = sx_base - width / 2;
3091 if (request.valign == VALIGN_TOP)
3093 else if (request.valign == VALIGN_BOTTOM)
3094 sy = sy_base - height;
3096 sy = sy_base - height / 2;
3098 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3099 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3101 if (add_border_size)
3111 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3113 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3116 static void DrawEnvelopeRequest(char *text)
3118 char *text_final = text;
3119 char *text_door_style = NULL;
3120 int graphic = IMG_BACKGROUND_REQUEST;
3121 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3122 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3123 int font_nr = FONT_REQUEST;
3124 int font_width = getFontWidth(font_nr);
3125 int font_height = getFontHeight(font_nr);
3126 int border_size = request.border_size;
3127 int line_spacing = request.line_spacing;
3128 int line_height = font_height + line_spacing;
3129 int max_text_width = request.width - 2 * border_size;
3130 int max_text_height = request.height - 2 * border_size;
3131 int line_length = max_text_width / font_width;
3132 int max_lines = max_text_height / line_height;
3133 int text_width = line_length * font_width;
3134 int width = request.width;
3135 int height = request.height;
3136 int tile_size = MAX(request.step_offset, 1);
3137 int x_steps = width / tile_size;
3138 int y_steps = height / tile_size;
3139 int sx_offset = border_size;
3140 int sy_offset = border_size;
3144 if (request.centered)
3145 sx_offset = (request.width - text_width) / 2;
3147 if (request.wrap_single_words && !request.autowrap)
3149 char *src_text_ptr, *dst_text_ptr;
3151 text_door_style = checked_malloc(2 * strlen(text) + 1);
3153 src_text_ptr = text;
3154 dst_text_ptr = text_door_style;
3156 while (*src_text_ptr)
3158 if (*src_text_ptr == ' ' ||
3159 *src_text_ptr == '?' ||
3160 *src_text_ptr == '!')
3161 *dst_text_ptr++ = '\n';
3163 if (*src_text_ptr != ' ')
3164 *dst_text_ptr++ = *src_text_ptr;
3169 *dst_text_ptr = '\0';
3171 text_final = text_door_style;
3174 setRequestPosition(&sx, &sy, FALSE);
3176 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3178 for (y = 0; y < y_steps; y++)
3179 for (x = 0; x < x_steps; x++)
3180 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3181 x, y, x_steps, y_steps,
3182 tile_size, tile_size);
3184 // force DOOR font inside door area
3185 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3187 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3188 line_length, -1, max_lines, line_spacing, mask_mode,
3189 request.autowrap, request.centered, FALSE);
3193 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3194 RedrawGadget(tool_gadget[i]);
3196 // store readily prepared envelope request for later use when animating
3197 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3199 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3201 if (text_door_style)
3202 free(text_door_style);
3205 static void AnimateEnvelopeRequest(int anim_mode, int action)
3207 int graphic = IMG_BACKGROUND_REQUEST;
3208 boolean draw_masked = graphic_info[graphic].draw_masked;
3209 int delay_value_normal = request.step_delay;
3210 int delay_value_fast = delay_value_normal / 2;
3211 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3212 boolean no_delay = (tape.warp_forward);
3213 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3214 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3215 DelayCounter anim_delay = { anim_delay_value };
3217 int tile_size = MAX(request.step_offset, 1);
3218 int max_xsize = request.width / tile_size;
3219 int max_ysize = request.height / tile_size;
3220 int max_xsize_inner = max_xsize - 2;
3221 int max_ysize_inner = max_ysize - 2;
3223 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3224 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3225 int xend = max_xsize_inner;
3226 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3227 int xstep = (xstart < xend ? 1 : 0);
3228 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3230 int end = MAX(xend - xstart, yend - ystart);
3233 if (setup.quick_doors)
3240 for (i = start; i <= end; i++)
3242 int last_frame = end; // last frame of this "for" loop
3243 int x = xstart + i * xstep;
3244 int y = ystart + i * ystep;
3245 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3246 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3247 int xsize_size_left = (xsize - 1) * tile_size;
3248 int ysize_size_top = (ysize - 1) * tile_size;
3249 int max_xsize_pos = (max_xsize - 1) * tile_size;
3250 int max_ysize_pos = (max_ysize - 1) * tile_size;
3251 int width = xsize * tile_size;
3252 int height = ysize * tile_size;
3257 setRequestPosition(&src_x, &src_y, FALSE);
3258 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3260 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3262 for (yy = 0; yy < 2; yy++)
3264 for (xx = 0; xx < 2; xx++)
3266 int src_xx = src_x + xx * max_xsize_pos;
3267 int src_yy = src_y + yy * max_ysize_pos;
3268 int dst_xx = dst_x + xx * xsize_size_left;
3269 int dst_yy = dst_y + yy * ysize_size_top;
3270 int xx_size = (xx ? tile_size : xsize_size_left);
3271 int yy_size = (yy ? tile_size : ysize_size_top);
3274 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3275 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3277 BlitBitmap(bitmap_db_store_2, backbuffer,
3278 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3282 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3284 redraw_mask |= REDRAW_FIELD;
3288 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3291 ClearAutoRepeatKeyEvents();
3294 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3296 int graphic = IMG_BACKGROUND_REQUEST;
3297 int sound_opening = SND_REQUEST_OPENING;
3298 int sound_closing = SND_REQUEST_CLOSING;
3299 int anim_mode_1 = request.anim_mode; // (higher priority)
3300 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3301 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3302 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3303 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3305 if (game_status == GAME_MODE_PLAYING)
3306 BlitScreenToBitmap(backbuffer);
3308 SetDrawtoField(DRAW_TO_BACKBUFFER);
3310 // SetDrawBackgroundMask(REDRAW_NONE);
3312 if (action == ACTION_OPENING)
3314 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3316 if (req_state & REQ_ASK)
3318 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3319 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3320 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3321 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3323 else if (req_state & REQ_CONFIRM)
3325 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3326 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3328 else if (req_state & REQ_PLAYER)
3330 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3331 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3332 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3333 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3336 DrawEnvelopeRequest(text);
3339 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3341 if (action == ACTION_OPENING)
3343 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3345 if (anim_mode == ANIM_DEFAULT)
3346 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3348 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3352 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3354 if (anim_mode != ANIM_NONE)
3355 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3357 if (anim_mode == ANIM_DEFAULT)
3358 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3361 game.envelope_active = FALSE;
3363 if (action == ACTION_CLOSING)
3364 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3366 // SetDrawBackgroundMask(last_draw_background_mask);
3368 redraw_mask |= REDRAW_FIELD;
3372 if (action == ACTION_CLOSING &&
3373 game_status == GAME_MODE_PLAYING &&
3374 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3375 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3378 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3380 if (IS_MM_WALL(element))
3382 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3388 int graphic = el2preimg(element);
3390 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3391 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3396 void DrawLevel(int draw_background_mask)
3400 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3401 SetDrawBackgroundMask(draw_background_mask);
3405 for (x = BX1; x <= BX2; x++)
3406 for (y = BY1; y <= BY2; y++)
3407 DrawScreenField(x, y);
3409 redraw_mask |= REDRAW_FIELD;
3412 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3417 for (x = 0; x < size_x; x++)
3418 for (y = 0; y < size_y; y++)
3419 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3421 redraw_mask |= REDRAW_FIELD;
3424 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3428 for (x = 0; x < size_x; x++)
3429 for (y = 0; y < size_y; y++)
3430 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3432 redraw_mask |= REDRAW_FIELD;
3435 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3437 boolean show_level_border = (BorderElement != EL_EMPTY);
3438 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3439 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3440 int tile_size = preview.tile_size;
3441 int preview_width = preview.xsize * tile_size;
3442 int preview_height = preview.ysize * tile_size;
3443 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3444 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3445 int real_preview_width = real_preview_xsize * tile_size;
3446 int real_preview_height = real_preview_ysize * tile_size;
3447 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3448 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3451 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3454 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3456 dst_x += (preview_width - real_preview_width) / 2;
3457 dst_y += (preview_height - real_preview_height) / 2;
3459 for (x = 0; x < real_preview_xsize; x++)
3461 for (y = 0; y < real_preview_ysize; y++)
3463 int lx = from_x + x + (show_level_border ? -1 : 0);
3464 int ly = from_y + y + (show_level_border ? -1 : 0);
3465 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3466 getBorderElement(lx, ly));
3468 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3469 element, tile_size);
3473 redraw_mask |= REDRAW_FIELD;
3476 #define MICROLABEL_EMPTY 0
3477 #define MICROLABEL_LEVEL_NAME 1
3478 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3479 #define MICROLABEL_LEVEL_AUTHOR 3
3480 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3481 #define MICROLABEL_IMPORTED_FROM 5
3482 #define MICROLABEL_IMPORTED_BY_HEAD 6
3483 #define MICROLABEL_IMPORTED_BY 7
3485 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3487 int max_text_width = SXSIZE;
3488 int font_width = getFontWidth(font_nr);
3490 if (pos->align == ALIGN_CENTER)
3491 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3492 else if (pos->align == ALIGN_RIGHT)
3493 max_text_width = pos->x;
3495 max_text_width = SXSIZE - pos->x;
3497 return max_text_width / font_width;
3500 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3502 char label_text[MAX_OUTPUT_LINESIZE + 1];
3503 int max_len_label_text;
3504 int font_nr = pos->font;
3507 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3510 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3511 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3512 mode == MICROLABEL_IMPORTED_BY_HEAD)
3513 font_nr = pos->font_alt;
3515 max_len_label_text = getMaxTextLength(pos, font_nr);
3517 if (pos->size != -1)
3518 max_len_label_text = pos->size;
3520 for (i = 0; i < max_len_label_text; i++)
3521 label_text[i] = ' ';
3522 label_text[max_len_label_text] = '\0';
3524 if (strlen(label_text) > 0)
3525 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3528 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3529 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3530 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3531 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3532 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3533 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3534 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3535 max_len_label_text);
3536 label_text[max_len_label_text] = '\0';
3538 if (strlen(label_text) > 0)
3539 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3541 redraw_mask |= REDRAW_FIELD;
3544 static void DrawPreviewLevelLabel(int mode)
3546 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3549 static void DrawPreviewLevelInfo(int mode)
3551 if (mode == MICROLABEL_LEVEL_NAME)
3552 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3553 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3554 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3557 static void DrawPreviewLevelExt(boolean restart)
3559 static DelayCounter scroll_delay = { 0 };
3560 static DelayCounter label_delay = { 0 };
3561 static int from_x, from_y, scroll_direction;
3562 static int label_state, label_counter;
3563 boolean show_level_border = (BorderElement != EL_EMPTY);
3564 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3565 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3567 scroll_delay.value = preview.step_delay;
3568 label_delay.value = MICROLEVEL_LABEL_DELAY;
3575 if (preview.anim_mode == ANIM_CENTERED)
3577 if (level_xsize > preview.xsize)
3578 from_x = (level_xsize - preview.xsize) / 2;
3579 if (level_ysize > preview.ysize)
3580 from_y = (level_ysize - preview.ysize) / 2;
3583 from_x += preview.xoffset;
3584 from_y += preview.yoffset;
3586 scroll_direction = MV_RIGHT;
3590 DrawPreviewLevelPlayfield(from_x, from_y);
3591 DrawPreviewLevelLabel(label_state);
3593 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3594 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3596 // initialize delay counters
3597 ResetDelayCounter(&scroll_delay);
3598 ResetDelayCounter(&label_delay);
3600 if (leveldir_current->name)
3602 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3603 char label_text[MAX_OUTPUT_LINESIZE + 1];
3604 int font_nr = pos->font;
3605 int max_len_label_text = getMaxTextLength(pos, font_nr);
3607 if (pos->size != -1)
3608 max_len_label_text = pos->size;
3610 strncpy(label_text, leveldir_current->name, max_len_label_text);
3611 label_text[max_len_label_text] = '\0';
3613 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3614 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3620 // scroll preview level, if needed
3621 if (preview.anim_mode != ANIM_NONE &&
3622 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3623 DelayReached(&scroll_delay))
3625 switch (scroll_direction)
3630 from_x -= preview.step_offset;
3631 from_x = (from_x < 0 ? 0 : from_x);
3634 scroll_direction = MV_UP;
3638 if (from_x < level_xsize - preview.xsize)
3640 from_x += preview.step_offset;
3641 from_x = (from_x > level_xsize - preview.xsize ?
3642 level_xsize - preview.xsize : from_x);
3645 scroll_direction = MV_DOWN;
3651 from_y -= preview.step_offset;
3652 from_y = (from_y < 0 ? 0 : from_y);
3655 scroll_direction = MV_RIGHT;
3659 if (from_y < level_ysize - preview.ysize)
3661 from_y += preview.step_offset;
3662 from_y = (from_y > level_ysize - preview.ysize ?
3663 level_ysize - preview.ysize : from_y);
3666 scroll_direction = MV_LEFT;
3673 DrawPreviewLevelPlayfield(from_x, from_y);
3676 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3677 // redraw micro level label, if needed
3678 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3679 !strEqual(level.author, ANONYMOUS_NAME) &&
3680 !strEqual(level.author, leveldir_current->name) &&
3681 DelayReached(&label_delay))
3683 int max_label_counter = 23;
3685 if (leveldir_current->imported_from != NULL &&
3686 strlen(leveldir_current->imported_from) > 0)
3687 max_label_counter += 14;
3688 if (leveldir_current->imported_by != NULL &&
3689 strlen(leveldir_current->imported_by) > 0)
3690 max_label_counter += 14;
3692 label_counter = (label_counter + 1) % max_label_counter;
3693 label_state = (label_counter >= 0 && label_counter <= 7 ?
3694 MICROLABEL_LEVEL_NAME :
3695 label_counter >= 9 && label_counter <= 12 ?
3696 MICROLABEL_LEVEL_AUTHOR_HEAD :
3697 label_counter >= 14 && label_counter <= 21 ?
3698 MICROLABEL_LEVEL_AUTHOR :
3699 label_counter >= 23 && label_counter <= 26 ?
3700 MICROLABEL_IMPORTED_FROM_HEAD :
3701 label_counter >= 28 && label_counter <= 35 ?
3702 MICROLABEL_IMPORTED_FROM :
3703 label_counter >= 37 && label_counter <= 40 ?
3704 MICROLABEL_IMPORTED_BY_HEAD :
3705 label_counter >= 42 && label_counter <= 49 ?
3706 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3708 if (leveldir_current->imported_from == NULL &&
3709 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3710 label_state == MICROLABEL_IMPORTED_FROM))
3711 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3712 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3714 DrawPreviewLevelLabel(label_state);
3718 void DrawPreviewPlayers(void)
3720 if (game_status != GAME_MODE_MAIN)
3723 // do not draw preview players if level preview redefined, but players aren't
3724 if (preview.redefined && !menu.main.preview_players.redefined)
3727 boolean player_found[MAX_PLAYERS];
3728 int num_players = 0;
3731 for (i = 0; i < MAX_PLAYERS; i++)
3732 player_found[i] = FALSE;
3734 // check which players can be found in the level (simple approach)
3735 for (x = 0; x < lev_fieldx; x++)
3737 for (y = 0; y < lev_fieldy; y++)
3739 int element = level.field[x][y];
3741 if (IS_PLAYER_ELEMENT(element))
3743 int player_nr = GET_PLAYER_NR(element);
3745 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3747 if (!player_found[player_nr])
3750 player_found[player_nr] = TRUE;
3755 struct TextPosInfo *pos = &menu.main.preview_players;
3756 int tile_size = pos->tile_size;
3757 int border_size = pos->border_size;
3758 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3759 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3760 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3761 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3762 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3763 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3764 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3765 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3766 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3767 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3768 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3769 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3771 // clear area in which the players will be drawn
3772 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3773 max_players_width, max_players_height);
3775 if (!network.enabled && !setup.team_mode)
3778 // only draw players if level is suited for team mode
3779 if (num_players < 2)
3782 // draw all players that were found in the level
3783 for (i = 0; i < MAX_PLAYERS; i++)
3785 if (player_found[i])
3787 int graphic = el2img(EL_PLAYER_1 + i);
3789 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3791 xpos += player_xoffset;
3792 ypos += player_yoffset;
3797 void DrawPreviewLevelInitial(void)
3799 DrawPreviewLevelExt(TRUE);
3800 DrawPreviewPlayers();
3803 void DrawPreviewLevelAnimation(void)
3805 DrawPreviewLevelExt(FALSE);
3808 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3809 int border_size, int font_nr)
3811 int graphic = el2img(EL_PLAYER_1 + player_nr);
3812 int font_height = getFontHeight(font_nr);
3813 int player_height = MAX(tile_size, font_height);
3814 int xoffset_text = tile_size + border_size;
3815 int yoffset_text = (player_height - font_height) / 2;
3816 int yoffset_graphic = (player_height - tile_size) / 2;
3817 char *player_name = getNetworkPlayerName(player_nr + 1);
3819 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3821 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3824 static void DrawNetworkPlayersExt(boolean force)
3826 if (game_status != GAME_MODE_MAIN)
3829 if (!network.connected && !force)
3832 // do not draw network players if level preview redefined, but players aren't
3833 if (preview.redefined && !menu.main.network_players.redefined)
3836 int num_players = 0;
3839 for (i = 0; i < MAX_PLAYERS; i++)
3840 if (stored_player[i].connected_network)
3843 struct TextPosInfo *pos = &menu.main.network_players;
3844 int tile_size = pos->tile_size;
3845 int border_size = pos->border_size;
3846 int xoffset_text = tile_size + border_size;
3847 int font_nr = pos->font;
3848 int font_width = getFontWidth(font_nr);
3849 int font_height = getFontHeight(font_nr);
3850 int player_height = MAX(tile_size, font_height);
3851 int player_yoffset = player_height + border_size;
3852 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3853 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3854 int all_players_height = num_players * player_yoffset - border_size;
3855 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3856 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3857 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3859 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3860 max_players_width, max_players_height);
3862 // first draw local network player ...
3863 for (i = 0; i < MAX_PLAYERS; i++)
3865 if (stored_player[i].connected_network &&
3866 stored_player[i].connected_locally)
3868 char *player_name = getNetworkPlayerName(i + 1);
3869 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3870 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3872 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3874 ypos += player_yoffset;
3878 // ... then draw all other network players
3879 for (i = 0; i < MAX_PLAYERS; i++)
3881 if (stored_player[i].connected_network &&
3882 !stored_player[i].connected_locally)
3884 char *player_name = getNetworkPlayerName(i + 1);
3885 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3886 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3888 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3890 ypos += player_yoffset;
3895 void DrawNetworkPlayers(void)
3897 DrawNetworkPlayersExt(FALSE);
3900 void ClearNetworkPlayers(void)
3902 DrawNetworkPlayersExt(TRUE);
3905 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3906 int graphic, int lx, int ly,
3909 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3911 if (mask_mode == USE_MASKING)
3912 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3914 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3917 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3918 int graphic, int sync_frame, int mask_mode)
3920 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3922 if (mask_mode == USE_MASKING)
3923 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3925 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3928 static void DrawGraphicAnimation(int x, int y, int graphic)
3930 int lx = LEVELX(x), ly = LEVELY(y);
3931 int mask_mode = NO_MASKING;
3933 if (!IN_SCR_FIELD(x, y))
3936 if (game.use_masked_elements)
3938 if (Tile[lx][ly] != EL_EMPTY)
3940 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3942 mask_mode = USE_MASKING;
3946 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3947 graphic, lx, ly, mask_mode);
3949 MarkTileDirty(x, y);
3952 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3954 int lx = LEVELX(x), ly = LEVELY(y);
3955 int mask_mode = NO_MASKING;
3957 if (!IN_SCR_FIELD(x, y))
3960 if (game.use_masked_elements)
3962 if (Tile[lx][ly] != EL_EMPTY)
3964 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3966 mask_mode = USE_MASKING;
3970 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3971 graphic, lx, ly, mask_mode);
3973 MarkTileDirty(x, y);
3976 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3978 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3981 void DrawLevelElementAnimation(int x, int y, int element)
3983 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3985 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3988 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3990 int sx = SCREENX(x), sy = SCREENY(y);
3992 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3995 if (Tile[x][y] == EL_EMPTY)
3996 graphic = el2img(GfxElementEmpty[x][y]);
3998 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4001 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4004 DrawGraphicAnimation(sx, sy, graphic);
4007 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4008 DrawLevelFieldCrumbled(x, y);
4010 if (GFX_CRUMBLED(Tile[x][y]))
4011 DrawLevelFieldCrumbled(x, y);
4015 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4017 int sx = SCREENX(x), sy = SCREENY(y);
4020 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4023 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4025 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4028 DrawGraphicAnimation(sx, sy, graphic);
4030 if (GFX_CRUMBLED(element))
4031 DrawLevelFieldCrumbled(x, y);
4034 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4036 if (player->use_murphy)
4038 // this works only because currently only one player can be "murphy" ...
4039 static int last_horizontal_dir = MV_LEFT;
4040 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4042 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4043 last_horizontal_dir = move_dir;
4045 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4047 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4049 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4055 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
4058 static boolean equalGraphics(int graphic1, int graphic2)
4060 struct GraphicInfo *g1 = &graphic_info[graphic1];
4061 struct GraphicInfo *g2 = &graphic_info[graphic2];
4063 return (g1->bitmap == g2->bitmap &&
4064 g1->src_x == g2->src_x &&
4065 g1->src_y == g2->src_y &&
4066 g1->anim_frames == g2->anim_frames &&
4067 g1->anim_delay == g2->anim_delay &&
4068 g1->anim_mode == g2->anim_mode);
4071 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4075 DRAW_PLAYER_STAGE_INIT = 0,
4076 DRAW_PLAYER_STAGE_LAST_FIELD,
4077 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4078 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4079 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4080 DRAW_PLAYER_STAGE_PLAYER,
4082 DRAW_PLAYER_STAGE_PLAYER,
4083 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4085 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4086 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4088 NUM_DRAW_PLAYER_STAGES
4091 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4093 static int static_last_player_graphic[MAX_PLAYERS];
4094 static int static_last_player_frame[MAX_PLAYERS];
4095 static boolean static_player_is_opaque[MAX_PLAYERS];
4096 static boolean draw_player[MAX_PLAYERS];
4097 int pnr = player->index_nr;
4099 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4101 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4102 static_last_player_frame[pnr] = player->Frame;
4103 static_player_is_opaque[pnr] = FALSE;
4105 draw_player[pnr] = TRUE;
4108 if (!draw_player[pnr])
4112 if (!IN_LEV_FIELD(player->jx, player->jy))
4114 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4115 Debug("draw:DrawPlayerExt", "This should never happen!");
4117 draw_player[pnr] = FALSE;
4123 int last_player_graphic = static_last_player_graphic[pnr];
4124 int last_player_frame = static_last_player_frame[pnr];
4125 boolean player_is_opaque = static_player_is_opaque[pnr];
4127 int jx = player->jx;
4128 int jy = player->jy;
4129 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4130 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4131 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4132 int last_jx = (player->is_moving ? jx - dx : jx);
4133 int last_jy = (player->is_moving ? jy - dy : jy);
4134 int next_jx = jx + dx;
4135 int next_jy = jy + dy;
4136 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4137 int sx = SCREENX(jx);
4138 int sy = SCREENY(jy);
4139 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4140 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4141 int element = Tile[jx][jy];
4142 int last_element = Tile[last_jx][last_jy];
4143 int action = (player->is_pushing ? ACTION_PUSHING :
4144 player->is_digging ? ACTION_DIGGING :
4145 player->is_collecting ? ACTION_COLLECTING :
4146 player->is_moving ? ACTION_MOVING :
4147 player->is_snapping ? ACTION_SNAPPING :
4148 player->is_dropping ? ACTION_DROPPING :
4149 player->is_waiting ? player->action_waiting :
4152 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4154 // ------------------------------------------------------------------------
4155 // initialize drawing the player
4156 // ------------------------------------------------------------------------
4158 draw_player[pnr] = FALSE;
4160 // GfxElement[][] is set to the element the player is digging or collecting;
4161 // remove also for off-screen player if the player is not moving anymore
4162 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4163 GfxElement[jx][jy] = EL_UNDEFINED;
4165 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4168 if (element == EL_EXPLOSION)
4171 InitPlayerGfxAnimation(player, action, move_dir);
4173 draw_player[pnr] = TRUE;
4175 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4177 // ------------------------------------------------------------------------
4178 // draw things in the field the player is leaving, if needed
4179 // ------------------------------------------------------------------------
4181 if (!IN_SCR_FIELD(sx, sy))
4182 draw_player[pnr] = FALSE;
4184 if (!player->is_moving)
4187 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4189 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4191 if (last_element == EL_DYNAMITE_ACTIVE ||
4192 last_element == EL_EM_DYNAMITE_ACTIVE ||
4193 last_element == EL_SP_DISK_RED_ACTIVE)
4194 DrawDynamite(last_jx, last_jy);
4196 DrawLevelFieldThruMask(last_jx, last_jy);
4198 else if (last_element == EL_DYNAMITE_ACTIVE ||
4199 last_element == EL_EM_DYNAMITE_ACTIVE ||
4200 last_element == EL_SP_DISK_RED_ACTIVE)
4201 DrawDynamite(last_jx, last_jy);
4203 DrawLevelField(last_jx, last_jy);
4205 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4207 // ------------------------------------------------------------------------
4208 // draw things behind the player, if needed
4209 // ------------------------------------------------------------------------
4213 DrawLevelElement(jx, jy, Back[jx][jy]);
4218 if (IS_ACTIVE_BOMB(element))
4220 DrawLevelElement(jx, jy, EL_EMPTY);
4225 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4227 int old_element = GfxElement[jx][jy];
4228 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4229 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4231 if (GFX_CRUMBLED(old_element))
4232 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4234 DrawScreenGraphic(sx, sy, old_graphic, frame);
4236 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4237 static_player_is_opaque[pnr] = TRUE;
4241 GfxElement[jx][jy] = EL_UNDEFINED;
4243 // make sure that pushed elements are drawn with correct frame rate
4244 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4246 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4247 GfxFrame[jx][jy] = player->StepFrame;
4249 DrawLevelField(jx, jy);
4252 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4254 // ------------------------------------------------------------------------
4255 // draw things the player is pushing, if needed
4256 // ------------------------------------------------------------------------
4258 if (!player->is_pushing || !player->is_moving)
4261 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4264 int gfx_frame = GfxFrame[jx][jy];
4266 if (!IS_MOVING(jx, jy)) // push movement already finished
4268 element = Tile[next_jx][next_jy];
4269 gfx_frame = GfxFrame[next_jx][next_jy];
4272 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4273 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4274 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4276 // draw background element under pushed element (like the Sokoban field)
4277 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4279 // this allows transparent pushing animation over non-black background
4282 DrawLevelElement(jx, jy, Back[jx][jy]);
4284 DrawLevelElement(jx, jy, EL_EMPTY);
4287 if (Back[next_jx][next_jy])
4288 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4290 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4292 int px = SCREENX(jx), py = SCREENY(jy);
4293 int pxx = (TILEX - ABS(sxx)) * dx;
4294 int pyy = (TILEY - ABS(syy)) * dy;
4297 // do not draw (EM style) pushing animation when pushing is finished
4298 // (two-tile animations usually do not contain start and end frame)
4299 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4300 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4302 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4304 // masked drawing is needed for EMC style (double) movement graphics
4305 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4306 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4309 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4311 // ------------------------------------------------------------------------
4312 // draw player himself
4313 // ------------------------------------------------------------------------
4315 int graphic = getPlayerGraphic(player, move_dir);
4317 // in the case of changed player action or direction, prevent the current
4318 // animation frame from being restarted for identical animations
4319 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4320 player->Frame = last_player_frame;
4322 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4324 if (player_is_opaque)
4325 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4327 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4329 if (SHIELD_ON(player))
4331 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4332 IMG_SHIELD_NORMAL_ACTIVE);
4333 frame = getGraphicAnimationFrame(graphic, -1);
4335 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4338 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4340 // ------------------------------------------------------------------------
4341 // draw things in front of player (active dynamite or dynabombs)
4342 // ------------------------------------------------------------------------
4344 if (IS_ACTIVE_BOMB(element))
4346 int graphic = el2img(element);
4347 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4349 if (game.emulation == EMU_SUPAPLEX)
4350 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4352 DrawGraphicThruMask(sx, sy, graphic, frame);
4355 if (player_is_moving && last_element == EL_EXPLOSION)
4357 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4358 GfxElement[last_jx][last_jy] : EL_EMPTY);
4359 int graphic = el_act2img(element, ACTION_EXPLODING);
4360 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4361 int phase = ExplodePhase[last_jx][last_jy] - 1;
4362 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4365 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4368 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4370 // ------------------------------------------------------------------------
4371 // draw elements the player is just walking/passing through/under
4372 // ------------------------------------------------------------------------
4374 if (player_is_moving)
4376 // handle the field the player is leaving ...
4377 if (IS_ACCESSIBLE_INSIDE(last_element))
4378 DrawLevelField(last_jx, last_jy);
4379 else if (IS_ACCESSIBLE_UNDER(last_element))
4380 DrawLevelFieldThruMask(last_jx, last_jy);
4383 // do not redraw accessible elements if the player is just pushing them
4384 if (!player_is_moving || !player->is_pushing)
4386 // ... and the field the player is entering
4387 if (IS_ACCESSIBLE_INSIDE(element))
4388 DrawLevelField(jx, jy);
4389 else if (IS_ACCESSIBLE_UNDER(element))
4390 DrawLevelFieldThruMask(jx, jy);
4393 MarkTileDirty(sx, sy);
4397 void DrawPlayer(struct PlayerInfo *player)
4401 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4402 DrawPlayerExt(player, i);
4405 void DrawAllPlayers(void)
4409 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4410 for (j = 0; j < MAX_PLAYERS; j++)
4411 if (stored_player[j].active)
4412 DrawPlayerExt(&stored_player[j], i);
4415 void DrawPlayerField(int x, int y)
4417 if (!IS_PLAYER(x, y))
4420 DrawPlayer(PLAYERINFO(x, y));
4423 // ----------------------------------------------------------------------------
4425 void WaitForEventToContinue(void)
4427 boolean first_wait = TRUE;
4428 boolean still_wait = TRUE;
4430 if (program.headless)
4433 // simulate releasing mouse button over last gadget, if still pressed
4435 HandleGadgets(-1, -1, 0);
4437 button_status = MB_RELEASED;
4440 ClearPlayerAction();
4446 if (NextValidEvent(&event))
4450 case EVENT_BUTTONPRESS:
4451 case EVENT_FINGERPRESS:
4455 case EVENT_BUTTONRELEASE:
4456 case EVENT_FINGERRELEASE:
4457 still_wait = first_wait;
4460 case EVENT_KEYPRESS:
4461 case SDL_CONTROLLERBUTTONDOWN:
4462 case SDL_JOYBUTTONDOWN:
4467 HandleOtherEvents(&event);
4471 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4476 if (!PendingEvent())
4481 #define MAX_REQUEST_LINES 13
4482 #define MAX_REQUEST_LINE_FONT1_LEN 7
4483 #define MAX_REQUEST_LINE_FONT2_LEN 10
4485 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4487 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4489 int draw_buffer_last = GetDrawtoField();
4490 int width = request.width;
4491 int height = request.height;
4495 // when showing request dialog after game ended, deactivate game panel
4496 if (game_just_ended)
4497 game.panel.active = FALSE;
4499 game.request_active = TRUE;
4501 setRequestPosition(&sx, &sy, FALSE);
4503 button_status = MB_RELEASED;
4505 request_gadget_id = -1;
4510 boolean event_handled = FALSE;
4512 if (game_just_ended)
4514 SetDrawtoField(draw_buffer_game);
4516 HandleGameActions();
4518 SetDrawtoField(DRAW_TO_BACKBUFFER);
4520 if (global.use_envelope_request)
4522 // copy current state of request area to middle of playfield area
4523 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4531 while (NextValidEvent(&event))
4533 event_handled = TRUE;
4537 case EVENT_BUTTONPRESS:
4538 case EVENT_BUTTONRELEASE:
4539 case EVENT_MOTIONNOTIFY:
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 // this sets 'request_gadget_id'
4564 HandleGadgets(mx, my, button_status);
4566 switch (request_gadget_id)
4568 case TOOL_CTRL_ID_YES:
4569 case TOOL_CTRL_ID_TOUCH_YES:
4572 case TOOL_CTRL_ID_NO:
4573 case TOOL_CTRL_ID_TOUCH_NO:
4576 case TOOL_CTRL_ID_CONFIRM:
4577 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4578 result = TRUE | FALSE;
4581 case TOOL_CTRL_ID_PLAYER_1:
4584 case TOOL_CTRL_ID_PLAYER_2:
4587 case TOOL_CTRL_ID_PLAYER_3:
4590 case TOOL_CTRL_ID_PLAYER_4:
4595 // only check clickable animations if no request gadget clicked
4596 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4603 case SDL_WINDOWEVENT:
4604 HandleWindowEvent((WindowEvent *) &event);
4607 case SDL_APP_WILLENTERBACKGROUND:
4608 case SDL_APP_DIDENTERBACKGROUND:
4609 case SDL_APP_WILLENTERFOREGROUND:
4610 case SDL_APP_DIDENTERFOREGROUND:
4611 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4614 case EVENT_KEYPRESS:
4616 Key key = GetEventKey((KeyEvent *)&event);
4621 if (req_state & REQ_CONFIRM)
4630 #if defined(KSYM_Rewind)
4631 case KSYM_Rewind: // for Amazon Fire TV remote
4640 #if defined(KSYM_FastForward)
4641 case KSYM_FastForward: // for Amazon Fire TV remote
4647 HandleKeysDebug(key, KEY_PRESSED);
4651 if (req_state & REQ_PLAYER)
4653 int old_player_nr = setup.network_player_nr;
4656 result = old_player_nr + 1;
4661 result = old_player_nr + 1;
4692 case EVENT_FINGERRELEASE:
4693 case EVENT_KEYRELEASE:
4694 ClearPlayerAction();
4697 case SDL_CONTROLLERBUTTONDOWN:
4698 switch (event.cbutton.button)
4700 case SDL_CONTROLLER_BUTTON_A:
4701 case SDL_CONTROLLER_BUTTON_X:
4702 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4703 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4707 case SDL_CONTROLLER_BUTTON_B:
4708 case SDL_CONTROLLER_BUTTON_Y:
4709 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4710 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4711 case SDL_CONTROLLER_BUTTON_BACK:
4716 if (req_state & REQ_PLAYER)
4718 int old_player_nr = setup.network_player_nr;
4721 result = old_player_nr + 1;
4723 switch (event.cbutton.button)
4725 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4726 case SDL_CONTROLLER_BUTTON_Y:
4730 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4731 case SDL_CONTROLLER_BUTTON_B:
4735 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4736 case SDL_CONTROLLER_BUTTON_A:
4740 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4741 case SDL_CONTROLLER_BUTTON_X:
4752 case SDL_CONTROLLERBUTTONUP:
4753 HandleJoystickEvent(&event);
4754 ClearPlayerAction();
4758 HandleOtherEvents(&event);
4763 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4765 int joy = AnyJoystick();
4767 if (joy & JOY_BUTTON_1)
4769 else if (joy & JOY_BUTTON_2)
4772 else if (AnyJoystick())
4774 int joy = AnyJoystick();
4776 if (req_state & REQ_PLAYER)
4780 else if (joy & JOY_RIGHT)
4782 else if (joy & JOY_DOWN)
4784 else if (joy & JOY_LEFT)
4791 if (game_just_ended)
4793 if (global.use_envelope_request)
4795 // copy back current state of pressed buttons inside request area
4796 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4800 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
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_CHAR_START &&
8210 element_rnd <= EL_CHAR_END ?
8211 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8213 element_rnd >= EL_MM_RUNTIME_START &&
8214 element_rnd <= EL_MM_RUNTIME_END ?
8215 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8217 EL_MM_EMPTY_NATIVE);
8220 int map_element_MM_to_RND(int element_mm)
8222 return (element_mm == EL_MM_EMPTY_NATIVE ||
8223 element_mm == EL_DF_EMPTY_NATIVE ?
8226 element_mm >= EL_MM_START_1_NATIVE &&
8227 element_mm <= EL_MM_END_1_NATIVE ?
8228 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8230 element_mm >= EL_MM_START_2_NATIVE &&
8231 element_mm <= EL_MM_END_2_NATIVE ?
8232 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8234 element_mm >= EL_MM_CHAR_START_NATIVE &&
8235 element_mm <= EL_MM_CHAR_END_NATIVE ?
8236 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8238 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8239 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8240 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8245 int map_action_MM_to_RND(int action_mm)
8247 // all MM actions are defined to exactly match their RND counterparts
8251 int map_sound_MM_to_RND(int sound_mm)
8255 case SND_MM_GAME_LEVELTIME_CHARGING:
8256 return SND_GAME_LEVELTIME_CHARGING;
8258 case SND_MM_GAME_HEALTH_CHARGING:
8259 return SND_GAME_HEALTH_CHARGING;
8262 return SND_UNDEFINED;
8266 int map_mm_wall_element(int element)
8268 return (element >= EL_MM_STEEL_WALL_START &&
8269 element <= EL_MM_STEEL_WALL_END ?
8272 element >= EL_MM_WOODEN_WALL_START &&
8273 element <= EL_MM_WOODEN_WALL_END ?
8276 element >= EL_MM_ICE_WALL_START &&
8277 element <= EL_MM_ICE_WALL_END ?
8280 element >= EL_MM_AMOEBA_WALL_START &&
8281 element <= EL_MM_AMOEBA_WALL_END ?
8284 element >= EL_DF_STEEL_WALL_START &&
8285 element <= EL_DF_STEEL_WALL_END ?
8288 element >= EL_DF_WOODEN_WALL_START &&
8289 element <= EL_DF_WOODEN_WALL_END ?
8295 int map_mm_wall_element_editor(int element)
8299 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8300 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8301 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8302 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8303 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8304 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8306 default: return element;
8310 int get_next_element(int element)
8314 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8315 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8316 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8317 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8318 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8319 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8320 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8321 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8322 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8323 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8324 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8326 default: return element;
8330 int el2img_mm(int element_mm)
8332 return el2img(map_element_MM_to_RND(element_mm));
8335 int el_act2img_mm(int element_mm, int action)
8337 return el_act2img(map_element_MM_to_RND(element_mm), action);
8340 int el_act_dir2img(int element, int action, int direction)
8342 element = GFX_ELEMENT(element);
8343 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8345 // direction_graphic[][] == graphic[] for undefined direction graphics
8346 return element_info[element].direction_graphic[action][direction];
8349 static int el_act_dir2crm(int element, int action, int direction)
8351 element = GFX_ELEMENT(element);
8352 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8354 // direction_graphic[][] == graphic[] for undefined direction graphics
8355 return element_info[element].direction_crumbled[action][direction];
8358 int el_act2img(int element, int action)
8360 element = GFX_ELEMENT(element);
8362 return element_info[element].graphic[action];
8365 int el_act2crm(int element, int action)
8367 element = GFX_ELEMENT(element);
8369 return element_info[element].crumbled[action];
8372 int el_dir2img(int element, int direction)
8374 element = GFX_ELEMENT(element);
8376 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8379 int el2baseimg(int element)
8381 return element_info[element].graphic[ACTION_DEFAULT];
8384 int el2img(int element)
8386 element = GFX_ELEMENT(element);
8388 return element_info[element].graphic[ACTION_DEFAULT];
8391 int el2edimg(int element)
8393 element = GFX_ELEMENT(element);
8395 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8398 int el2preimg(int element)
8400 element = GFX_ELEMENT(element);
8402 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8405 int el2panelimg(int element)
8407 element = GFX_ELEMENT(element);
8409 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8412 int font2baseimg(int font_nr)
8414 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8417 int getBeltNrFromBeltElement(int element)
8419 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8420 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8421 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8424 int getBeltNrFromBeltActiveElement(int element)
8426 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8427 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8428 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8431 int getBeltNrFromBeltSwitchElement(int element)
8433 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8434 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8435 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8438 int getBeltDirNrFromBeltElement(int element)
8440 static int belt_base_element[4] =
8442 EL_CONVEYOR_BELT_1_LEFT,
8443 EL_CONVEYOR_BELT_2_LEFT,
8444 EL_CONVEYOR_BELT_3_LEFT,
8445 EL_CONVEYOR_BELT_4_LEFT
8448 int belt_nr = getBeltNrFromBeltElement(element);
8449 int belt_dir_nr = element - belt_base_element[belt_nr];
8451 return (belt_dir_nr % 3);
8454 int getBeltDirNrFromBeltSwitchElement(int element)
8456 static int belt_base_element[4] =
8458 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8459 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8460 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8461 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8464 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8465 int belt_dir_nr = element - belt_base_element[belt_nr];
8467 return (belt_dir_nr % 3);
8470 int getBeltDirFromBeltElement(int element)
8472 static int belt_move_dir[3] =
8479 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8481 return belt_move_dir[belt_dir_nr];
8484 int getBeltDirFromBeltSwitchElement(int element)
8486 static int belt_move_dir[3] =
8493 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8495 return belt_move_dir[belt_dir_nr];
8498 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8500 static int belt_base_element[4] =
8502 EL_CONVEYOR_BELT_1_LEFT,
8503 EL_CONVEYOR_BELT_2_LEFT,
8504 EL_CONVEYOR_BELT_3_LEFT,
8505 EL_CONVEYOR_BELT_4_LEFT
8508 return belt_base_element[belt_nr] + belt_dir_nr;
8511 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8513 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8515 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8518 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8520 static int belt_base_element[4] =
8522 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8523 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8524 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8525 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8528 return belt_base_element[belt_nr] + belt_dir_nr;
8531 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8533 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8535 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8538 boolean swapTiles_EM(boolean is_pre_emc_cave)
8540 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8543 boolean getTeamMode_EM(void)
8545 return game.team_mode || network_playing;
8548 boolean isActivePlayer_EM(int player_nr)
8550 return stored_player[player_nr].active;
8553 unsigned int InitRND(int seed)
8555 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8556 return InitEngineRandom_EM(seed);
8557 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8558 return InitEngineRandom_SP(seed);
8559 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8560 return InitEngineRandom_MM(seed);
8562 return InitEngineRandom_RND(seed);
8565 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8566 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8568 static int get_effective_element_EM(int tile, int frame_em)
8570 int element = object_mapping[tile].element_rnd;
8571 int action = object_mapping[tile].action;
8572 boolean is_backside = object_mapping[tile].is_backside;
8573 boolean action_removing = (action == ACTION_DIGGING ||
8574 action == ACTION_SNAPPING ||
8575 action == ACTION_COLLECTING);
8583 return (frame_em > 5 ? EL_EMPTY : element);
8589 else // frame_em == 7
8600 case Ydiamond_stone:
8604 case Xdrip_stretchB:
8620 case Ymagnify_blank:
8623 case Xsand_stonein_1:
8624 case Xsand_stonein_2:
8625 case Xsand_stonein_3:
8626 case Xsand_stonein_4:
8630 return (is_backside || action_removing ? EL_EMPTY : element);
8635 static boolean check_linear_animation_EM(int tile)
8639 case Xsand_stonesand_1:
8640 case Xsand_stonesand_quickout_1:
8641 case Xsand_sandstone_1:
8642 case Xsand_stonein_1:
8643 case Xsand_stoneout_1:
8671 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8672 boolean has_crumbled_graphics,
8673 int crumbled, int sync_frame)
8675 // if element can be crumbled, but certain action graphics are just empty
8676 // space (like instantly snapping sand to empty space in 1 frame), do not
8677 // treat these empty space graphics as crumbled graphics in EMC engine
8678 if (crumbled == IMG_EMPTY_SPACE)
8679 has_crumbled_graphics = FALSE;
8681 if (has_crumbled_graphics)
8683 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8684 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8685 g_crumbled->anim_delay,
8686 g_crumbled->anim_mode,
8687 g_crumbled->anim_start_frame,
8690 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8691 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8693 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8694 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8696 g_em->has_crumbled_graphics = TRUE;
8700 g_em->crumbled_bitmap = NULL;
8701 g_em->crumbled_src_x = 0;
8702 g_em->crumbled_src_y = 0;
8703 g_em->crumbled_border_size = 0;
8704 g_em->crumbled_tile_size = 0;
8706 g_em->has_crumbled_graphics = FALSE;
8711 void ResetGfxAnimation_EM(int x, int y, int tile)
8717 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8718 int tile, int frame_em, int x, int y)
8720 int action = object_mapping[tile].action;
8721 int direction = object_mapping[tile].direction;
8722 int effective_element = get_effective_element_EM(tile, frame_em);
8723 int graphic = (direction == MV_NONE ?
8724 el_act2img(effective_element, action) :
8725 el_act_dir2img(effective_element, action, direction));
8726 struct GraphicInfo *g = &graphic_info[graphic];
8728 boolean action_removing = (action == ACTION_DIGGING ||
8729 action == ACTION_SNAPPING ||
8730 action == ACTION_COLLECTING);
8731 boolean action_moving = (action == ACTION_FALLING ||
8732 action == ACTION_MOVING ||
8733 action == ACTION_PUSHING ||
8734 action == ACTION_EATING ||
8735 action == ACTION_FILLING ||
8736 action == ACTION_EMPTYING);
8737 boolean action_falling = (action == ACTION_FALLING ||
8738 action == ACTION_FILLING ||
8739 action == ACTION_EMPTYING);
8741 // special case: graphic uses "2nd movement tile" and has defined
8742 // 7 frames for movement animation (or less) => use default graphic
8743 // for last (8th) frame which ends the movement animation
8744 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8746 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8747 graphic = (direction == MV_NONE ?
8748 el_act2img(effective_element, action) :
8749 el_act_dir2img(effective_element, action, direction));
8751 g = &graphic_info[graphic];
8754 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8758 else if (action_moving)
8760 boolean is_backside = object_mapping[tile].is_backside;
8764 int direction = object_mapping[tile].direction;
8765 int move_dir = (action_falling ? MV_DOWN : direction);
8770 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8771 if (g->double_movement && frame_em == 0)
8775 if (move_dir == MV_LEFT)
8776 GfxFrame[x - 1][y] = GfxFrame[x][y];
8777 else if (move_dir == MV_RIGHT)
8778 GfxFrame[x + 1][y] = GfxFrame[x][y];
8779 else if (move_dir == MV_UP)
8780 GfxFrame[x][y - 1] = GfxFrame[x][y];
8781 else if (move_dir == MV_DOWN)
8782 GfxFrame[x][y + 1] = GfxFrame[x][y];
8789 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8790 if (tile == Xsand_stonesand_quickout_1 ||
8791 tile == Xsand_stonesand_quickout_2)
8795 if (graphic_info[graphic].anim_global_sync)
8796 sync_frame = FrameCounter;
8797 else if (graphic_info[graphic].anim_global_anim_sync)
8798 sync_frame = getGlobalAnimSyncFrame();
8799 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8800 sync_frame = GfxFrame[x][y];
8802 sync_frame = 0; // playfield border (pseudo steel)
8804 SetRandomAnimationValue(x, y);
8806 int frame = getAnimationFrame(g->anim_frames,
8809 g->anim_start_frame,
8812 g_em->unique_identifier =
8813 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8816 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8817 int tile, int frame_em, int x, int y)
8819 int action = object_mapping[tile].action;
8820 int direction = object_mapping[tile].direction;
8821 boolean is_backside = object_mapping[tile].is_backside;
8822 int effective_element = get_effective_element_EM(tile, frame_em);
8823 int effective_action = action;
8824 int graphic = (direction == MV_NONE ?
8825 el_act2img(effective_element, effective_action) :
8826 el_act_dir2img(effective_element, effective_action,
8828 int crumbled = (direction == MV_NONE ?
8829 el_act2crm(effective_element, effective_action) :
8830 el_act_dir2crm(effective_element, effective_action,
8832 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8833 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8834 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8835 struct GraphicInfo *g = &graphic_info[graphic];
8838 // special case: graphic uses "2nd movement tile" and has defined
8839 // 7 frames for movement animation (or less) => use default graphic
8840 // for last (8th) frame which ends the movement animation
8841 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8843 effective_action = ACTION_DEFAULT;
8844 graphic = (direction == MV_NONE ?
8845 el_act2img(effective_element, effective_action) :
8846 el_act_dir2img(effective_element, effective_action,
8848 crumbled = (direction == MV_NONE ?
8849 el_act2crm(effective_element, effective_action) :
8850 el_act_dir2crm(effective_element, effective_action,
8853 g = &graphic_info[graphic];
8856 if (graphic_info[graphic].anim_global_sync)
8857 sync_frame = FrameCounter;
8858 else if (graphic_info[graphic].anim_global_anim_sync)
8859 sync_frame = getGlobalAnimSyncFrame();
8860 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8861 sync_frame = GfxFrame[x][y];
8863 sync_frame = 0; // playfield border (pseudo steel)
8865 SetRandomAnimationValue(x, y);
8867 int frame = getAnimationFrame(g->anim_frames,
8870 g->anim_start_frame,
8873 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8874 g->double_movement && is_backside);
8876 // (updating the "crumbled" graphic definitions is probably not really needed,
8877 // as animations for crumbled graphics can't be longer than one EMC cycle)
8878 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8882 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8883 int player_nr, int anim, int frame_em)
8885 int element = player_mapping[player_nr][anim].element_rnd;
8886 int action = player_mapping[player_nr][anim].action;
8887 int direction = player_mapping[player_nr][anim].direction;
8888 int graphic = (direction == MV_NONE ?
8889 el_act2img(element, action) :
8890 el_act_dir2img(element, action, direction));
8891 struct GraphicInfo *g = &graphic_info[graphic];
8894 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8896 stored_player[player_nr].StepFrame = frame_em;
8898 sync_frame = stored_player[player_nr].Frame;
8900 int frame = getAnimationFrame(g->anim_frames,
8903 g->anim_start_frame,
8906 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8907 &g_em->src_x, &g_em->src_y, FALSE);
8910 void InitGraphicInfo_EM(void)
8914 // always start with reliable default values
8915 for (i = 0; i < GAME_TILE_MAX; i++)
8917 object_mapping[i].element_rnd = EL_UNKNOWN;
8918 object_mapping[i].is_backside = FALSE;
8919 object_mapping[i].action = ACTION_DEFAULT;
8920 object_mapping[i].direction = MV_NONE;
8923 // always start with reliable default values
8924 for (p = 0; p < MAX_PLAYERS; p++)
8926 for (i = 0; i < PLY_MAX; i++)
8928 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8929 player_mapping[p][i].action = ACTION_DEFAULT;
8930 player_mapping[p][i].direction = MV_NONE;
8934 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8936 int e = em_object_mapping_list[i].element_em;
8938 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8939 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8941 if (em_object_mapping_list[i].action != -1)
8942 object_mapping[e].action = em_object_mapping_list[i].action;
8944 if (em_object_mapping_list[i].direction != -1)
8945 object_mapping[e].direction =
8946 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8949 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8951 int a = em_player_mapping_list[i].action_em;
8952 int p = em_player_mapping_list[i].player_nr;
8954 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8956 if (em_player_mapping_list[i].action != -1)
8957 player_mapping[p][a].action = em_player_mapping_list[i].action;
8959 if (em_player_mapping_list[i].direction != -1)
8960 player_mapping[p][a].direction =
8961 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8964 for (i = 0; i < GAME_TILE_MAX; i++)
8966 int element = object_mapping[i].element_rnd;
8967 int action = object_mapping[i].action;
8968 int direction = object_mapping[i].direction;
8969 boolean is_backside = object_mapping[i].is_backside;
8970 boolean action_exploding = ((action == ACTION_EXPLODING ||
8971 action == ACTION_SMASHED_BY_ROCK ||
8972 action == ACTION_SMASHED_BY_SPRING) &&
8973 element != EL_DIAMOND);
8974 boolean action_active = (action == ACTION_ACTIVE);
8975 boolean action_other = (action == ACTION_OTHER);
8977 for (j = 0; j < 8; j++)
8979 int effective_element = get_effective_element_EM(i, j);
8980 int effective_action = (j < 7 ? action :
8981 i == Xdrip_stretch ? action :
8982 i == Xdrip_stretchB ? action :
8983 i == Ydrip_1_s ? action :
8984 i == Ydrip_1_sB ? action :
8985 i == Yball_1 ? action :
8986 i == Xball_2 ? action :
8987 i == Yball_2 ? action :
8988 i == Yball_blank ? action :
8989 i == Ykey_1_blank ? action :
8990 i == Ykey_2_blank ? action :
8991 i == Ykey_3_blank ? action :
8992 i == Ykey_4_blank ? action :
8993 i == Ykey_5_blank ? action :
8994 i == Ykey_6_blank ? action :
8995 i == Ykey_7_blank ? action :
8996 i == Ykey_8_blank ? action :
8997 i == Ylenses_blank ? action :
8998 i == Ymagnify_blank ? action :
8999 i == Ygrass_blank ? action :
9000 i == Ydirt_blank ? action :
9001 i == Xsand_stonein_1 ? action :
9002 i == Xsand_stonein_2 ? action :
9003 i == Xsand_stonein_3 ? action :
9004 i == Xsand_stonein_4 ? action :
9005 i == Xsand_stoneout_1 ? action :
9006 i == Xsand_stoneout_2 ? action :
9007 i == Xboom_android ? ACTION_EXPLODING :
9008 action_exploding ? ACTION_EXPLODING :
9009 action_active ? action :
9010 action_other ? action :
9012 int graphic = (el_act_dir2img(effective_element, effective_action,
9014 int crumbled = (el_act_dir2crm(effective_element, effective_action,
9016 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
9017 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
9018 boolean has_action_graphics = (graphic != base_graphic);
9019 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9020 struct GraphicInfo *g = &graphic_info[graphic];
9021 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9024 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9025 boolean special_animation = (action != ACTION_DEFAULT &&
9026 g->anim_frames == 3 &&
9027 g->anim_delay == 2 &&
9028 g->anim_mode & ANIM_LINEAR);
9029 int sync_frame = (i == Xdrip_stretch ? 7 :
9030 i == Xdrip_stretchB ? 7 :
9031 i == Ydrip_2_s ? j + 8 :
9032 i == Ydrip_2_sB ? j + 8 :
9041 i == Xfake_acid_1 ? 0 :
9042 i == Xfake_acid_2 ? 10 :
9043 i == Xfake_acid_3 ? 20 :
9044 i == Xfake_acid_4 ? 30 :
9045 i == Xfake_acid_5 ? 40 :
9046 i == Xfake_acid_6 ? 50 :
9047 i == Xfake_acid_7 ? 60 :
9048 i == Xfake_acid_8 ? 70 :
9049 i == Xfake_acid_1_player ? 0 :
9050 i == Xfake_acid_2_player ? 10 :
9051 i == Xfake_acid_3_player ? 20 :
9052 i == Xfake_acid_4_player ? 30 :
9053 i == Xfake_acid_5_player ? 40 :
9054 i == Xfake_acid_6_player ? 50 :
9055 i == Xfake_acid_7_player ? 60 :
9056 i == Xfake_acid_8_player ? 70 :
9058 i == Yball_2 ? j + 8 :
9059 i == Yball_blank ? j + 1 :
9060 i == Ykey_1_blank ? j + 1 :
9061 i == Ykey_2_blank ? j + 1 :
9062 i == Ykey_3_blank ? j + 1 :
9063 i == Ykey_4_blank ? j + 1 :
9064 i == Ykey_5_blank ? j + 1 :
9065 i == Ykey_6_blank ? j + 1 :
9066 i == Ykey_7_blank ? j + 1 :
9067 i == Ykey_8_blank ? j + 1 :
9068 i == Ylenses_blank ? j + 1 :
9069 i == Ymagnify_blank ? j + 1 :
9070 i == Ygrass_blank ? j + 1 :
9071 i == Ydirt_blank ? j + 1 :
9072 i == Xamoeba_1 ? 0 :
9073 i == Xamoeba_2 ? 1 :
9074 i == Xamoeba_3 ? 2 :
9075 i == Xamoeba_4 ? 3 :
9076 i == Xamoeba_5 ? 0 :
9077 i == Xamoeba_6 ? 1 :
9078 i == Xamoeba_7 ? 2 :
9079 i == Xamoeba_8 ? 3 :
9080 i == Xexit_2 ? j + 8 :
9081 i == Xexit_3 ? j + 16 :
9082 i == Xdynamite_1 ? 0 :
9083 i == Xdynamite_2 ? 8 :
9084 i == Xdynamite_3 ? 16 :
9085 i == Xdynamite_4 ? 24 :
9086 i == Xsand_stonein_1 ? j + 1 :
9087 i == Xsand_stonein_2 ? j + 9 :
9088 i == Xsand_stonein_3 ? j + 17 :
9089 i == Xsand_stonein_4 ? j + 25 :
9090 i == Xsand_stoneout_1 && j == 0 ? 0 :
9091 i == Xsand_stoneout_1 && j == 1 ? 0 :
9092 i == Xsand_stoneout_1 && j == 2 ? 1 :
9093 i == Xsand_stoneout_1 && j == 3 ? 2 :
9094 i == Xsand_stoneout_1 && j == 4 ? 2 :
9095 i == Xsand_stoneout_1 && j == 5 ? 3 :
9096 i == Xsand_stoneout_1 && j == 6 ? 4 :
9097 i == Xsand_stoneout_1 && j == 7 ? 4 :
9098 i == Xsand_stoneout_2 && j == 0 ? 5 :
9099 i == Xsand_stoneout_2 && j == 1 ? 6 :
9100 i == Xsand_stoneout_2 && j == 2 ? 7 :
9101 i == Xsand_stoneout_2 && j == 3 ? 8 :
9102 i == Xsand_stoneout_2 && j == 4 ? 9 :
9103 i == Xsand_stoneout_2 && j == 5 ? 11 :
9104 i == Xsand_stoneout_2 && j == 6 ? 13 :
9105 i == Xsand_stoneout_2 && j == 7 ? 15 :
9106 i == Xboom_bug && j == 1 ? 2 :
9107 i == Xboom_bug && j == 2 ? 2 :
9108 i == Xboom_bug && j == 3 ? 4 :
9109 i == Xboom_bug && j == 4 ? 4 :
9110 i == Xboom_bug && j == 5 ? 2 :
9111 i == Xboom_bug && j == 6 ? 2 :
9112 i == Xboom_bug && j == 7 ? 0 :
9113 i == Xboom_tank && j == 1 ? 2 :
9114 i == Xboom_tank && j == 2 ? 2 :
9115 i == Xboom_tank && j == 3 ? 4 :
9116 i == Xboom_tank && j == 4 ? 4 :
9117 i == Xboom_tank && j == 5 ? 2 :
9118 i == Xboom_tank && j == 6 ? 2 :
9119 i == Xboom_tank && j == 7 ? 0 :
9120 i == Xboom_android && j == 7 ? 6 :
9121 i == Xboom_1 && j == 1 ? 2 :
9122 i == Xboom_1 && j == 2 ? 2 :
9123 i == Xboom_1 && j == 3 ? 4 :
9124 i == Xboom_1 && j == 4 ? 4 :
9125 i == Xboom_1 && j == 5 ? 6 :
9126 i == Xboom_1 && j == 6 ? 6 :
9127 i == Xboom_1 && j == 7 ? 8 :
9128 i == Xboom_2 && j == 0 ? 8 :
9129 i == Xboom_2 && j == 1 ? 8 :
9130 i == Xboom_2 && j == 2 ? 10 :
9131 i == Xboom_2 && j == 3 ? 10 :
9132 i == Xboom_2 && j == 4 ? 10 :
9133 i == Xboom_2 && j == 5 ? 12 :
9134 i == Xboom_2 && j == 6 ? 12 :
9135 i == Xboom_2 && j == 7 ? 12 :
9136 special_animation && j == 4 ? 3 :
9137 effective_action != action ? 0 :
9139 int frame = getAnimationFrame(g->anim_frames,
9142 g->anim_start_frame,
9145 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9146 g->double_movement && is_backside);
9148 g_em->bitmap = src_bitmap;
9149 g_em->src_x = src_x;
9150 g_em->src_y = src_y;
9151 g_em->src_offset_x = 0;
9152 g_em->src_offset_y = 0;
9153 g_em->dst_offset_x = 0;
9154 g_em->dst_offset_y = 0;
9155 g_em->width = TILEX;
9156 g_em->height = TILEY;
9158 g_em->preserve_background = FALSE;
9160 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9163 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9164 effective_action == ACTION_MOVING ||
9165 effective_action == ACTION_PUSHING ||
9166 effective_action == ACTION_EATING)) ||
9167 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9168 effective_action == ACTION_EMPTYING)))
9171 (effective_action == ACTION_FALLING ||
9172 effective_action == ACTION_FILLING ||
9173 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9174 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9175 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9176 int num_steps = (i == Ydrip_1_s ? 16 :
9177 i == Ydrip_1_sB ? 16 :
9178 i == Ydrip_2_s ? 16 :
9179 i == Ydrip_2_sB ? 16 :
9180 i == Xsand_stonein_1 ? 32 :
9181 i == Xsand_stonein_2 ? 32 :
9182 i == Xsand_stonein_3 ? 32 :
9183 i == Xsand_stonein_4 ? 32 :
9184 i == Xsand_stoneout_1 ? 16 :
9185 i == Xsand_stoneout_2 ? 16 : 8);
9186 int cx = ABS(dx) * (TILEX / num_steps);
9187 int cy = ABS(dy) * (TILEY / num_steps);
9188 int step_frame = (i == Ydrip_2_s ? j + 8 :
9189 i == Ydrip_2_sB ? j + 8 :
9190 i == Xsand_stonein_2 ? j + 8 :
9191 i == Xsand_stonein_3 ? j + 16 :
9192 i == Xsand_stonein_4 ? j + 24 :
9193 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9194 int step = (is_backside ? step_frame : num_steps - step_frame);
9196 if (is_backside) // tile where movement starts
9198 if (dx < 0 || dy < 0)
9200 g_em->src_offset_x = cx * step;
9201 g_em->src_offset_y = cy * step;
9205 g_em->dst_offset_x = cx * step;
9206 g_em->dst_offset_y = cy * step;
9209 else // tile where movement ends
9211 if (dx < 0 || dy < 0)
9213 g_em->dst_offset_x = cx * step;
9214 g_em->dst_offset_y = cy * step;
9218 g_em->src_offset_x = cx * step;
9219 g_em->src_offset_y = cy * step;
9223 g_em->width = TILEX - cx * step;
9224 g_em->height = TILEY - cy * step;
9227 // create unique graphic identifier to decide if tile must be redrawn
9228 /* bit 31 - 16 (16 bit): EM style graphic
9229 bit 15 - 12 ( 4 bit): EM style frame
9230 bit 11 - 6 ( 6 bit): graphic width
9231 bit 5 - 0 ( 6 bit): graphic height */
9232 g_em->unique_identifier =
9233 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9237 for (i = 0; i < GAME_TILE_MAX; i++)
9239 for (j = 0; j < 8; j++)
9241 int element = object_mapping[i].element_rnd;
9242 int action = object_mapping[i].action;
9243 int direction = object_mapping[i].direction;
9244 boolean is_backside = object_mapping[i].is_backside;
9245 int graphic_action = el_act_dir2img(element, action, direction);
9246 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9248 if ((action == ACTION_SMASHED_BY_ROCK ||
9249 action == ACTION_SMASHED_BY_SPRING ||
9250 action == ACTION_EATING) &&
9251 graphic_action == graphic_default)
9253 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9254 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9255 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9256 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9259 // no separate animation for "smashed by rock" -- use rock instead
9260 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9261 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9263 g_em->bitmap = g_xx->bitmap;
9264 g_em->src_x = g_xx->src_x;
9265 g_em->src_y = g_xx->src_y;
9266 g_em->src_offset_x = g_xx->src_offset_x;
9267 g_em->src_offset_y = g_xx->src_offset_y;
9268 g_em->dst_offset_x = g_xx->dst_offset_x;
9269 g_em->dst_offset_y = g_xx->dst_offset_y;
9270 g_em->width = g_xx->width;
9271 g_em->height = g_xx->height;
9272 g_em->unique_identifier = g_xx->unique_identifier;
9275 g_em->preserve_background = TRUE;
9280 for (p = 0; p < MAX_PLAYERS; p++)
9282 for (i = 0; i < PLY_MAX; i++)
9284 int element = player_mapping[p][i].element_rnd;
9285 int action = player_mapping[p][i].action;
9286 int direction = player_mapping[p][i].direction;
9288 for (j = 0; j < 8; j++)
9290 int effective_element = element;
9291 int effective_action = action;
9292 int graphic = (direction == MV_NONE ?
9293 el_act2img(effective_element, effective_action) :
9294 el_act_dir2img(effective_element, effective_action,
9296 struct GraphicInfo *g = &graphic_info[graphic];
9297 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9301 int frame = getAnimationFrame(g->anim_frames,
9304 g->anim_start_frame,
9307 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9309 g_em->bitmap = src_bitmap;
9310 g_em->src_x = src_x;
9311 g_em->src_y = src_y;
9312 g_em->src_offset_x = 0;
9313 g_em->src_offset_y = 0;
9314 g_em->dst_offset_x = 0;
9315 g_em->dst_offset_y = 0;
9316 g_em->width = TILEX;
9317 g_em->height = TILEY;
9323 static void CheckSaveEngineSnapshot_EM(int frame,
9324 boolean any_player_moving,
9325 boolean any_player_snapping,
9326 boolean any_player_dropping)
9328 if (frame == 7 && !any_player_dropping)
9330 if (!local_player->was_waiting)
9332 if (!CheckSaveEngineSnapshotToList())
9335 local_player->was_waiting = TRUE;
9338 else if (any_player_moving || any_player_snapping || any_player_dropping)
9340 local_player->was_waiting = FALSE;
9344 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9345 boolean murphy_is_dropping)
9347 if (murphy_is_waiting)
9349 if (!local_player->was_waiting)
9351 if (!CheckSaveEngineSnapshotToList())
9354 local_player->was_waiting = TRUE;
9359 local_player->was_waiting = FALSE;
9363 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9364 boolean button_released)
9366 if (button_released)
9368 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9369 CheckSaveEngineSnapshotToList();
9371 else if (element_clicked)
9373 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9374 CheckSaveEngineSnapshotToList();
9376 game.snapshot.changed_action = TRUE;
9380 boolean CheckSingleStepMode_EM(int frame,
9381 boolean any_player_moving,
9382 boolean any_player_snapping,
9383 boolean any_player_dropping)
9385 if (tape.single_step && tape.recording && !tape.pausing)
9386 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9387 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9389 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9390 any_player_snapping, any_player_dropping);
9392 return tape.pausing;
9395 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9396 boolean murphy_is_dropping)
9398 boolean murphy_starts_dropping = FALSE;
9401 for (i = 0; i < MAX_PLAYERS; i++)
9402 if (stored_player[i].force_dropping)
9403 murphy_starts_dropping = TRUE;
9405 if (tape.single_step && tape.recording && !tape.pausing)
9406 if (murphy_is_waiting && !murphy_starts_dropping)
9407 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9409 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9412 void CheckSingleStepMode_MM(boolean element_clicked,
9413 boolean button_released)
9415 if (tape.single_step && tape.recording && !tape.pausing)
9416 if (button_released)
9417 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9419 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9422 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9423 int graphic, int sync_frame)
9425 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9427 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9430 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9432 return (IS_NEXT_FRAME(sync_frame, graphic));
9435 int getGraphicInfo_Delay(int graphic)
9437 return graphic_info[graphic].anim_delay;
9440 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9442 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9445 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9451 void PlayMenuSoundExt(int sound)
9453 if (sound == SND_UNDEFINED)
9456 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9457 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9460 if (IS_LOOP_SOUND(sound))
9461 PlaySoundLoop(sound);
9466 void PlayMenuSound(void)
9468 PlayMenuSoundExt(menu.sound[game_status]);
9471 void PlayMenuSoundStereo(int sound, int stereo_position)
9473 if (sound == SND_UNDEFINED)
9476 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9477 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9480 if (IS_LOOP_SOUND(sound))
9481 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9483 PlaySoundStereo(sound, stereo_position);
9486 void PlayMenuSoundIfLoopExt(int sound)
9488 if (sound == SND_UNDEFINED)
9491 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9492 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9495 if (IS_LOOP_SOUND(sound))
9496 PlaySoundLoop(sound);
9499 void PlayMenuSoundIfLoop(void)
9501 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9504 void PlayMenuMusicExt(int music)
9506 if (music == MUS_UNDEFINED)
9509 if (!setup.sound_music)
9512 if (IS_LOOP_MUSIC(music))
9513 PlayMusicLoop(music);
9518 void PlayMenuMusic(void)
9520 char *curr_music = getCurrentlyPlayingMusicFilename();
9521 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9523 if (!strEqual(curr_music, next_music))
9524 PlayMenuMusicExt(menu.music[game_status]);
9527 void PlayMenuSoundsAndMusic(void)
9533 static void FadeMenuSounds(void)
9538 static void FadeMenuMusic(void)
9540 char *curr_music = getCurrentlyPlayingMusicFilename();
9541 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9543 if (!strEqual(curr_music, next_music))
9547 void FadeMenuSoundsAndMusic(void)
9553 void PlaySoundActivating(void)
9556 PlaySound(SND_MENU_ITEM_ACTIVATING);
9560 void PlaySoundSelecting(void)
9563 PlaySound(SND_MENU_ITEM_SELECTING);
9567 void ToggleFullscreenIfNeeded(void)
9569 // if setup and video fullscreen state are already matching, nothing do do
9570 if (setup.fullscreen == video.fullscreen_enabled ||
9571 !video.fullscreen_available)
9574 SDLSetWindowFullscreen(setup.fullscreen);
9576 // set setup value according to successfully changed fullscreen mode
9577 setup.fullscreen = video.fullscreen_enabled;
9580 void ChangeWindowScalingIfNeeded(void)
9582 // if setup and video window scaling are already matching, nothing do do
9583 if (setup.window_scaling_percent == video.window_scaling_percent ||
9584 video.fullscreen_enabled)
9587 SDLSetWindowScaling(setup.window_scaling_percent);
9589 // set setup value according to successfully changed window scaling
9590 setup.window_scaling_percent = video.window_scaling_percent;
9593 void ChangeVsyncModeIfNeeded(void)
9595 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9596 int video_vsync_mode = video.vsync_mode;
9598 // if setup and video vsync mode are already matching, nothing do do
9599 if (setup_vsync_mode == video_vsync_mode)
9602 // if renderer is using OpenGL, vsync mode can directly be changed
9603 SDLSetScreenVsyncMode(setup.vsync_mode);
9605 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9606 if (video.vsync_mode == video_vsync_mode)
9608 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9610 // save backbuffer content which gets lost when re-creating screen
9611 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9613 // force re-creating screen and renderer to set new vsync mode
9614 video.fullscreen_enabled = !setup.fullscreen;
9616 // when creating new renderer, destroy textures linked to old renderer
9617 FreeAllImageTextures(); // needs old renderer to free the textures
9619 // re-create screen and renderer (including change of vsync mode)
9620 ChangeVideoModeIfNeeded(setup.fullscreen);
9622 // set setup value according to successfully changed fullscreen mode
9623 setup.fullscreen = video.fullscreen_enabled;
9625 // restore backbuffer content from temporary backbuffer backup bitmap
9626 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9627 FreeBitmap(tmp_backbuffer);
9629 // update visible window/screen
9630 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9632 // when changing vsync mode, re-create textures for new renderer
9633 InitImageTextures();
9636 // set setup value according to successfully changed vsync mode
9637 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9640 static void JoinRectangles(int *x, int *y, int *width, int *height,
9641 int x2, int y2, int width2, int height2)
9643 // do not join with "off-screen" rectangle
9644 if (x2 == -1 || y2 == -1)
9649 *width = MAX(*width, width2);
9650 *height = MAX(*height, height2);
9653 void SetAnimStatus(int anim_status_new)
9655 if (anim_status_new == GAME_MODE_MAIN)
9656 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9657 else if (anim_status_new == GAME_MODE_NAMES)
9658 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9659 else if (anim_status_new == GAME_MODE_SCORES)
9660 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9662 global.anim_status_next = anim_status_new;
9664 // directly set screen modes that are entered without fading
9665 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9666 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9667 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9668 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9669 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9670 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9671 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9672 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9673 global.anim_status = global.anim_status_next;
9676 void SetGameStatus(int game_status_new)
9678 if (game_status_new != game_status)
9679 game_status_last_screen = game_status;
9681 game_status = game_status_new;
9683 SetAnimStatus(game_status_new);
9686 void SetFontStatus(int game_status_new)
9688 static int last_game_status = -1;
9690 if (game_status_new != -1)
9692 // set game status for font use after storing last game status
9693 last_game_status = game_status;
9694 game_status = game_status_new;
9698 // reset game status after font use from last stored game status
9699 game_status = last_game_status;
9703 void ResetFontStatus(void)
9708 void SetLevelSetInfo(char *identifier, int level_nr)
9710 setString(&levelset.identifier, identifier);
9712 levelset.level_nr = level_nr;
9715 boolean CheckIfAllViewportsHaveChanged(void)
9717 // if game status has not changed, viewports have not changed either
9718 if (game_status == game_status_last)
9721 // check if all viewports have changed with current game status
9723 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9724 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9725 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9726 int new_real_sx = vp_playfield->x;
9727 int new_real_sy = vp_playfield->y;
9728 int new_full_sxsize = vp_playfield->width;
9729 int new_full_sysize = vp_playfield->height;
9730 int new_dx = vp_door_1->x;
9731 int new_dy = vp_door_1->y;
9732 int new_dxsize = vp_door_1->width;
9733 int new_dysize = vp_door_1->height;
9734 int new_vx = vp_door_2->x;
9735 int new_vy = vp_door_2->y;
9736 int new_vxsize = vp_door_2->width;
9737 int new_vysize = vp_door_2->height;
9739 boolean playfield_viewport_has_changed =
9740 (new_real_sx != REAL_SX ||
9741 new_real_sy != REAL_SY ||
9742 new_full_sxsize != FULL_SXSIZE ||
9743 new_full_sysize != FULL_SYSIZE);
9745 boolean door_1_viewport_has_changed =
9748 new_dxsize != DXSIZE ||
9749 new_dysize != DYSIZE);
9751 boolean door_2_viewport_has_changed =
9754 new_vxsize != VXSIZE ||
9755 new_vysize != VYSIZE ||
9756 game_status_last == GAME_MODE_EDITOR);
9758 return (playfield_viewport_has_changed &&
9759 door_1_viewport_has_changed &&
9760 door_2_viewport_has_changed);
9763 boolean CheckFadeAll(void)
9765 return (CheckIfGlobalBorderHasChanged() ||
9766 CheckIfAllViewportsHaveChanged());
9769 void ChangeViewportPropertiesIfNeeded(void)
9771 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9772 FALSE : setup.small_game_graphics);
9773 int gfx_game_mode = getGlobalGameStatus(game_status);
9774 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9776 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9777 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9778 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9779 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9780 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9781 int new_win_xsize = vp_window->width;
9782 int new_win_ysize = vp_window->height;
9783 int border_left = vp_playfield->border_left;
9784 int border_right = vp_playfield->border_right;
9785 int border_top = vp_playfield->border_top;
9786 int border_bottom = vp_playfield->border_bottom;
9787 int new_sx = vp_playfield->x + border_left;
9788 int new_sy = vp_playfield->y + border_top;
9789 int new_sxsize = vp_playfield->width - border_left - border_right;
9790 int new_sysize = vp_playfield->height - border_top - border_bottom;
9791 int new_real_sx = vp_playfield->x;
9792 int new_real_sy = vp_playfield->y;
9793 int new_full_sxsize = vp_playfield->width;
9794 int new_full_sysize = vp_playfield->height;
9795 int new_dx = vp_door_1->x;
9796 int new_dy = vp_door_1->y;
9797 int new_dxsize = vp_door_1->width;
9798 int new_dysize = vp_door_1->height;
9799 int new_vx = vp_door_2->x;
9800 int new_vy = vp_door_2->y;
9801 int new_vxsize = vp_door_2->width;
9802 int new_vysize = vp_door_2->height;
9803 int new_ex = vp_door_3->x;
9804 int new_ey = vp_door_3->y;
9805 int new_exsize = vp_door_3->width;
9806 int new_eysize = vp_door_3->height;
9807 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9808 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9809 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9810 int new_scr_fieldx = new_sxsize / tilesize;
9811 int new_scr_fieldy = new_sysize / tilesize;
9812 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9813 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9814 boolean init_gfx_buffers = FALSE;
9815 boolean init_video_buffer = FALSE;
9816 boolean init_gadgets_and_anims = FALSE;
9817 boolean init_em_graphics = FALSE;
9819 if (new_win_xsize != WIN_XSIZE ||
9820 new_win_ysize != WIN_YSIZE)
9822 WIN_XSIZE = new_win_xsize;
9823 WIN_YSIZE = new_win_ysize;
9825 init_video_buffer = TRUE;
9826 init_gfx_buffers = TRUE;
9827 init_gadgets_and_anims = TRUE;
9829 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9832 if (new_scr_fieldx != SCR_FIELDX ||
9833 new_scr_fieldy != SCR_FIELDY)
9835 // this always toggles between MAIN and GAME when using small tile size
9837 SCR_FIELDX = new_scr_fieldx;
9838 SCR_FIELDY = new_scr_fieldy;
9840 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9851 new_sxsize != SXSIZE ||
9852 new_sysize != SYSIZE ||
9853 new_dxsize != DXSIZE ||
9854 new_dysize != DYSIZE ||
9855 new_vxsize != VXSIZE ||
9856 new_vysize != VYSIZE ||
9857 new_exsize != EXSIZE ||
9858 new_eysize != EYSIZE ||
9859 new_real_sx != REAL_SX ||
9860 new_real_sy != REAL_SY ||
9861 new_full_sxsize != FULL_SXSIZE ||
9862 new_full_sysize != FULL_SYSIZE ||
9863 new_tilesize_var != TILESIZE_VAR
9866 // ------------------------------------------------------------------------
9867 // determine next fading area for changed viewport definitions
9868 // ------------------------------------------------------------------------
9870 // start with current playfield area (default fading area)
9873 FADE_SXSIZE = FULL_SXSIZE;
9874 FADE_SYSIZE = FULL_SYSIZE;
9876 // add new playfield area if position or size has changed
9877 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9878 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9880 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9881 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9884 // add current and new door 1 area if position or size has changed
9885 if (new_dx != DX || new_dy != DY ||
9886 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9888 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9889 DX, DY, DXSIZE, DYSIZE);
9890 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9891 new_dx, new_dy, new_dxsize, new_dysize);
9894 // add current and new door 2 area if position or size has changed
9895 if (new_vx != VX || new_vy != VY ||
9896 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9898 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9899 VX, VY, VXSIZE, VYSIZE);
9900 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9901 new_vx, new_vy, new_vxsize, new_vysize);
9904 // ------------------------------------------------------------------------
9905 // handle changed tile size
9906 // ------------------------------------------------------------------------
9908 if (new_tilesize_var != TILESIZE_VAR)
9910 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9912 // changing tile size invalidates scroll values of engine snapshots
9913 FreeEngineSnapshotSingle();
9915 // changing tile size requires update of graphic mapping for EM engine
9916 init_em_graphics = TRUE;
9927 SXSIZE = new_sxsize;
9928 SYSIZE = new_sysize;
9929 DXSIZE = new_dxsize;
9930 DYSIZE = new_dysize;
9931 VXSIZE = new_vxsize;
9932 VYSIZE = new_vysize;
9933 EXSIZE = new_exsize;
9934 EYSIZE = new_eysize;
9935 REAL_SX = new_real_sx;
9936 REAL_SY = new_real_sy;
9937 FULL_SXSIZE = new_full_sxsize;
9938 FULL_SYSIZE = new_full_sysize;
9939 TILESIZE_VAR = new_tilesize_var;
9941 init_gfx_buffers = TRUE;
9942 init_gadgets_and_anims = TRUE;
9944 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9945 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9948 if (init_gfx_buffers)
9950 // Debug("tools:viewport", "init_gfx_buffers");
9952 SCR_FIELDX = new_scr_fieldx_buffers;
9953 SCR_FIELDY = new_scr_fieldy_buffers;
9957 SCR_FIELDX = new_scr_fieldx;
9958 SCR_FIELDY = new_scr_fieldy;
9960 SetDrawDeactivationMask(REDRAW_NONE);
9961 SetDrawBackgroundMask(REDRAW_FIELD);
9964 if (init_video_buffer)
9966 // Debug("tools:viewport", "init_video_buffer");
9968 FreeAllImageTextures(); // needs old renderer to free the textures
9970 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9971 InitImageTextures();
9974 if (init_gadgets_and_anims)
9976 // Debug("tools:viewport", "init_gadgets_and_anims");
9979 InitGlobalAnimations();
9982 if (init_em_graphics)
9984 InitGraphicInfo_EM();
9988 void OpenURL(char *url)
9990 #if SDL_VERSION_ATLEAST(2,0,14)
9993 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
9994 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
9995 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
9999 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
10001 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
10005 // ============================================================================
10007 // ============================================================================
10009 #if defined(PLATFORM_WINDOWS)
10010 /* FILETIME of Jan 1 1970 00:00:00. */
10011 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
10014 * timezone information is stored outside the kernel so tzp isn't used anymore.
10016 * Note: this function is not for Win32 high precision timing purpose. See
10019 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
10021 FILETIME file_time;
10022 SYSTEMTIME system_time;
10023 ULARGE_INTEGER ularge;
10025 GetSystemTime(&system_time);
10026 SystemTimeToFileTime(&system_time, &file_time);
10027 ularge.LowPart = file_time.dwLowDateTime;
10028 ularge.HighPart = file_time.dwHighDateTime;
10030 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10031 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10037 static char *test_init_uuid_random_function_simple(void)
10039 static char seed_text[100];
10040 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10042 sprintf(seed_text, "%d", seed);
10047 static char *test_init_uuid_random_function_better(void)
10049 static char seed_text[100];
10050 struct timeval current_time;
10052 gettimeofday(¤t_time, NULL);
10054 prng_seed_bytes(¤t_time, sizeof(current_time));
10056 sprintf(seed_text, "%ld.%ld",
10057 (long)current_time.tv_sec,
10058 (long)current_time.tv_usec);
10063 #if defined(PLATFORM_WINDOWS)
10064 static char *test_init_uuid_random_function_better_windows(void)
10066 static char seed_text[100];
10067 struct timeval current_time;
10069 gettimeofday_windows(¤t_time, NULL);
10071 prng_seed_bytes(¤t_time, sizeof(current_time));
10073 sprintf(seed_text, "%ld.%ld",
10074 (long)current_time.tv_sec,
10075 (long)current_time.tv_usec);
10081 static unsigned int test_uuid_random_function_simple(int max)
10083 return GetSimpleRandom(max);
10086 static unsigned int test_uuid_random_function_better(int max)
10088 return (max > 0 ? prng_get_uint() % max : 0);
10091 #if defined(PLATFORM_WINDOWS)
10092 #define NUM_UUID_TESTS 3
10094 #define NUM_UUID_TESTS 2
10097 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10099 struct hashtable *hash_seeds =
10100 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10101 struct hashtable *hash_uuids =
10102 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10103 static char message[100];
10106 char *random_name = (nr == 0 ? "simple" : "better");
10107 char *random_type = (always_seed ? "always" : "only once");
10108 char *(*init_random_function)(void) =
10110 test_init_uuid_random_function_simple :
10111 test_init_uuid_random_function_better);
10112 unsigned int (*random_function)(int) =
10114 test_uuid_random_function_simple :
10115 test_uuid_random_function_better);
10118 #if defined(PLATFORM_WINDOWS)
10121 random_name = "windows";
10122 init_random_function = test_init_uuid_random_function_better_windows;
10128 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10129 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10131 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10132 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10133 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10135 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10139 // always initialize random number generator at least once
10140 init_random_function();
10142 unsigned int time_start = SDL_GetTicks();
10144 for (i = 0; i < num_uuids; i++)
10148 char *seed = getStringCopy(init_random_function());
10150 hashtable_remove(hash_seeds, seed);
10151 hashtable_insert(hash_seeds, seed, "1");
10154 char *uuid = getStringCopy(getUUIDExt(random_function));
10156 hashtable_remove(hash_uuids, uuid);
10157 hashtable_insert(hash_uuids, uuid, "1");
10160 int num_unique_seeds = hashtable_count(hash_seeds);
10161 int num_unique_uuids = hashtable_count(hash_uuids);
10163 unsigned int time_needed = SDL_GetTicks() - time_start;
10165 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10167 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10170 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10172 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10173 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10175 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10177 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10179 Request(message, REQ_CONFIRM);
10181 hashtable_destroy(hash_seeds, 0);
10182 hashtable_destroy(hash_uuids, 0);
10185 void TestGeneratingUUIDs(void)
10187 int num_uuids = 1000000;
10190 for (i = 0; i < NUM_UUID_TESTS; i++)
10191 for (j = 0; j < 2; j++)
10192 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10194 CloseAllAndExit(0);