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 MapToolButtons(unsigned int);
179 static void UnmapToolButtons(void);
180 static void HandleToolButtons(struct GadgetInfo *);
181 static int el_act_dir2crm(int, int, int);
182 static int el_act2crm(int, int);
184 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
185 static int request_gadget_id = -1;
187 static char *print_if_not_empty(int element)
189 static char *s = NULL;
190 char *token_name = element_info[element].token_name;
195 s = checked_malloc(strlen(token_name) + 10 + 1);
197 if (element != EL_EMPTY)
198 sprintf(s, "%d\t['%s']", element, token_name);
200 sprintf(s, "%d", element);
205 int getFieldbufferOffsetX_RND(int dir, int pos)
207 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
208 int dx = (dir & MV_HORIZONTAL ? pos : 0);
209 int dx_var = dx * TILESIZE_VAR / TILESIZE;
212 if (EVEN(SCR_FIELDX))
214 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
215 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
217 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
218 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
220 fx += (dx_var > 0 ? TILEX_VAR : 0);
227 if (full_lev_fieldx <= SCR_FIELDX)
229 if (EVEN(SCR_FIELDX))
230 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
232 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
238 int getFieldbufferOffsetY_RND(int dir, int pos)
240 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
241 int dy = (dir & MV_VERTICAL ? pos : 0);
242 int dy_var = dy * TILESIZE_VAR / TILESIZE;
245 if (EVEN(SCR_FIELDY))
247 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
248 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
250 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
251 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
253 fy += (dy_var > 0 ? TILEY_VAR : 0);
260 if (full_lev_fieldy <= SCR_FIELDY)
262 if (EVEN(SCR_FIELDY))
263 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
265 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
271 static int getLevelFromScreenX_RND(int sx)
273 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
276 int lx = LEVELX((px + dx) / TILESIZE_VAR);
281 static int getLevelFromScreenY_RND(int sy)
283 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
286 int ly = LEVELY((py + dy) / TILESIZE_VAR);
291 static int getLevelFromScreenX_EM(int sx)
293 int level_xsize = level.native_em_level->cav->width;
294 int full_xsize = level_xsize * TILESIZE_VAR;
296 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
298 int fx = getFieldbufferOffsetX_EM();
301 int lx = LEVELX((px + dx) / TILESIZE_VAR);
306 static int getLevelFromScreenY_EM(int sy)
308 int level_ysize = level.native_em_level->cav->height;
309 int full_ysize = level_ysize * TILESIZE_VAR;
311 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
313 int fy = getFieldbufferOffsetY_EM();
316 int ly = LEVELY((py + dy) / TILESIZE_VAR);
321 static int getLevelFromScreenX_SP(int sx)
323 int menBorder = setup.sp_show_border_elements;
324 int level_xsize = level.native_sp_level->width;
325 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
327 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
329 int fx = getFieldbufferOffsetX_SP();
332 int lx = LEVELX((px + dx) / TILESIZE_VAR);
337 static int getLevelFromScreenY_SP(int sy)
339 int menBorder = setup.sp_show_border_elements;
340 int level_ysize = level.native_sp_level->height;
341 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
343 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
345 int fy = getFieldbufferOffsetY_SP();
348 int ly = LEVELY((py + dy) / TILESIZE_VAR);
353 static int getLevelFromScreenX_MM(int sx)
355 int level_xsize = level.native_mm_level->fieldx;
356 int full_xsize = level_xsize * TILESIZE_VAR;
358 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
361 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
366 static int getLevelFromScreenY_MM(int sy)
368 int level_ysize = level.native_mm_level->fieldy;
369 int full_ysize = level_ysize * TILESIZE_VAR;
371 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
374 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
379 int getLevelFromScreenX(int x)
381 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
382 return getLevelFromScreenX_EM(x);
383 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
384 return getLevelFromScreenX_SP(x);
385 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
386 return getLevelFromScreenX_MM(x);
388 return getLevelFromScreenX_RND(x);
391 int getLevelFromScreenY(int y)
393 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
394 return getLevelFromScreenY_EM(y);
395 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
396 return getLevelFromScreenY_SP(y);
397 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
398 return getLevelFromScreenY_MM(y);
400 return getLevelFromScreenY_RND(y);
403 int getScreenFieldSizeX(void)
405 return (tape.playing ? tape.scr_fieldx : SCR_FIELDX);
408 int getScreenFieldSizeY(void)
410 return (tape.playing ? tape.scr_fieldy : SCR_FIELDY);
413 void DumpTile(int x, int y)
420 Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
423 if (!IN_LEV_FIELD(x, y))
425 Info("(not in level field)");
431 token_name = element_info[Tile[x][y]].token_name;
433 Info("Tile: %d\t['%s']", Tile[x][y], token_name);
434 Info("Back: %s", print_if_not_empty(Back[x][y]));
435 Info("Store: %s", print_if_not_empty(Store[x][y]));
436 Info("Store2: %s", print_if_not_empty(Store2[x][y]));
437 Info("StorePlayer: %s", print_if_not_empty(StorePlayer[x][y]));
438 Info("MovPos: %d", MovPos[x][y]);
439 Info("MovDir: %d", MovDir[x][y]);
440 Info("MovDelay: %d", MovDelay[x][y]);
441 Info("ChangeDelay: %d", ChangeDelay[x][y]);
442 Info("CustomValue: %d", CustomValue[x][y]);
443 Info("GfxElement: %d", GfxElement[x][y]);
444 Info("GfxAction: %d", GfxAction[x][y]);
445 Info("GfxFrame: %d [%d]", GfxFrame[x][y], FrameCounter);
446 Info("Player x/y: %d, %d", local_player->jx, local_player->jy);
450 void DumpTileFromScreen(int sx, int sy)
452 int lx = getLevelFromScreenX(sx);
453 int ly = getLevelFromScreenY(sy);
458 void SetDrawtoField(int mode)
460 if (mode == DRAW_TO_FIELDBUFFER)
466 BX2 = SCR_FIELDX + 1;
467 BY2 = SCR_FIELDY + 1;
469 drawto_field = fieldbuffer;
471 else // DRAW_TO_BACKBUFFER
477 BX2 = SCR_FIELDX - 1;
478 BY2 = SCR_FIELDY - 1;
480 drawto_field = backbuffer;
484 int GetDrawtoField(void)
486 return (drawto_field == fieldbuffer ? DRAW_TO_FIELDBUFFER : DRAW_TO_BACKBUFFER);
489 static void RedrawPlayfield_RND(void)
491 if (game.envelope_active)
494 DrawLevel(REDRAW_ALL);
498 void RedrawPlayfield(void)
500 if (game_status != GAME_MODE_PLAYING)
503 if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
504 RedrawPlayfield_BD(TRUE);
505 else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
506 RedrawPlayfield_EM(TRUE);
507 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
508 RedrawPlayfield_SP(TRUE);
509 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
510 RedrawPlayfield_MM();
511 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
512 RedrawPlayfield_RND();
514 BlitScreenToBitmap(backbuffer);
516 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
520 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
523 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
524 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
526 // may happen for "border.draw_masked.*" with undefined "global.border.*"
527 if (src_bitmap == NULL)
530 if (x == -1 && y == -1)
533 if (draw_target == DRAW_TO_SCREEN)
534 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
536 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
539 static void DrawMaskedBorderExt_FIELD(int draw_target)
541 if (global.border_status >= GAME_MODE_MAIN &&
542 global.border_status <= GAME_MODE_PLAYING &&
543 border.draw_masked[global.border_status])
544 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
548 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
550 // when drawing to backbuffer, never draw border over open doors
551 if (draw_target == DRAW_TO_BACKBUFFER &&
552 (GetDoorState() & DOOR_OPEN_1))
555 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
556 (global.border_status != GAME_MODE_EDITOR ||
557 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
558 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
561 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
563 // when drawing to backbuffer, never draw border over open doors
564 if (draw_target == DRAW_TO_BACKBUFFER &&
565 (GetDoorState() & DOOR_OPEN_2))
568 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
569 global.border_status != GAME_MODE_EDITOR)
570 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
573 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
575 // currently not available
578 static void DrawMaskedBorderExt_ALL(int draw_target)
580 DrawMaskedBorderExt_FIELD(draw_target);
581 DrawMaskedBorderExt_DOOR_1(draw_target);
582 DrawMaskedBorderExt_DOOR_2(draw_target);
583 DrawMaskedBorderExt_DOOR_3(draw_target);
586 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
588 // never draw masked screen borders on borderless screens
589 if (global.border_status == GAME_MODE_LOADING ||
590 global.border_status == GAME_MODE_TITLE)
593 if (redraw_mask & REDRAW_ALL)
594 DrawMaskedBorderExt_ALL(draw_target);
597 if (redraw_mask & REDRAW_FIELD)
598 DrawMaskedBorderExt_FIELD(draw_target);
599 if (redraw_mask & REDRAW_DOOR_1)
600 DrawMaskedBorderExt_DOOR_1(draw_target);
601 if (redraw_mask & REDRAW_DOOR_2)
602 DrawMaskedBorderExt_DOOR_2(draw_target);
603 if (redraw_mask & REDRAW_DOOR_3)
604 DrawMaskedBorderExt_DOOR_3(draw_target);
608 void DrawMaskedBorder_FIELD(void)
610 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
613 void DrawMaskedBorder(int redraw_mask)
615 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
618 void DrawMaskedBorderToTarget(int draw_target)
620 if (draw_target == DRAW_TO_BACKBUFFER ||
621 draw_target == DRAW_TO_SCREEN)
623 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
627 int last_border_status = global.border_status;
629 if (draw_target == DRAW_TO_FADE_SOURCE)
631 global.border_status = gfx.fade_border_source_status;
632 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
634 else if (draw_target == DRAW_TO_FADE_TARGET)
636 global.border_status = gfx.fade_border_target_status;
637 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
640 // always use global border for PLAYING when restarting the game
641 if (global.border_status == GAME_MODE_PSEUDO_RESTARTING)
642 global.border_status = GAME_MODE_PLAYING;
644 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
646 global.border_status = last_border_status;
647 gfx.masked_border_bitmap_ptr = backbuffer;
651 void DrawTileCursor(int draw_target, int drawing_stage)
653 int tile_cursor_active = (game_status == GAME_MODE_PLAYING);
655 DrawTileCursor_MM(draw_target, drawing_stage, tile_cursor_active);
658 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
660 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
663 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
665 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
666 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
668 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
671 void BlitScreenToBitmap(Bitmap *target_bitmap)
673 if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
674 BlitScreenToBitmap_BD(target_bitmap);
675 else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
676 BlitScreenToBitmap_EM(target_bitmap);
677 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
678 BlitScreenToBitmap_SP(target_bitmap);
679 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
680 BlitScreenToBitmap_MM(target_bitmap);
681 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
682 BlitScreenToBitmap_RND(target_bitmap);
684 redraw_mask |= REDRAW_FIELD;
687 static void DrawFramesPerSecond(void)
690 int font_nr = FONT_TEXT_2;
691 int font_width = getFontWidth(font_nr);
692 int draw_deactivation_mask = GetDrawDeactivationMask();
693 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
695 // draw FPS with leading space (needed if field buffer deactivated)
696 sprintf(text, " %04.1f fps", global.frames_per_second);
698 // override draw deactivation mask (required for invisible warp mode)
699 SetDrawDeactivationMask(REDRAW_NONE);
701 // draw opaque FPS if field buffer deactivated, else draw masked FPS
702 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
703 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
705 // set draw deactivation mask to previous value
706 SetDrawDeactivationMask(draw_deactivation_mask);
708 // force full-screen redraw in this frame
709 redraw_mask = REDRAW_ALL;
713 static void PrintFrameTimeDebugging(void)
715 static unsigned int last_counter = 0;
716 unsigned int counter = Counter();
717 int diff_1 = counter - last_counter;
718 int diff_2 = diff_1 - GAME_FRAME_DELAY;
720 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
721 char diff_bar[2 * diff_2_max + 5];
725 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
727 for (i = 0; i < diff_2_max; i++)
728 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
729 i >= diff_2_max - diff_2_cut ? '-' : ' ');
731 diff_bar[pos++] = '|';
733 for (i = 0; i < diff_2_max; i++)
734 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
736 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
738 diff_bar[pos++] = '\0';
740 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
743 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
746 last_counter = counter;
750 static int unifiedRedrawMask(int mask)
752 if (mask & REDRAW_ALL)
755 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
761 static boolean equalRedrawMasks(int mask_1, int mask_2)
763 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
766 void BackToFront(void)
768 static int last_redraw_mask = REDRAW_NONE;
770 // force screen redraw in every frame to continue drawing global animations
771 // (but always use the last redraw mask to prevent unwanted side effects)
772 if (redraw_mask == REDRAW_NONE)
773 redraw_mask = last_redraw_mask;
775 last_redraw_mask = redraw_mask;
778 // masked border now drawn immediately when blitting backbuffer to window
780 // draw masked border to all viewports, if defined
781 DrawMaskedBorder(redraw_mask);
784 // draw frames per second (only if debug mode is enabled)
785 if (redraw_mask & REDRAW_FPS)
786 DrawFramesPerSecond();
788 // remove playfield redraw before potentially merging with doors redraw
789 if (DrawingDeactivated(REAL_SX, REAL_SY))
790 redraw_mask &= ~REDRAW_FIELD;
792 // redraw complete window if both playfield and (some) doors need redraw
793 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
794 redraw_mask = REDRAW_ALL;
796 /* although redrawing the whole window would be fine for normal gameplay,
797 being able to only redraw the playfield is required for deactivating
798 certain drawing areas (mainly playfield) to work, which is needed for
799 warp-forward to be fast enough (by skipping redraw of most frames) */
801 if (redraw_mask & REDRAW_ALL)
803 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
805 else if (redraw_mask & REDRAW_FIELD)
807 BlitBitmap(backbuffer, window,
808 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
810 else if (redraw_mask & REDRAW_DOORS)
812 // merge door areas to prevent calling screen redraw more than once
818 if (redraw_mask & REDRAW_DOOR_1)
822 x2 = MAX(x2, DX + DXSIZE);
823 y2 = MAX(y2, DY + DYSIZE);
826 if (redraw_mask & REDRAW_DOOR_2)
830 x2 = MAX(x2, VX + VXSIZE);
831 y2 = MAX(y2, VY + VYSIZE);
834 if (redraw_mask & REDRAW_DOOR_3)
838 x2 = MAX(x2, EX + EXSIZE);
839 y2 = MAX(y2, EY + EYSIZE);
842 // make sure that at least one pixel is blitted, and inside the screen
843 // (else nothing is blitted, causing the animations not to be updated)
844 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
845 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
846 x2 = MIN(MAX(1, x2), WIN_XSIZE);
847 y2 = MIN(MAX(1, y2), WIN_YSIZE);
849 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
852 redraw_mask = REDRAW_NONE;
855 PrintFrameTimeDebugging();
859 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
861 unsigned int frame_delay_value_old = GetVideoFrameDelay();
863 SetVideoFrameDelay(frame_delay_value);
867 SetVideoFrameDelay(frame_delay_value_old);
870 static int fade_type_skip = FADE_TYPE_NONE;
872 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
874 void (*draw_border_function)(void) = NULL;
875 int x, y, width, height;
876 int fade_delay, post_delay;
878 if (fade_type == FADE_TYPE_FADE_OUT)
880 if (fade_type_skip != FADE_TYPE_NONE)
882 // skip all fade operations until specified fade operation
883 if (fade_type & fade_type_skip)
884 fade_type_skip = FADE_TYPE_NONE;
889 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
893 redraw_mask |= fade_mask;
895 if (fade_type == FADE_TYPE_SKIP)
897 fade_type_skip = fade_mode;
902 fade_delay = fading.fade_delay;
903 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
905 if (fade_type_skip != FADE_TYPE_NONE)
907 // skip all fade operations until specified fade operation
908 if (fade_type & fade_type_skip)
909 fade_type_skip = FADE_TYPE_NONE;
914 if (global.autoplay_leveldir)
919 if (fade_mask == REDRAW_FIELD)
924 height = FADE_SYSIZE;
926 if (border.draw_masked_when_fading)
927 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
929 DrawMaskedBorder_FIELD(); // draw once
939 // when switching screens without fading, set fade delay to zero
940 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
943 // do not display black frame when fading out without fade delay
944 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
947 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
948 draw_border_function);
950 redraw_mask &= ~fade_mask;
952 ClearAutoRepeatKeyEvents();
955 static void SetScreenStates_BeforeFadingIn(void)
957 // temporarily set screen mode for animations to screen after fading in
958 global.anim_status = global.anim_status_next;
960 // store backbuffer with all animations that will be started after fading in
961 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
963 // set screen mode for animations back to fading
964 global.anim_status = GAME_MODE_PSEUDO_FADING;
967 static void SetScreenStates_AfterFadingIn(void)
969 // store new source screen (to use correct masked border for fading)
970 gfx.fade_border_source_status = global.border_status;
972 global.anim_status = global.anim_status_next;
975 static void SetScreenStates_BeforeFadingOut(void)
977 // store new target screen (to use correct masked border for fading)
978 gfx.fade_border_target_status = game_status;
980 // set screen mode for animations to fading
981 global.anim_status = GAME_MODE_PSEUDO_FADING;
983 // store backbuffer with all animations that will be stopped for fading out
984 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
987 static void SetScreenStates_AfterFadingOut(void)
989 global.border_status = game_status;
991 // always use global border for PLAYING when restarting the game
992 if (global.border_status == GAME_MODE_PSEUDO_RESTARTING)
993 global.border_status = GAME_MODE_PLAYING;
996 void FadeIn(int fade_mask)
998 SetScreenStates_BeforeFadingIn();
1001 DrawMaskedBorder(REDRAW_ALL);
1004 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1005 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1007 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1011 FADE_SXSIZE = FULL_SXSIZE;
1012 FADE_SYSIZE = FULL_SYSIZE;
1014 // activate virtual buttons depending on upcoming game status
1015 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1016 game_status == GAME_MODE_PLAYING && !tape.playing)
1017 SetOverlayActive(TRUE);
1019 SetScreenStates_AfterFadingIn();
1021 // force update of global animation status in case of rapid screen changes
1022 redraw_mask = REDRAW_ALL;
1026 void FadeOut(int fade_mask)
1028 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1029 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1030 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1033 // when using BD game engine, cover playfield before fading out after a game
1034 if (game_bd.cover_screen)
1037 SetScreenStates_BeforeFadingOut();
1039 SetTileCursorActive(FALSE);
1040 SetOverlayActive(FALSE);
1043 DrawMaskedBorder(REDRAW_ALL);
1046 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1047 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1049 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1051 SetScreenStates_AfterFadingOut();
1054 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1056 static struct TitleFadingInfo fading_leave_stored;
1059 fading_leave_stored = fading_leave;
1061 fading = fading_leave_stored;
1064 void FadeSetEnterMenu(void)
1066 fading = menu.enter_menu;
1068 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1071 void FadeSetLeaveMenu(void)
1073 fading = menu.leave_menu;
1075 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1078 void FadeSetEnterScreen(void)
1080 fading = menu.enter_screen[game_status];
1082 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1085 void FadeSetNextScreen(void)
1087 fading = menu.next_screen[game_status];
1089 // (do not overwrite fade mode set by FadeSetEnterScreen)
1090 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1093 void FadeSetLeaveScreen(void)
1095 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1098 void FadeSetFromType(int type)
1100 if (type & TYPE_ENTER_SCREEN)
1101 FadeSetEnterScreen();
1102 else if (type & TYPE_ENTER)
1104 else if (type & TYPE_LEAVE)
1108 void FadeSetDisabled(void)
1110 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1112 fading = fading_none;
1115 void FadeSkipNextFadeIn(void)
1117 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1120 void FadeSkipNextFadeOut(void)
1122 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1125 static int getGlobalGameStatus(int status)
1127 return (status == GAME_MODE_PSEUDO_TYPENAME ? GAME_MODE_MAIN :
1128 status == GAME_MODE_SCOREINFO ? GAME_MODE_SCORES :
1132 int getImageFromGraphicOrDefault(int graphic, int default_graphic)
1134 if (graphic == IMG_UNDEFINED)
1135 return IMG_UNDEFINED;
1137 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1139 return (graphic_info[graphic].bitmap != NULL || redefined ?
1140 graphic : default_graphic);
1143 static int getBackgroundImage(int graphic)
1145 return getImageFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1148 static int getGlobalBorderImage(int graphic)
1150 return getImageFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1153 Bitmap *getGlobalBorderBitmapFromStatus(int status_raw)
1155 int status = getGlobalGameStatus(status_raw);
1157 (status == GAME_MODE_MAIN ? IMG_GLOBAL_BORDER_MAIN :
1158 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1159 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1160 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1162 int graphic_final = getGlobalBorderImage(graphic);
1164 return graphic_info[graphic_final].bitmap;
1167 void SetBackgroundImage(int graphic, int redraw_mask)
1169 struct GraphicInfo *g = &graphic_info[graphic];
1170 struct GraphicInfo g_undefined = { 0 };
1172 if (graphic == IMG_UNDEFINED)
1175 // always use original size bitmap for backgrounds, if existing
1176 Bitmap *bitmap = (g->bitmaps != NULL &&
1177 g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] != NULL ?
1178 g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] : g->bitmap);
1180 // remove every mask before setting mask for window, and
1181 // remove window area mask before setting mask for main or door area
1182 int remove_mask = (redraw_mask == REDRAW_ALL ? 0xffff : REDRAW_ALL);
1184 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
1185 SetBackgroundBitmap(NULL, remove_mask, 0, 0, 0, 0); // !!! FIX THIS !!!
1186 SetBackgroundBitmap(bitmap, redraw_mask,
1188 g->width, g->height);
1191 void SetWindowBackgroundImageIfDefined(int graphic)
1193 if (graphic_info[graphic].bitmap)
1194 SetBackgroundImage(graphic, REDRAW_ALL);
1197 void SetMainBackgroundImageIfDefined(int graphic)
1199 if (graphic_info[graphic].bitmap)
1200 SetBackgroundImage(graphic, REDRAW_FIELD);
1203 void SetDoorBackgroundImageIfDefined(int graphic)
1205 if (graphic_info[graphic].bitmap)
1206 SetBackgroundImage(graphic, REDRAW_DOOR_1);
1209 void SetWindowBackgroundImage(int graphic)
1211 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_ALL);
1214 void SetMainBackgroundImage(int graphic)
1216 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_FIELD);
1219 void SetDoorBackgroundImage(int graphic)
1221 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_DOOR_1);
1224 void SetPanelBackground(void)
1226 SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
1229 void DrawBackground(int x, int y, int width, int height)
1231 // "drawto" might still point to playfield buffer here (hall of fame)
1232 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1234 if (IN_GFX_FIELD_FULL(x, y))
1235 redraw_mask |= REDRAW_FIELD;
1236 else if (IN_GFX_DOOR_1(x, y))
1237 redraw_mask |= REDRAW_DOOR_1;
1238 else if (IN_GFX_DOOR_2(x, y))
1239 redraw_mask |= REDRAW_DOOR_2;
1240 else if (IN_GFX_DOOR_3(x, y))
1241 redraw_mask |= REDRAW_DOOR_3;
1244 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1246 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1248 if (font->bitmap == NULL)
1251 DrawBackground(x, y, width, height);
1254 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1256 struct GraphicInfo *g = &graphic_info[graphic];
1258 if (g->bitmap == NULL)
1261 DrawBackground(x, y, width, height);
1264 static int game_status_last = -1;
1265 static Bitmap *global_border_bitmap_last = NULL;
1266 static Bitmap *global_border_bitmap = NULL;
1267 static int real_sx_last = -1, real_sy_last = -1;
1268 static int full_sxsize_last = -1, full_sysize_last = -1;
1269 static int dx_last = -1, dy_last = -1;
1270 static int dxsize_last = -1, dysize_last = -1;
1271 static int vx_last = -1, vy_last = -1;
1272 static int vxsize_last = -1, vysize_last = -1;
1273 static int ex_last = -1, ey_last = -1;
1274 static int exsize_last = -1, eysize_last = -1;
1276 boolean CheckIfGlobalBorderHasChanged(void)
1278 // if game status has not changed, global border has not changed either
1279 if (game_status == game_status_last)
1282 // determine and store new global border bitmap for current game status
1283 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1285 return (global_border_bitmap_last != global_border_bitmap);
1288 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1290 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1291 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1293 // if game status has not changed, nothing has to be redrawn
1294 if (game_status == game_status_last)
1297 // redraw if last screen was title screen
1298 if (game_status_last == GAME_MODE_TITLE)
1301 // redraw if global screen border has changed
1302 if (CheckIfGlobalBorderHasChanged())
1305 // redraw if position or size of playfield area has changed
1306 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1307 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1310 // redraw if position or size of door area has changed
1311 if (dx_last != DX || dy_last != DY ||
1312 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1315 // redraw if position or size of tape area has changed
1316 if (vx_last != VX || vy_last != VY ||
1317 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1320 // redraw if position or size of editor area has changed
1321 if (ex_last != EX || ey_last != EY ||
1322 exsize_last != EXSIZE || eysize_last != EYSIZE)
1329 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1332 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1334 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1337 void RedrawGlobalBorder(void)
1339 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1341 RedrawGlobalBorderFromBitmap(bitmap);
1343 redraw_mask = REDRAW_ALL;
1346 static void RedrawGlobalBorderIfNeeded(void)
1348 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1349 if (game_status == game_status_last)
1353 // copy current draw buffer to later copy back areas that have not changed
1354 if (game_status_last != GAME_MODE_TITLE)
1355 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1357 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1358 if (CheckIfGlobalBorderRedrawIsNeeded())
1360 // determine and store new global border bitmap for current game status
1361 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1364 // redraw global screen border (or clear, if defined to be empty)
1365 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1367 if (game_status == GAME_MODE_EDITOR)
1368 DrawSpecialEditorDoor();
1370 // copy previous playfield and door areas, if they are defined on both
1371 // previous and current screen and if they still have the same size
1373 if (real_sx_last != -1 && real_sy_last != -1 &&
1374 REAL_SX != -1 && REAL_SY != -1 &&
1375 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1376 BlitBitmap(bitmap_db_store_1, backbuffer,
1377 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1380 if (dx_last != -1 && dy_last != -1 &&
1381 DX != -1 && DY != -1 &&
1382 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1383 BlitBitmap(bitmap_db_store_1, backbuffer,
1384 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1386 if (game_status != GAME_MODE_EDITOR)
1388 if (vx_last != -1 && vy_last != -1 &&
1389 VX != -1 && VY != -1 &&
1390 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1391 BlitBitmap(bitmap_db_store_1, backbuffer,
1392 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1396 if (ex_last != -1 && ey_last != -1 &&
1397 EX != -1 && EY != -1 &&
1398 exsize_last == EXSIZE && eysize_last == EYSIZE)
1399 BlitBitmap(bitmap_db_store_1, backbuffer,
1400 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1403 redraw_mask = REDRAW_ALL;
1406 game_status_last = game_status;
1408 global_border_bitmap_last = global_border_bitmap;
1410 real_sx_last = REAL_SX;
1411 real_sy_last = REAL_SY;
1412 full_sxsize_last = FULL_SXSIZE;
1413 full_sysize_last = FULL_SYSIZE;
1416 dxsize_last = DXSIZE;
1417 dysize_last = DYSIZE;
1420 vxsize_last = VXSIZE;
1421 vysize_last = VYSIZE;
1424 exsize_last = EXSIZE;
1425 eysize_last = EYSIZE;
1428 void ClearField(void)
1430 RedrawGlobalBorderIfNeeded();
1432 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1433 // (when entering hall of fame after playing)
1434 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1436 // !!! maybe this should be done before clearing the background !!!
1437 if (game_status == GAME_MODE_PLAYING)
1439 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1440 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1444 SetDrawtoField(DRAW_TO_BACKBUFFER);
1448 void MarkTileDirty(int x, int y)
1450 redraw_mask |= REDRAW_FIELD;
1453 void SetBorderElement(void)
1457 BorderElement = EL_EMPTY;
1459 // only the R'n'D game engine may use an additional steelwall border
1460 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1463 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1465 for (x = 0; x < lev_fieldx; x++)
1467 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1468 BorderElement = EL_STEELWALL;
1470 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1476 void FloodFillLevelExt(int start_x, int start_y, int fill_element,
1477 int max_array_fieldx, int max_array_fieldy,
1478 short field[max_array_fieldx][max_array_fieldy],
1479 int max_fieldx, int max_fieldy)
1481 static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY];
1482 struct XY *check = xy_topdown;
1483 int old_element = field[start_x][start_y];
1486 // do nothing if start field already has the desired content
1487 if (old_element == fill_element)
1490 stack_buffer[stack_pos++] = (struct XY){ start_x, start_y };
1492 while (stack_pos > 0)
1494 struct XY current = stack_buffer[--stack_pos];
1497 field[current.x][current.y] = fill_element;
1499 for (i = 0; i < 4; i++)
1501 int x = current.x + check[i].x;
1502 int y = current.y + check[i].y;
1504 // check for stack buffer overflow (should not happen)
1505 if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY)
1506 Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug.");
1508 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1509 stack_buffer[stack_pos++] = (struct XY){ x, y };
1514 void FloodFillLevel(int from_x, int from_y, int fill_element,
1515 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1516 int max_fieldx, int max_fieldy)
1518 FloodFillLevelExt(from_x, from_y, fill_element,
1519 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1520 max_fieldx, max_fieldy);
1523 void SetRandomAnimationValue(int x, int y)
1525 gfx.anim_random_frame = GfxRandom[x][y];
1528 void SetAnimationFirstLevel(int first_level)
1530 gfx.anim_first_level = first_level;
1533 int getGraphicAnimationFrame(int graphic, int sync_frame)
1535 // animation synchronized with global frame counter, not move position
1536 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1537 sync_frame = FrameCounter;
1538 else if (graphic_info[graphic].anim_global_anim_sync)
1539 sync_frame = getGlobalAnimSyncFrame();
1541 return getAnimationFrame(graphic_info[graphic].anim_frames,
1542 graphic_info[graphic].anim_delay,
1543 graphic_info[graphic].anim_mode,
1544 graphic_info[graphic].anim_start_frame,
1548 int getGraphicAnimationFrameXY(int graphic, int lx, int ly)
1550 if (graphic_info[graphic].anim_mode & ANIM_TILED)
1552 struct GraphicInfo *g = &graphic_info[graphic];
1553 int xsize = MAX(1, g->anim_frames_per_line);
1554 int ysize = MAX(1, g->anim_frames / xsize);
1555 int xoffset = g->anim_start_frame % xsize;
1556 int yoffset = g->anim_start_frame % ysize;
1557 // may be needed if screen field is significantly larger than playfield
1558 int x = (lx + xoffset + SCR_FIELDX * xsize) % xsize;
1559 int y = (ly + yoffset + SCR_FIELDY * ysize) % ysize;
1560 int sync_frame = y * xsize + x;
1562 return sync_frame % g->anim_frames;
1564 else if (graphic_info[graphic].anim_mode & ANIM_RANDOM_STATIC)
1566 struct GraphicInfo *g = &graphic_info[graphic];
1567 // may be needed if screen field is significantly larger than playfield
1568 int x = (lx + SCR_FIELDX * lev_fieldx) % lev_fieldx;
1569 int y = (ly + SCR_FIELDY * lev_fieldy) % lev_fieldy;
1570 int sync_frame = GfxRandomStatic[x][y];
1572 return sync_frame % g->anim_frames;
1576 int sync_frame = (IN_LEV_FIELD(lx, ly) ? GfxFrame[lx][ly] : -1);
1578 return getGraphicAnimationFrame(graphic, sync_frame);
1582 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1584 struct GraphicInfo *g = &graphic_info[graphic];
1585 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1587 if (tilesize == gfx.standard_tile_size)
1588 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1589 else if (tilesize == game.tile_size)
1590 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1592 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1595 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1596 boolean get_backside)
1598 struct GraphicInfo *g = &graphic_info[graphic];
1599 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1600 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1602 if (g->offset_y == 0) // frames are ordered horizontally
1604 int max_width = g->anim_frames_per_line * g->width;
1605 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1607 *x = pos % max_width;
1608 *y = src_y % g->height + pos / max_width * g->height;
1610 else if (g->offset_x == 0) // frames are ordered vertically
1612 int max_height = g->anim_frames_per_line * g->height;
1613 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1615 *x = src_x % g->width + pos / max_height * g->width;
1616 *y = pos % max_height;
1618 else // frames are ordered diagonally
1620 *x = src_x + frame * g->offset_x;
1621 *y = src_y + frame * g->offset_y;
1625 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1626 Bitmap **bitmap, int *x, int *y,
1627 boolean get_backside)
1629 struct GraphicInfo *g = &graphic_info[graphic];
1631 // if no graphics defined at all, use fallback graphics
1632 if (g->bitmaps == NULL)
1633 *g = graphic_info[IMG_CHAR_EXCLAM];
1635 // if no in-game graphics defined, always use standard graphic size
1636 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1637 tilesize = TILESIZE;
1639 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1640 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1642 *x = *x * tilesize / g->tile_size;
1643 *y = *y * tilesize / g->tile_size;
1646 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1647 Bitmap **bitmap, int *x, int *y)
1649 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1652 void getFixedGraphicSource(int graphic, int frame,
1653 Bitmap **bitmap, int *x, int *y)
1655 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1658 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1660 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1663 void getGlobalAnimGraphicSource(int graphic, int frame,
1664 Bitmap **bitmap, int *x, int *y)
1666 struct GraphicInfo *g = &graphic_info[graphic];
1668 // if no graphics defined at all, use fallback graphics
1669 if (g->bitmaps == NULL)
1670 *g = graphic_info[IMG_CHAR_EXCLAM];
1672 // use original size graphics, if existing, else use standard size graphics
1673 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1674 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1676 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1678 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1681 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1682 int *x, int *y, boolean get_backside)
1684 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1688 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1690 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1693 void DrawGraphic(int x, int y, int graphic, int frame)
1696 if (!IN_SCR_FIELD(x, y))
1698 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1699 Debug("draw:DrawGraphic", "This should never happen!");
1705 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1708 MarkTileDirty(x, y);
1711 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1714 if (!IN_SCR_FIELD(x, y))
1716 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1718 Debug("draw:DrawFixedGraphic", "This should never happen!");
1724 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1726 MarkTileDirty(x, y);
1729 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1735 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1737 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1740 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1746 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1747 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1750 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1753 if (!IN_SCR_FIELD(x, y))
1755 Debug("draw:DrawGraphicThruMask", "x = %d, y = %d, graphic = %d",
1757 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1763 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1766 MarkTileDirty(x, y);
1769 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1772 if (!IN_SCR_FIELD(x, y))
1774 Debug("draw:DrawFixedGraphicThruMask", "x = %d, y = %d, graphic = %d",
1776 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1782 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1784 MarkTileDirty(x, y);
1787 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1793 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1795 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1799 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1800 int graphic, int frame)
1805 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1807 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1811 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1813 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1815 MarkTileDirty(x / tilesize, y / tilesize);
1818 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1821 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1822 graphic, frame, tilesize);
1823 MarkTileDirty(x / tilesize, y / tilesize);
1826 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1832 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1833 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1836 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1837 int frame, int tilesize)
1842 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1843 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1846 void DrawMiniGraphic(int x, int y, int graphic)
1848 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX, SY + y * MINI_TILEY, graphic);
1849 MarkTileDirty(x / 2, y / 2);
1852 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1857 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1858 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1861 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1862 int graphic, int frame,
1863 int cut_mode, int mask_mode)
1868 int width = TILEX, height = TILEY;
1871 if (dx || dy) // shifted graphic
1873 if (x < BX1) // object enters playfield from the left
1880 else if (x > BX2) // object enters playfield from the right
1886 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1892 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1894 else if (dx) // general horizontal movement
1895 MarkTileDirty(x + SIGN(dx), y);
1897 if (y < BY1) // object enters playfield from the top
1899 if (cut_mode == CUT_BELOW) // object completely above top border
1907 else if (y > BY2) // object enters playfield from the bottom
1913 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1919 else if (dy > 0 && cut_mode == CUT_ABOVE)
1921 if (y == BY2) // object completely above bottom border
1927 MarkTileDirty(x, y + 1);
1928 } // object leaves playfield to the bottom
1929 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1931 else if (dy) // general vertical movement
1932 MarkTileDirty(x, y + SIGN(dy));
1936 if (!IN_SCR_FIELD(x, y))
1938 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1940 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1946 width = width * TILESIZE_VAR / TILESIZE;
1947 height = height * TILESIZE_VAR / TILESIZE;
1948 cx = cx * TILESIZE_VAR / TILESIZE;
1949 cy = cy * TILESIZE_VAR / TILESIZE;
1950 dx = dx * TILESIZE_VAR / TILESIZE;
1951 dy = dy * TILESIZE_VAR / TILESIZE;
1953 if (width > 0 && height > 0)
1955 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1960 dst_x = FX + x * TILEX_VAR + dx;
1961 dst_y = FY + y * TILEY_VAR + dy;
1963 if (mask_mode == USE_MASKING)
1964 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1967 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1970 MarkTileDirty(x, y);
1974 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1975 int graphic, int frame,
1976 int cut_mode, int mask_mode)
1981 int width = TILEX_VAR, height = TILEY_VAR;
1984 int x2 = x + SIGN(dx);
1985 int y2 = y + SIGN(dy);
1987 // movement with two-tile animations must be sync'ed with movement position,
1988 // not with current GfxFrame (which can be higher when using slow movement)
1989 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1990 int anim_frames = graphic_info[graphic].anim_frames;
1992 // (we also need anim_delay here for movement animations with less frames)
1993 int anim_delay = graphic_info[graphic].anim_delay;
1994 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1996 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1997 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1999 // re-calculate animation frame for two-tile movement animation
2000 frame = getGraphicAnimationFrame(graphic, sync_frame);
2002 // check if movement start graphic inside screen area and should be drawn
2003 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
2005 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
2007 dst_x = FX + x1 * TILEX_VAR;
2008 dst_y = FY + y1 * TILEY_VAR;
2010 if (mask_mode == USE_MASKING)
2011 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
2014 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
2017 MarkTileDirty(x1, y1);
2020 // check if movement end graphic inside screen area and should be drawn
2021 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
2023 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
2025 dst_x = FX + x2 * TILEX_VAR;
2026 dst_y = FY + y2 * TILEY_VAR;
2028 if (mask_mode == USE_MASKING)
2029 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
2032 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
2035 MarkTileDirty(x2, y2);
2039 static void DrawGraphicShifted(int x, int y, int dx, int dy,
2040 int graphic, int frame,
2041 int cut_mode, int mask_mode)
2045 DrawGraphic(x, y, graphic, frame);
2050 if (graphic_info[graphic].double_movement) // EM style movement images
2051 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2053 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2056 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
2057 int graphic, int frame, int cut_mode)
2059 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
2062 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
2063 int cut_mode, int mask_mode)
2065 int lx = LEVELX(x), ly = LEVELY(y);
2069 if (IN_LEV_FIELD(lx, ly))
2071 if (element == EL_EMPTY)
2072 element = GfxElementEmpty[lx][ly];
2074 SetRandomAnimationValue(lx, ly);
2076 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2077 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2079 // do not use double (EM style) movement graphic when not moving
2080 if (graphic_info[graphic].double_movement && !dx && !dy)
2082 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2083 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2086 if (game.use_masked_elements && (dx || dy))
2087 mask_mode = USE_MASKING;
2089 else // border element
2091 graphic = el2img(element);
2092 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2095 if (element == EL_EXPANDABLE_WALL)
2097 boolean left_stopped = FALSE, right_stopped = FALSE;
2099 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2100 left_stopped = TRUE;
2101 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2102 right_stopped = TRUE;
2104 if (left_stopped && right_stopped)
2106 else if (left_stopped)
2108 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2109 frame = graphic_info[graphic].anim_frames - 1;
2111 else if (right_stopped)
2113 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2114 frame = graphic_info[graphic].anim_frames - 1;
2119 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2120 else if (mask_mode == USE_MASKING)
2121 DrawGraphicThruMask(x, y, graphic, frame);
2123 DrawGraphic(x, y, graphic, frame);
2126 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2127 int cut_mode, int mask_mode)
2129 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2130 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2131 cut_mode, mask_mode);
2134 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2137 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2140 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2143 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2146 void DrawLevelElementThruMask(int x, int y, int element)
2148 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2151 void DrawLevelFieldThruMask(int x, int y)
2153 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2156 // !!! implementation of quicksand is totally broken !!!
2157 #define IS_CRUMBLED_TILE(x, y, e) \
2158 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2159 !IS_MOVING(x, y) || \
2160 (e) == EL_QUICKSAND_EMPTYING || \
2161 (e) == EL_QUICKSAND_FAST_EMPTYING))
2163 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2168 int width, height, cx, cy;
2169 int sx = SCREENX(x), sy = SCREENY(y);
2170 int crumbled_border_size = graphic_info[graphic].border_size;
2171 int crumbled_tile_size = graphic_info[graphic].tile_size;
2172 int crumbled_border_size_var =
2173 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2176 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2178 for (i = 1; i < 4; i++)
2180 int dxx = (i & 1 ? dx : 0);
2181 int dyy = (i & 2 ? dy : 0);
2184 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2187 // check if neighbour field is of same crumble type
2188 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2189 graphic_info[graphic].class ==
2190 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2192 // return if check prevents inner corner
2193 if (same == (dxx == dx && dyy == dy))
2197 // if we reach this point, we have an inner corner
2199 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2201 width = crumbled_border_size_var;
2202 height = crumbled_border_size_var;
2203 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2204 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2206 if (game.use_masked_elements)
2208 int graphic0 = el2img(EL_EMPTY);
2209 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2210 Bitmap *src_bitmap0;
2213 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2215 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2217 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2219 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2221 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2224 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2226 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2229 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2234 int width, height, bx, by, cx, cy;
2235 int sx = SCREENX(x), sy = SCREENY(y);
2236 int crumbled_border_size = graphic_info[graphic].border_size;
2237 int crumbled_tile_size = graphic_info[graphic].tile_size;
2238 int crumbled_border_size_var =
2239 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2240 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2243 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2245 // only needed when using masked elements
2246 int graphic0 = el2img(EL_EMPTY);
2247 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2248 Bitmap *src_bitmap0;
2251 if (game.use_masked_elements)
2252 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2254 // draw simple, sloppy, non-corner-accurate crumbled border
2256 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2257 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2258 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2259 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2261 if (game.use_masked_elements)
2263 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2265 FX + sx * TILEX_VAR + cx,
2266 FY + sy * TILEY_VAR + cy);
2268 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2270 FX + sx * TILEX_VAR + cx,
2271 FY + sy * TILEY_VAR + cy);
2274 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2276 FX + sx * TILEX_VAR + cx,
2277 FY + sy * TILEY_VAR + cy);
2279 // (remaining middle border part must be at least as big as corner part)
2280 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2281 crumbled_border_size_var >= TILESIZE_VAR / 3)
2284 // correct corners of crumbled border, if needed
2286 for (i = -1; i <= 1; i += 2)
2288 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2289 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2290 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2293 // check if neighbour field is of same crumble type
2294 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2295 graphic_info[graphic].class ==
2296 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2298 // no crumbled corner, but continued crumbled border
2300 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2301 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2302 int b1 = (i == 1 ? crumbled_border_size_var :
2303 TILESIZE_VAR - 2 * crumbled_border_size_var);
2305 width = crumbled_border_size_var;
2306 height = crumbled_border_size_var;
2308 if (dir == 1 || dir == 2)
2323 if (game.use_masked_elements)
2325 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2327 FX + sx * TILEX_VAR + cx,
2328 FY + sy * TILEY_VAR + cy);
2330 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2332 FX + sx * TILEX_VAR + cx,
2333 FY + sy * TILEY_VAR + cy);
2336 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2338 FX + sx * TILEX_VAR + cx,
2339 FY + sy * TILEY_VAR + cy);
2344 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2346 int sx = SCREENX(x), sy = SCREENY(y);
2349 struct XY *xy = xy_topdown;
2351 if (!IN_LEV_FIELD(x, y))
2354 element = TILE_GFX_ELEMENT(x, y);
2356 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2358 if (!IN_SCR_FIELD(sx, sy))
2361 // crumble field borders towards direct neighbour fields
2362 for (i = 0; i < 4; i++)
2364 int xx = x + xy[i].x;
2365 int yy = y + xy[i].y;
2367 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2370 // check if neighbour field is of same crumble type
2371 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2372 graphic_info[graphic].class ==
2373 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2376 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2379 // crumble inner field corners towards corner neighbour fields
2380 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2381 graphic_info[graphic].anim_frames == 2)
2383 for (i = 0; i < 4; i++)
2385 int dx = (i & 1 ? +1 : -1);
2386 int dy = (i & 2 ? +1 : -1);
2388 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2392 MarkTileDirty(sx, sy);
2394 else // center field is not crumbled -- crumble neighbour fields
2396 // crumble field borders of direct neighbour fields
2397 for (i = 0; i < 4; i++)
2399 int xx = x + xy[i].x;
2400 int yy = y + xy[i].y;
2401 int sxx = sx + xy[i].x;
2402 int syy = sy + xy[i].y;
2404 if (!IN_LEV_FIELD(xx, yy) ||
2405 !IN_SCR_FIELD(sxx, syy))
2408 // do not crumble fields that are being digged or snapped
2409 if (Tile[xx][yy] == EL_EMPTY ||
2410 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2413 element = TILE_GFX_ELEMENT(xx, yy);
2415 if (!IS_CRUMBLED_TILE(xx, yy, element))
2418 graphic = el_act2crm(element, ACTION_DEFAULT);
2420 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2422 MarkTileDirty(sxx, syy);
2425 // crumble inner field corners of corner neighbour fields
2426 for (i = 0; i < 4; i++)
2428 int dx = (i & 1 ? +1 : -1);
2429 int dy = (i & 2 ? +1 : -1);
2435 if (!IN_LEV_FIELD(xx, yy) ||
2436 !IN_SCR_FIELD(sxx, syy))
2439 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2442 element = TILE_GFX_ELEMENT(xx, yy);
2444 if (!IS_CRUMBLED_TILE(xx, yy, element))
2447 graphic = el_act2crm(element, ACTION_DEFAULT);
2449 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2450 graphic_info[graphic].anim_frames == 2)
2451 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2453 MarkTileDirty(sxx, syy);
2458 void DrawLevelFieldCrumbled(int x, int y)
2462 if (!IN_LEV_FIELD(x, y))
2465 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2466 GfxElement[x][y] != EL_UNDEFINED &&
2467 GFX_CRUMBLED(GfxElement[x][y]))
2469 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2474 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2476 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2479 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2482 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2483 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2484 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2485 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2486 int sx = SCREENX(x), sy = SCREENY(y);
2488 DrawScreenGraphic(sx, sy, graphic1, frame1);
2489 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2492 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2494 int sx = SCREENX(x), sy = SCREENY(y);
2495 struct XY *xy = xy_topdown;
2498 // crumble direct neighbour fields (required for field borders)
2499 for (i = 0; i < 4; i++)
2501 int xx = x + xy[i].x;
2502 int yy = y + xy[i].y;
2503 int sxx = sx + xy[i].x;
2504 int syy = sy + xy[i].y;
2506 if (!IN_LEV_FIELD(xx, yy) ||
2507 !IN_SCR_FIELD(sxx, syy) ||
2508 !GFX_CRUMBLED(Tile[xx][yy]) ||
2512 DrawLevelField(xx, yy);
2515 // crumble corner neighbour fields (required for inner field corners)
2516 for (i = 0; i < 4; i++)
2518 int dx = (i & 1 ? +1 : -1);
2519 int dy = (i & 2 ? +1 : -1);
2525 if (!IN_LEV_FIELD(xx, yy) ||
2526 !IN_SCR_FIELD(sxx, syy) ||
2527 !GFX_CRUMBLED(Tile[xx][yy]) ||
2531 int element = TILE_GFX_ELEMENT(xx, yy);
2532 int graphic = el_act2crm(element, ACTION_DEFAULT);
2534 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2535 graphic_info[graphic].anim_frames == 2)
2536 DrawLevelField(xx, yy);
2540 static int getBorderElement(int x, int y)
2544 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2545 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2546 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2547 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2548 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2549 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2550 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2552 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2553 int steel_position = (x == -1 && y == -1 ? 0 :
2554 x == lev_fieldx && y == -1 ? 1 :
2555 x == -1 && y == lev_fieldy ? 2 :
2556 x == lev_fieldx && y == lev_fieldy ? 3 :
2557 x == -1 || x == lev_fieldx ? 4 :
2558 y == -1 || y == lev_fieldy ? 5 : 6);
2560 return border[steel_position][steel_type];
2563 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2565 if (game.use_masked_elements)
2567 if (graphic != el2img(EL_EMPTY))
2568 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2570 DrawGraphicThruMask(x, y, graphic, frame);
2574 DrawGraphic(x, y, graphic, frame);
2578 void DrawLevelGraphic(int x, int y, int graphic, int frame)
2580 DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2583 void DrawScreenElement(int x, int y, int element)
2585 int mask_mode = NO_MASKING;
2587 if (game.use_masked_elements)
2589 int lx = LEVELX(x), ly = LEVELY(y);
2591 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2593 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2595 mask_mode = USE_MASKING;
2599 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2600 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2603 void DrawLevelElement(int x, int y, int element)
2605 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2606 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2609 void DrawScreenField(int x, int y)
2611 int lx = LEVELX(x), ly = LEVELY(y);
2612 int element, content;
2614 if (!IN_LEV_FIELD(lx, ly))
2616 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2619 element = getBorderElement(lx, ly);
2621 DrawScreenElement(x, y, element);
2626 element = Tile[lx][ly];
2627 content = Store[lx][ly];
2629 if (IS_MOVING(lx, ly))
2631 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2632 boolean cut_mode = NO_CUTTING;
2634 if (element == EL_QUICKSAND_EMPTYING ||
2635 element == EL_QUICKSAND_FAST_EMPTYING ||
2636 element == EL_MAGIC_WALL_EMPTYING ||
2637 element == EL_BD_MAGIC_WALL_EMPTYING ||
2638 element == EL_DC_MAGIC_WALL_EMPTYING ||
2639 element == EL_AMOEBA_DROPPING)
2640 cut_mode = CUT_ABOVE;
2641 else if (element == EL_QUICKSAND_FILLING ||
2642 element == EL_QUICKSAND_FAST_FILLING ||
2643 element == EL_MAGIC_WALL_FILLING ||
2644 element == EL_BD_MAGIC_WALL_FILLING ||
2645 element == EL_DC_MAGIC_WALL_FILLING)
2646 cut_mode = CUT_BELOW;
2648 if (cut_mode == CUT_ABOVE)
2649 DrawScreenElement(x, y, element);
2651 DrawScreenElement(x, y, EL_EMPTY);
2653 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2655 int dir = MovDir[lx][ly];
2656 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2657 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2659 if (IN_SCR_FIELD(newx, newy))
2660 DrawScreenElement(newx, newy, EL_EMPTY);
2664 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2665 else if (cut_mode == NO_CUTTING)
2666 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2669 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2671 if (cut_mode == CUT_BELOW &&
2672 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2673 DrawLevelElement(lx, ly + 1, element);
2676 if (content == EL_ACID)
2678 int dir = MovDir[lx][ly];
2679 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2680 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2682 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2684 // prevent target field from being drawn again (but without masking)
2685 // (this would happen if target field is scanned after moving element)
2686 Stop[newlx][newly] = TRUE;
2689 else if (IS_BLOCKED(lx, ly))
2694 boolean cut_mode = NO_CUTTING;
2695 int element_old, content_old;
2697 Blocked2Moving(lx, ly, &oldx, &oldy);
2700 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2701 MovDir[oldx][oldy] == MV_RIGHT);
2703 element_old = Tile[oldx][oldy];
2704 content_old = Store[oldx][oldy];
2706 if (element_old == EL_QUICKSAND_EMPTYING ||
2707 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2708 element_old == EL_MAGIC_WALL_EMPTYING ||
2709 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2710 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2711 element_old == EL_AMOEBA_DROPPING)
2712 cut_mode = CUT_ABOVE;
2714 DrawScreenElement(x, y, EL_EMPTY);
2717 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2719 else if (cut_mode == NO_CUTTING)
2720 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2723 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2726 else if (IS_DRAWABLE(element))
2727 DrawScreenElement(x, y, element);
2729 DrawScreenElement(x, y, EL_EMPTY);
2732 void DrawLevelField(int x, int y)
2734 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2735 DrawScreenField(SCREENX(x), SCREENY(y));
2736 else if (IS_MOVING(x, y))
2740 Moving2Blocked(x, y, &newx, &newy);
2741 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2742 DrawScreenField(SCREENX(newx), SCREENY(newy));
2744 else if (IS_BLOCKED(x, y))
2748 Blocked2Moving(x, y, &oldx, &oldy);
2749 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2750 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2754 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2755 int (*el2img_function)(int), boolean masked,
2756 int element_bits_draw)
2758 int element_base = map_mm_wall_element(element);
2759 int element_bits = (IS_DF_WALL(element) ?
2760 element - EL_DF_WALL_START :
2761 IS_MM_WALL(element) ?
2762 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2763 int graphic = el2img_function(element_base);
2764 int tilesize_draw = tilesize / 2;
2769 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2771 for (i = 0; i < 4; i++)
2773 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2774 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2776 if (!(element_bits_draw & (1 << i)))
2779 if (element_bits & (1 << i))
2782 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2783 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2785 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2786 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2791 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2792 tilesize_draw, tilesize_draw);
2797 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2798 boolean masked, int element_bits_draw)
2800 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2801 element, tilesize, el2edimg, masked, element_bits_draw);
2804 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2805 int (*el2img_function)(int))
2807 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2811 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2814 if (IS_MM_WALL(element))
2816 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2817 element, tilesize, el2edimg, masked, 0x000f);
2823 el2edimg_with_frame(element, &graphic, &frame);
2826 DrawSizedGraphicThruMask(x, y, graphic, frame, tilesize);
2828 DrawSizedGraphic(x, y, graphic, frame, tilesize);
2832 void DrawSizedElement(int x, int y, int element, int tilesize)
2834 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2837 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2839 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2842 void DrawMiniElement(int x, int y, int element)
2846 graphic = el2edimg(element);
2847 DrawMiniGraphic(x, y, graphic);
2850 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2853 int x = sx + scroll_x, y = sy + scroll_y;
2855 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2856 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2857 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2858 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2860 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2863 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2865 int x = sx + scroll_x, y = sy + scroll_y;
2867 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2868 DrawMiniElement(sx, sy, EL_EMPTY);
2869 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2870 DrawMiniElement(sx, sy, Tile[x][y]);
2872 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2875 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2876 int x, int y, int xsize, int ysize,
2877 int tile_width, int tile_height)
2881 int dst_x = startx + x * tile_width;
2882 int dst_y = starty + y * tile_height;
2883 int width = graphic_info[graphic].width;
2884 int height = graphic_info[graphic].height;
2885 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2886 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2887 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2888 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2889 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2890 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2891 boolean draw_masked = graphic_info[graphic].draw_masked;
2893 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2895 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2897 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2901 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2902 inner_sx + (x - 1) * tile_width % inner_width);
2903 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2904 inner_sy + (y - 1) * tile_height % inner_height);
2907 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2910 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2914 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2915 int x, int y, int xsize, int ysize,
2918 int font_width = getFontWidth(font_nr);
2919 int font_height = getFontHeight(font_nr);
2921 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2922 font_width, font_height);
2925 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2927 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2928 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2929 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2930 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2931 boolean no_delay = (tape.warp_forward);
2932 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2933 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2934 DelayCounter anim_delay = { anim_delay_value };
2935 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2936 int font_width = getFontWidth(font_nr);
2937 int font_height = getFontHeight(font_nr);
2938 int max_xsize = level.envelope[envelope_nr].xsize;
2939 int max_ysize = level.envelope[envelope_nr].ysize;
2940 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2941 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2942 int xend = max_xsize;
2943 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2944 int xstep = (xstart < xend ? 1 : 0);
2945 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2947 int end = MAX(xend - xstart, yend - ystart);
2950 for (i = start; i <= end; i++)
2952 int last_frame = end; // last frame of this "for" loop
2953 int x = xstart + i * xstep;
2954 int y = ystart + i * ystep;
2955 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2956 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2957 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2958 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2961 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2963 BlitScreenToBitmap(backbuffer);
2965 SetDrawtoField(DRAW_TO_BACKBUFFER);
2967 for (yy = 0; yy < ysize; yy++)
2968 for (xx = 0; xx < xsize; xx++)
2969 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2971 DrawTextArea(sx + font_width, sy + font_height,
2972 level.envelope[envelope_nr].text, font_nr, max_xsize,
2973 xsize - 2, ysize - 2, 0, mask_mode,
2974 level.envelope[envelope_nr].autowrap,
2975 level.envelope[envelope_nr].centered, FALSE);
2977 redraw_mask |= REDRAW_FIELD;
2980 SkipUntilDelayReached(&anim_delay, &i, last_frame);
2983 ClearAutoRepeatKeyEvents();
2986 void ShowEnvelope(int envelope_nr)
2988 int element = EL_ENVELOPE_1 + envelope_nr;
2989 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2990 int sound_opening = element_info[element].sound[ACTION_OPENING];
2991 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2992 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2993 boolean no_delay = (tape.warp_forward);
2994 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2995 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2996 int anim_mode = graphic_info[graphic].anim_mode;
2997 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2998 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2999 boolean overlay_enabled = GetOverlayEnabled();
3001 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3003 SetOverlayEnabled(FALSE);
3006 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3008 if (anim_mode == ANIM_DEFAULT)
3009 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
3011 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
3014 Delay_WithScreenUpdates(wait_delay_value);
3016 WaitForEventToContinue();
3019 SetOverlayEnabled(overlay_enabled);
3021 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3023 if (anim_mode != ANIM_NONE)
3024 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
3026 if (anim_mode == ANIM_DEFAULT)
3027 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
3029 game.envelope_active = FALSE;
3031 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3033 redraw_mask |= REDRAW_FIELD;
3037 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
3038 int xsize, int ysize)
3040 if (!global.use_envelope_request)
3043 if (request.bitmap == NULL ||
3044 xsize > request.xsize ||
3045 ysize > request.ysize)
3047 if (request.bitmap != NULL)
3048 FreeBitmap(request.bitmap);
3050 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
3052 SDL_Surface *surface = request.bitmap->surface;
3054 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
3055 Fail("SDLGetNativeSurface() failed");
3058 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3060 // create masked surface for request bitmap, if needed
3061 if (graphic_info[IMG_BACKGROUND_REQUEST].draw_masked)
3063 SDL_Surface *surface = request.bitmap->surface;
3064 SDL_Surface *surface_masked = request.bitmap->surface_masked;
3066 SDLBlitSurface(surface, surface_masked, 0, 0, xsize, ysize, 0, 0);
3067 SDL_SetColorKey(surface_masked, SET_TRANSPARENT_PIXEL,
3068 SDL_MapRGB(surface_masked->format, 0x00, 0x00, 0x00));
3071 SDLFreeBitmapTextures(request.bitmap);
3072 SDLCreateBitmapTextures(request.bitmap);
3074 ResetBitmapAlpha(request.bitmap);
3076 // set envelope request run-time values
3079 request.xsize = xsize;
3080 request.ysize = ysize;
3083 void DrawEnvelopeRequestToScreen(int drawing_target)
3085 if (global.use_envelope_request &&
3086 game.request_active &&
3087 drawing_target == DRAW_TO_SCREEN)
3089 struct GraphicInfo *g = &graphic_info[IMG_BACKGROUND_REQUEST];
3091 SetBitmapAlphaNextBlit(request.bitmap, g->alpha);
3094 BlitToScreenMasked(request.bitmap, 0, 0, request.xsize, request.ysize,
3095 request.sx, request.sy);
3097 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3098 request.sx, request.sy);
3102 static void setRequestBasePosition(int *x, int *y)
3104 int sx_base, sy_base;
3106 if (request.x != -1)
3107 sx_base = request.x;
3108 else if (request.align == ALIGN_LEFT)
3110 else if (request.align == ALIGN_RIGHT)
3111 sx_base = SX + SXSIZE;
3113 sx_base = SX + SXSIZE / 2;
3115 if (request.y != -1)
3116 sy_base = request.y;
3117 else if (request.valign == VALIGN_TOP)
3119 else if (request.valign == VALIGN_BOTTOM)
3120 sy_base = SY + SYSIZE;
3122 sy_base = SY + SYSIZE / 2;
3128 static void setRequestPositionExt(int *x, int *y, int width, int height,
3129 boolean add_border_size)
3131 int border_size = request.border_size;
3132 int sx_base, sy_base;
3135 setRequestBasePosition(&sx_base, &sy_base);
3137 if (request.align == ALIGN_LEFT)
3139 else if (request.align == ALIGN_RIGHT)
3140 sx = sx_base - width;
3142 sx = sx_base - width / 2;
3144 if (request.valign == VALIGN_TOP)
3146 else if (request.valign == VALIGN_BOTTOM)
3147 sy = sy_base - height;
3149 sy = sy_base - height / 2;
3151 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3152 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3154 if (add_border_size)
3164 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3166 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3169 static void DrawEnvelopeRequestText(int sx, int sy, char *text)
3171 char *text_final = text;
3172 char *text_door_style = NULL;
3173 int graphic = IMG_BACKGROUND_REQUEST;
3174 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3175 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3176 int font_nr = FONT_REQUEST;
3177 int font_width = getFontWidth(font_nr);
3178 int font_height = getFontHeight(font_nr);
3179 int border_size = request.border_size;
3180 int line_spacing = request.line_spacing;
3181 int line_height = font_height + line_spacing;
3182 int max_text_width = request.width - 2 * border_size;
3183 int max_text_height = request.height - 2 * border_size;
3184 int line_length = max_text_width / font_width;
3185 int max_lines = max_text_height / line_height;
3186 int text_width = line_length * font_width;
3187 int sx_offset = border_size;
3188 int sy_offset = border_size;
3190 // force DOOR font inside door area
3191 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3193 if (request.centered)
3194 sx_offset = (request.width - text_width) / 2;
3196 if (request.wrap_single_words && !request.autowrap)
3198 char *src_text_ptr, *dst_text_ptr;
3200 if (maxWordLengthInRequestString(text) > line_length)
3202 font_nr = FONT_REQUEST_NARROW;
3203 font_width = getFontWidth(font_nr);
3204 line_length = max_text_width / font_width;
3207 text_door_style = checked_malloc(2 * strlen(text) + 1);
3209 src_text_ptr = text;
3210 dst_text_ptr = text_door_style;
3212 while (*src_text_ptr)
3214 if (*src_text_ptr == ' ' ||
3215 *src_text_ptr == '?' ||
3216 *src_text_ptr == '!')
3217 *dst_text_ptr++ = '\n';
3219 if (*src_text_ptr != ' ')
3220 *dst_text_ptr++ = *src_text_ptr;
3225 *dst_text_ptr = '\0';
3227 text_final = text_door_style;
3230 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3231 line_length, -1, max_lines, line_spacing, mask_mode,
3232 request.autowrap, request.centered, FALSE);
3234 if (text_door_style)
3235 free(text_door_style);
3240 static void DrawEnvelopeRequest(char *text, unsigned int req_state)
3242 DrawBuffer *drawto_last = drawto;
3243 int graphic = IMG_BACKGROUND_REQUEST;
3244 int width = request.width;
3245 int height = request.height;
3246 int tile_size = MAX(request.step_offset, 1);
3247 int x_steps = width / tile_size;
3248 int y_steps = height / tile_size;
3252 setRequestPosition(&sx, &sy, FALSE);
3254 // draw complete envelope request to temporary bitmap
3255 drawto = bitmap_db_store_1;
3257 ClearRectangle(drawto, sx, sy, width, height);
3259 for (y = 0; y < y_steps; y++)
3260 for (x = 0; x < x_steps; x++)
3261 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3262 x, y, x_steps, y_steps,
3263 tile_size, tile_size);
3265 // write text for request
3266 DrawEnvelopeRequestText(sx, sy, text);
3268 MapToolButtons(req_state);
3270 // restore pointer to drawing buffer
3271 drawto = drawto_last;
3273 // prepare complete envelope request from temporary bitmap
3274 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy, width, height);
3277 static void AnimateEnvelopeRequest(int anim_mode, int action)
3279 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
3280 int delay_value_normal = request.step_delay;
3281 int delay_value_fast = delay_value_normal / 2;
3282 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3283 boolean no_delay = (tape.warp_forward);
3284 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3285 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value) / 2);
3286 DelayCounter anim_delay = { anim_delay_value };
3288 int tile_size = MAX(request.step_offset, 1);
3289 int max_xsize = request.width / tile_size;
3290 int max_ysize = request.height / tile_size;
3291 int max_xsize_inner = max_xsize - 2;
3292 int max_ysize_inner = max_ysize - 2;
3294 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3295 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3296 int xend = max_xsize_inner;
3297 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3298 int xstep = (xstart < xend ? 1 : 0);
3299 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3301 int end = MAX(xend - xstart, yend - ystart);
3304 if (setup.quick_doors)
3311 for (i = start; i <= end; i++)
3313 int last_frame = end; // last frame of this "for" loop
3314 int x = xstart + i * xstep;
3315 int y = ystart + i * ystep;
3316 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3317 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3318 int xsize_size_left = (xsize - 1) * tile_size;
3319 int ysize_size_top = (ysize - 1) * tile_size;
3320 int max_xsize_pos = (max_xsize - 1) * tile_size;
3321 int max_ysize_pos = (max_ysize - 1) * tile_size;
3322 int width = xsize * tile_size;
3323 int height = ysize * tile_size;
3329 HandleGameActions();
3331 setRequestPosition(&src_x, &src_y, FALSE);
3332 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3334 for (yy = 0; yy < 2; yy++)
3336 for (xx = 0; xx < 2; xx++)
3338 int src_xx = src_x + xx * max_xsize_pos;
3339 int src_yy = src_y + yy * max_ysize_pos;
3340 int dst_xx = dst_x + xx * xsize_size_left;
3341 int dst_yy = dst_y + yy * ysize_size_top;
3342 int xx_size = (xx ? tile_size : xsize_size_left);
3343 int yy_size = (yy ? tile_size : ysize_size_top);
3345 // draw partial (animated) envelope request to temporary bitmap
3346 BlitBitmap(bitmap_db_store_1, bitmap_db_store_2,
3347 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3351 // prepare partial (animated) envelope request from temporary bitmap
3352 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, dst_x, dst_y,
3355 redraw_mask |= REDRAW_FIELD;
3359 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3362 ClearAutoRepeatKeyEvents();
3365 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3367 int graphic = IMG_BACKGROUND_REQUEST;
3368 int sound_opening = SND_REQUEST_OPENING;
3369 int sound_closing = SND_REQUEST_CLOSING;
3370 int anim_mode_1 = request.anim_mode; // (higher priority)
3371 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3372 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3373 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3374 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3376 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3378 if (action == ACTION_OPENING)
3380 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3382 if (anim_mode == ANIM_DEFAULT)
3383 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3385 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3389 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3391 if (anim_mode != ANIM_NONE)
3392 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3394 if (anim_mode == ANIM_DEFAULT)
3395 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3398 game.envelope_active = FALSE;
3401 static Bitmap *GetPreviewTileBitmap(Bitmap *bitmap)
3403 if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
3404 return GetPreviewTileBitmap_BD(bitmap);
3409 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3411 if (IS_MM_WALL(element))
3413 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3419 int graphic = el2preimg(element);
3421 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3423 // for BD style levels, maybe use bitmap with level-specific colors
3424 src_bitmap = GetPreviewTileBitmap(src_bitmap);
3426 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3431 void DrawLevel(int draw_background_mask)
3435 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3436 SetDrawBackgroundMask(draw_background_mask);
3440 for (x = BX1; x <= BX2; x++)
3441 for (y = BY1; y <= BY2; y++)
3442 DrawScreenField(x, y);
3444 redraw_mask |= REDRAW_FIELD;
3447 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3452 for (x = 0; x < size_x; x++)
3453 for (y = 0; y < size_y; y++)
3454 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3456 redraw_mask |= REDRAW_FIELD;
3459 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3463 for (x = 0; x < size_x; x++)
3464 for (y = 0; y < size_y; y++)
3465 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3467 redraw_mask |= REDRAW_FIELD;
3470 static int getPreviewLevelWidth(void)
3472 if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
3473 return (level.native_bd_level->cave->x2 - level.native_bd_level->cave->x1 + 1);
3478 static int getPreviewLevelHeight(void)
3480 if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
3481 return (level.native_bd_level->cave->y2 - level.native_bd_level->cave->y1 + 1);
3486 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3488 boolean show_level_border = (BorderElement != EL_EMPTY);
3489 int level_xsize = getPreviewLevelWidth() + (show_level_border ? 2 : 0);
3490 int level_ysize = getPreviewLevelHeight() + (show_level_border ? 2 : 0);
3491 int tile_size = preview.tile_size;
3492 int preview_width = preview.xsize * tile_size;
3493 int preview_height = preview.ysize * tile_size;
3494 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3495 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3496 int real_preview_width = real_preview_xsize * tile_size;
3497 int real_preview_height = real_preview_ysize * tile_size;
3498 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3499 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3502 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3505 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3507 dst_x += (preview_width - real_preview_width) / 2;
3508 dst_y += (preview_height - real_preview_height) / 2;
3510 for (x = 0; x < real_preview_xsize; x++)
3512 for (y = 0; y < real_preview_ysize; y++)
3514 int lx = from_x + x + (show_level_border ? -1 : 0);
3515 int ly = from_y + y + (show_level_border ? -1 : 0);
3516 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3517 getBorderElement(lx, ly));
3519 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3520 element, tile_size);
3524 redraw_mask |= REDRAW_FIELD;
3527 #define MICROLABEL_EMPTY 0
3528 #define MICROLABEL_LEVEL_NAME 1
3529 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3530 #define MICROLABEL_LEVEL_AUTHOR 3
3531 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3532 #define MICROLABEL_IMPORTED_FROM 5
3533 #define MICROLABEL_IMPORTED_BY_HEAD 6
3534 #define MICROLABEL_IMPORTED_BY 7
3536 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3538 int max_text_width = SXSIZE;
3539 int font_width = getFontWidth(font_nr);
3541 if (pos->align == ALIGN_CENTER)
3542 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3543 else if (pos->align == ALIGN_RIGHT)
3544 max_text_width = pos->x;
3546 max_text_width = SXSIZE - pos->x;
3548 return max_text_width / font_width;
3551 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3553 char label_text[MAX_OUTPUT_LINESIZE + 1];
3554 int max_len_label_text;
3555 int font_nr = pos->font;
3558 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3561 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3562 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3563 mode == MICROLABEL_IMPORTED_BY_HEAD)
3564 font_nr = pos->font_alt;
3566 max_len_label_text = getMaxTextLength(pos, font_nr);
3568 if (pos->size != -1)
3569 max_len_label_text = pos->size;
3571 for (i = 0; i < max_len_label_text; i++)
3572 label_text[i] = ' ';
3573 label_text[max_len_label_text] = '\0';
3575 if (strlen(label_text) > 0)
3576 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3579 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3580 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3581 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3582 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3583 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3584 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3585 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3586 max_len_label_text);
3587 label_text[max_len_label_text] = '\0';
3589 if (strlen(label_text) > 0)
3590 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3592 redraw_mask |= REDRAW_FIELD;
3595 static void DrawPreviewLevelLabel(int mode)
3597 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3600 static void DrawPreviewLevelInfo(int mode)
3602 if (mode == MICROLABEL_LEVEL_NAME)
3603 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3604 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3605 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3608 static void DrawPreviewLevelExt(boolean restart)
3610 static DelayCounter scroll_delay = { 0 };
3611 static DelayCounter label_delay = { 0 };
3612 static int from_x, from_y, scroll_direction;
3613 static int label_state, label_counter;
3614 boolean show_level_border = (BorderElement != EL_EMPTY);
3615 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3616 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3618 scroll_delay.value = preview.step_delay;
3619 label_delay.value = MICROLEVEL_LABEL_DELAY;
3626 if (preview.anim_mode == ANIM_CENTERED)
3628 if (level_xsize > preview.xsize)
3629 from_x = (level_xsize - preview.xsize) / 2;
3630 if (level_ysize > preview.ysize)
3631 from_y = (level_ysize - preview.ysize) / 2;
3634 from_x += preview.xoffset;
3635 from_y += preview.yoffset;
3637 scroll_direction = MV_RIGHT;
3641 DrawPreviewLevelPlayfield(from_x, from_y);
3642 DrawPreviewLevelLabel(label_state);
3644 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3645 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3647 // initialize delay counters
3648 ResetDelayCounter(&scroll_delay);
3649 ResetDelayCounter(&label_delay);
3651 if (leveldir_current->name)
3653 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3654 char label_text[MAX_OUTPUT_LINESIZE + 1];
3655 int font_nr = pos->font;
3656 int max_len_label_text = getMaxTextLength(pos, font_nr);
3658 if (pos->size != -1)
3659 max_len_label_text = pos->size;
3661 strncpy(label_text, leveldir_current->name, max_len_label_text);
3662 label_text[max_len_label_text] = '\0';
3664 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3665 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3671 // scroll preview level, if needed
3672 if (preview.anim_mode != ANIM_NONE &&
3673 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3674 DelayReached(&scroll_delay))
3676 switch (scroll_direction)
3681 from_x -= preview.step_offset;
3682 from_x = (from_x < 0 ? 0 : from_x);
3685 scroll_direction = MV_UP;
3689 if (from_x < level_xsize - preview.xsize)
3691 from_x += preview.step_offset;
3692 from_x = (from_x > level_xsize - preview.xsize ?
3693 level_xsize - preview.xsize : from_x);
3696 scroll_direction = MV_DOWN;
3702 from_y -= preview.step_offset;
3703 from_y = (from_y < 0 ? 0 : from_y);
3706 scroll_direction = MV_RIGHT;
3710 if (from_y < level_ysize - preview.ysize)
3712 from_y += preview.step_offset;
3713 from_y = (from_y > level_ysize - preview.ysize ?
3714 level_ysize - preview.ysize : from_y);
3717 scroll_direction = MV_LEFT;
3724 DrawPreviewLevelPlayfield(from_x, from_y);
3727 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3728 // redraw micro level label, if needed
3729 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3730 !strEqual(level.author, ANONYMOUS_NAME) &&
3731 !strEqual(level.author, leveldir_current->name) &&
3732 DelayReached(&label_delay))
3734 int max_label_counter = 23;
3736 if (leveldir_current->imported_from != NULL &&
3737 strlen(leveldir_current->imported_from) > 0)
3738 max_label_counter += 14;
3739 if (leveldir_current->imported_by != NULL &&
3740 strlen(leveldir_current->imported_by) > 0)
3741 max_label_counter += 14;
3743 label_counter = (label_counter + 1) % max_label_counter;
3744 label_state = (label_counter >= 0 && label_counter <= 7 ?
3745 MICROLABEL_LEVEL_NAME :
3746 label_counter >= 9 && label_counter <= 12 ?
3747 MICROLABEL_LEVEL_AUTHOR_HEAD :
3748 label_counter >= 14 && label_counter <= 21 ?
3749 MICROLABEL_LEVEL_AUTHOR :
3750 label_counter >= 23 && label_counter <= 26 ?
3751 MICROLABEL_IMPORTED_FROM_HEAD :
3752 label_counter >= 28 && label_counter <= 35 ?
3753 MICROLABEL_IMPORTED_FROM :
3754 label_counter >= 37 && label_counter <= 40 ?
3755 MICROLABEL_IMPORTED_BY_HEAD :
3756 label_counter >= 42 && label_counter <= 49 ?
3757 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3759 if (leveldir_current->imported_from == NULL &&
3760 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3761 label_state == MICROLABEL_IMPORTED_FROM))
3762 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3763 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3765 DrawPreviewLevelLabel(label_state);
3769 void DrawPreviewPlayers(void)
3771 if (game_status != GAME_MODE_MAIN)
3774 // do not draw preview players if level preview redefined, but players aren't
3775 if (preview.redefined && !menu.main.preview_players.redefined)
3778 boolean player_found[MAX_PLAYERS];
3779 int num_players = 0;
3782 for (i = 0; i < MAX_PLAYERS; i++)
3783 player_found[i] = FALSE;
3785 // check which players can be found in the level (simple approach)
3786 for (x = 0; x < lev_fieldx; x++)
3788 for (y = 0; y < lev_fieldy; y++)
3790 int element = level.field[x][y];
3792 if (IS_PLAYER_ELEMENT(element))
3794 int player_nr = GET_PLAYER_NR(element);
3796 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3798 if (!player_found[player_nr])
3801 player_found[player_nr] = TRUE;
3806 struct TextPosInfo *pos = &menu.main.preview_players;
3807 int tile_size = pos->tile_size;
3808 int border_size = pos->border_size;
3809 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3810 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3811 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3812 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3813 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3814 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3815 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3816 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3817 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3818 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3819 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3820 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3822 // clear area in which the players will be drawn
3823 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3824 max_players_width, max_players_height);
3826 if (!network.enabled && !setup.team_mode)
3829 // only draw players if level is suited for team mode
3830 if (num_players < 2)
3833 // draw all players that were found in the level
3834 for (i = 0; i < MAX_PLAYERS; i++)
3836 if (player_found[i])
3838 int graphic = el2img(EL_PLAYER_1 + i);
3840 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3842 xpos += player_xoffset;
3843 ypos += player_yoffset;
3848 static void PreparePreviewTileBitmap(void)
3850 // check if special preview bitmap with level-specific colors should be created
3851 if (level.game_engine_type != GAME_ENGINE_TYPE_BD)
3854 // use original sized bitmap (else reduced color palette is lost by downscaling)
3855 int original_tilesize = MAX(MINI_TILESIZE, preview.tile_size);
3856 int scale_down_factor = original_tilesize / preview.tile_size;
3859 int element_template = EL_BDX_GAME_GRAPHICS_COLOR_TEMPLATE;
3860 int graphic_template = el2preimg(element_template);
3861 int element_default = EL_BDX_ROCK;
3862 int graphic_default = el2preimg(element_default);
3864 // create special preview bitmap and scale it down to preview tile size
3865 getSizedGraphicSource(graphic_template, 0, original_tilesize, &src_bitmap, &src_x, &src_y);
3866 PreparePreviewTileBitmap_BD(src_bitmap, scale_down_factor);
3868 // force using special preview bitmap to replace original preview bitmap
3869 getSizedGraphicSource(graphic_default, 0, preview.tile_size, &src_bitmap, &src_x, &src_y);
3870 SetPreviewTileBitmapReference_BD(src_bitmap);
3873 void DrawPreviewLevelInitial(void)
3875 PreparePreviewTileBitmap(); // only needed for native BD style levels
3877 DrawPreviewLevelExt(TRUE);
3878 DrawPreviewPlayers();
3881 void DrawPreviewLevelAnimation(void)
3883 DrawPreviewLevelExt(FALSE);
3886 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3887 int border_size, int font_nr)
3889 int graphic = el2img(EL_PLAYER_1 + player_nr);
3890 int font_height = getFontHeight(font_nr);
3891 int player_height = MAX(tile_size, font_height);
3892 int xoffset_text = tile_size + border_size;
3893 int yoffset_text = (player_height - font_height) / 2;
3894 int yoffset_graphic = (player_height - tile_size) / 2;
3895 char *player_name = getNetworkPlayerName(player_nr + 1);
3897 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3899 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3902 static void DrawNetworkPlayersExt(boolean force)
3904 if (game_status != GAME_MODE_MAIN)
3907 if (!network.connected && !force)
3910 // do not draw network players if level preview redefined, but players aren't
3911 if (preview.redefined && !menu.main.network_players.redefined)
3914 int num_players = 0;
3917 for (i = 0; i < MAX_PLAYERS; i++)
3918 if (stored_player[i].connected_network)
3921 struct TextPosInfo *pos = &menu.main.network_players;
3922 int tile_size = pos->tile_size;
3923 int border_size = pos->border_size;
3924 int xoffset_text = tile_size + border_size;
3925 int font_nr = pos->font;
3926 int font_width = getFontWidth(font_nr);
3927 int font_height = getFontHeight(font_nr);
3928 int player_height = MAX(tile_size, font_height);
3929 int player_yoffset = player_height + border_size;
3930 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3931 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3932 int all_players_height = num_players * player_yoffset - border_size;
3933 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3934 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3935 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3937 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3938 max_players_width, max_players_height);
3940 // first draw local network player ...
3941 for (i = 0; i < MAX_PLAYERS; i++)
3943 if (stored_player[i].connected_network &&
3944 stored_player[i].connected_locally)
3946 char *player_name = getNetworkPlayerName(i + 1);
3947 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3948 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3950 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3952 ypos += player_yoffset;
3956 // ... then draw all other network players
3957 for (i = 0; i < MAX_PLAYERS; i++)
3959 if (stored_player[i].connected_network &&
3960 !stored_player[i].connected_locally)
3962 char *player_name = getNetworkPlayerName(i + 1);
3963 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3964 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3966 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3968 ypos += player_yoffset;
3973 void DrawNetworkPlayers(void)
3975 DrawNetworkPlayersExt(FALSE);
3978 void ClearNetworkPlayers(void)
3980 DrawNetworkPlayersExt(TRUE);
3983 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3984 int graphic, int lx, int ly,
3987 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3989 if (mask_mode == USE_MASKING)
3990 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3992 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3995 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3996 int graphic, int sync_frame, int mask_mode)
3998 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4000 if (mask_mode == USE_MASKING)
4001 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
4003 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
4006 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
4007 int graphic, int sync_frame, int tilesize,
4010 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4012 if (mask_mode == USE_MASKING)
4013 DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
4015 DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
4018 static void DrawGraphicAnimation(int x, int y, int graphic)
4020 int lx = LEVELX(x), ly = LEVELY(y);
4021 int mask_mode = NO_MASKING;
4023 if (!IN_SCR_FIELD(x, y))
4026 if (game.use_masked_elements)
4028 if (Tile[lx][ly] != EL_EMPTY)
4030 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
4032 mask_mode = USE_MASKING;
4036 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
4037 graphic, lx, ly, mask_mode);
4039 MarkTileDirty(x, y);
4042 void DrawFixedGraphicAnimation(int x, int y, int graphic)
4044 int lx = LEVELX(x), ly = LEVELY(y);
4045 int mask_mode = NO_MASKING;
4047 if (!IN_SCR_FIELD(x, y))
4050 if (game.use_masked_elements)
4052 if (Tile[lx][ly] != EL_EMPTY)
4054 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
4056 mask_mode = USE_MASKING;
4060 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
4061 graphic, lx, ly, mask_mode);
4063 MarkTileDirty(x, y);
4066 void DrawLevelGraphicAnimation(int x, int y, int graphic)
4068 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4071 void DrawLevelElementAnimation(int x, int y, int element)
4073 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4075 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4078 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
4080 int sx = SCREENX(x), sy = SCREENY(y);
4082 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4085 if (Tile[x][y] == EL_EMPTY)
4086 graphic = el2img(GfxElementEmpty[x][y]);
4088 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4091 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4094 DrawGraphicAnimation(sx, sy, graphic);
4097 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4098 DrawLevelFieldCrumbled(x, y);
4100 if (GFX_CRUMBLED(Tile[x][y]))
4101 DrawLevelFieldCrumbled(x, y);
4105 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4107 int sx = SCREENX(x), sy = SCREENY(y);
4110 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4113 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4115 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4118 DrawGraphicAnimation(sx, sy, graphic);
4120 if (GFX_CRUMBLED(element))
4121 DrawLevelFieldCrumbled(x, y);
4124 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4126 if (player->use_murphy)
4128 // this works only because currently only one player can be "murphy" ...
4129 static int last_horizontal_dir = MV_LEFT;
4130 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4132 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4133 last_horizontal_dir = move_dir;
4135 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4137 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4139 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4145 return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4148 static boolean equalGraphics(int graphic1, int graphic2)
4150 struct GraphicInfo *g1 = &graphic_info[graphic1];
4151 struct GraphicInfo *g2 = &graphic_info[graphic2];
4153 return (g1->bitmap == g2->bitmap &&
4154 g1->src_x == g2->src_x &&
4155 g1->src_y == g2->src_y &&
4156 g1->anim_frames == g2->anim_frames &&
4157 g1->anim_delay == g2->anim_delay &&
4158 g1->anim_mode == g2->anim_mode);
4161 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4165 DRAW_PLAYER_STAGE_INIT = 0,
4166 DRAW_PLAYER_STAGE_LAST_FIELD,
4167 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4168 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4169 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4170 DRAW_PLAYER_STAGE_PLAYER,
4172 DRAW_PLAYER_STAGE_PLAYER,
4173 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4175 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4176 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4178 NUM_DRAW_PLAYER_STAGES
4181 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4183 static int static_last_player_graphic[MAX_PLAYERS];
4184 static int static_last_player_frame[MAX_PLAYERS];
4185 static boolean static_player_is_opaque[MAX_PLAYERS];
4186 static boolean draw_player[MAX_PLAYERS];
4187 int pnr = player->index_nr;
4189 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4191 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4192 static_last_player_frame[pnr] = player->Frame;
4193 static_player_is_opaque[pnr] = FALSE;
4195 draw_player[pnr] = TRUE;
4198 if (!draw_player[pnr])
4202 if (!IN_LEV_FIELD(player->jx, player->jy))
4204 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4205 Debug("draw:DrawPlayerExt", "This should never happen!");
4207 draw_player[pnr] = FALSE;
4213 int last_player_graphic = static_last_player_graphic[pnr];
4214 int last_player_frame = static_last_player_frame[pnr];
4215 boolean player_is_opaque = static_player_is_opaque[pnr];
4217 int jx = player->jx;
4218 int jy = player->jy;
4219 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4220 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4221 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4222 int last_jx = (player->is_moving ? jx - dx : jx);
4223 int last_jy = (player->is_moving ? jy - dy : jy);
4224 int next_jx = jx + dx;
4225 int next_jy = jy + dy;
4226 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4227 int sx = SCREENX(jx);
4228 int sy = SCREENY(jy);
4229 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4230 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4231 int element = Tile[jx][jy];
4232 int last_element = Tile[last_jx][last_jy];
4233 int action = (player->is_pushing ? ACTION_PUSHING :
4234 player->is_digging ? ACTION_DIGGING :
4235 player->is_collecting ? ACTION_COLLECTING :
4236 player->is_moving ? ACTION_MOVING :
4237 player->is_snapping ? ACTION_SNAPPING :
4238 player->is_dropping ? ACTION_DROPPING :
4239 player->is_waiting ? player->action_waiting :
4242 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4244 // ------------------------------------------------------------------------
4245 // initialize drawing the player
4246 // ------------------------------------------------------------------------
4248 draw_player[pnr] = FALSE;
4250 // GfxElement[][] is set to the element the player is digging or collecting;
4251 // remove also for off-screen player if the player is not moving anymore
4252 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4253 GfxElement[jx][jy] = EL_UNDEFINED;
4255 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4258 if (element == EL_EXPLOSION)
4261 InitPlayerGfxAnimation(player, action, move_dir);
4263 draw_player[pnr] = TRUE;
4265 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4267 // ------------------------------------------------------------------------
4268 // draw things in the field the player is leaving, if needed
4269 // ------------------------------------------------------------------------
4271 if (!IN_SCR_FIELD(sx, sy))
4272 draw_player[pnr] = FALSE;
4274 if (!player->is_moving)
4277 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4279 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4281 if (last_element == EL_DYNAMITE_ACTIVE ||
4282 last_element == EL_EM_DYNAMITE_ACTIVE ||
4283 last_element == EL_SP_DISK_RED_ACTIVE)
4284 DrawDynamite(last_jx, last_jy);
4286 DrawLevelFieldThruMask(last_jx, last_jy);
4288 else if (last_element == EL_DYNAMITE_ACTIVE ||
4289 last_element == EL_EM_DYNAMITE_ACTIVE ||
4290 last_element == EL_SP_DISK_RED_ACTIVE)
4291 DrawDynamite(last_jx, last_jy);
4293 DrawLevelField(last_jx, last_jy);
4295 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4297 // ------------------------------------------------------------------------
4298 // draw things behind the player, if needed
4299 // ------------------------------------------------------------------------
4303 DrawLevelElement(jx, jy, Back[jx][jy]);
4308 if (IS_ACTIVE_BOMB(element))
4310 DrawLevelElement(jx, jy, EL_EMPTY);
4315 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4317 int old_element = GfxElement[jx][jy];
4318 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4319 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4321 if (GFX_CRUMBLED(old_element))
4322 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4324 DrawScreenGraphic(sx, sy, old_graphic, frame);
4326 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4327 static_player_is_opaque[pnr] = TRUE;
4331 GfxElement[jx][jy] = EL_UNDEFINED;
4333 // make sure that pushed elements are drawn with correct frame rate
4334 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4336 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4337 GfxFrame[jx][jy] = player->StepFrame;
4339 DrawLevelField(jx, jy);
4342 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4344 // ------------------------------------------------------------------------
4345 // draw things the player is pushing, if needed
4346 // ------------------------------------------------------------------------
4348 if (!player->is_pushing || !player->is_moving)
4351 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4354 int gfx_frame = GfxFrame[jx][jy];
4356 if (!IS_MOVING(jx, jy)) // push movement already finished
4358 element = Tile[next_jx][next_jy];
4359 gfx_frame = GfxFrame[next_jx][next_jy];
4362 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4363 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4364 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4366 // draw background element under pushed element (like the Sokoban field)
4367 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4369 // this allows transparent pushing animation over non-black background
4372 DrawLevelElement(jx, jy, Back[jx][jy]);
4374 DrawLevelElement(jx, jy, EL_EMPTY);
4377 if (Back[next_jx][next_jy])
4378 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4380 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4382 int px = SCREENX(jx), py = SCREENY(jy);
4383 int pxx = (TILEX - ABS(sxx)) * dx;
4384 int pyy = (TILEY - ABS(syy)) * dy;
4387 // do not draw (EM style) pushing animation when pushing is finished
4388 // (two-tile animations usually do not contain start and end frame)
4389 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4390 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4392 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4394 // masked drawing is needed for EMC style (double) movement graphics
4395 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4396 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4399 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4401 // ------------------------------------------------------------------------
4402 // draw player himself
4403 // ------------------------------------------------------------------------
4405 int graphic = getPlayerGraphic(player, move_dir);
4407 // in the case of changed player action or direction, prevent the current
4408 // animation frame from being restarted for identical animations
4409 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4410 player->Frame = last_player_frame;
4412 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4414 if (player_is_opaque)
4415 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4417 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4419 if (SHIELD_ON(player))
4421 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4422 IMG_SHIELD_NORMAL_ACTIVE);
4423 frame = getGraphicAnimationFrame(graphic, -1);
4425 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4428 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4430 // ------------------------------------------------------------------------
4431 // draw things in front of player (active dynamite or dynabombs)
4432 // ------------------------------------------------------------------------
4434 if (IS_ACTIVE_BOMB(element))
4436 int graphic = el2img(element);
4437 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4439 if (game.emulation == EMU_SUPAPLEX)
4440 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4442 DrawGraphicThruMask(sx, sy, graphic, frame);
4445 if (player_is_moving && last_element == EL_EXPLOSION)
4447 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4448 GfxElement[last_jx][last_jy] : EL_EMPTY);
4449 int graphic = el_act2img(element, ACTION_EXPLODING);
4450 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4451 int phase = ExplodePhase[last_jx][last_jy] - 1;
4452 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4455 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4458 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4460 // ------------------------------------------------------------------------
4461 // draw elements the player is just walking/passing through/under
4462 // ------------------------------------------------------------------------
4464 if (player_is_moving)
4466 // handle the field the player is leaving ...
4467 if (IS_ACCESSIBLE_INSIDE(last_element))
4468 DrawLevelField(last_jx, last_jy);
4469 else if (IS_ACCESSIBLE_UNDER(last_element))
4470 DrawLevelFieldThruMask(last_jx, last_jy);
4473 // do not redraw accessible elements if the player is just pushing them
4474 if (!player_is_moving || !player->is_pushing)
4476 // ... and the field the player is entering
4477 if (IS_ACCESSIBLE_INSIDE(element))
4478 DrawLevelField(jx, jy);
4479 else if (IS_ACCESSIBLE_UNDER(element))
4480 DrawLevelFieldThruMask(jx, jy);
4483 MarkTileDirty(sx, sy);
4487 void DrawPlayer(struct PlayerInfo *player)
4491 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4492 DrawPlayerExt(player, i);
4495 void DrawAllPlayers(void)
4499 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4500 for (j = 0; j < MAX_PLAYERS; j++)
4501 if (stored_player[j].active)
4502 DrawPlayerExt(&stored_player[j], i);
4505 void DrawPlayerField(int x, int y)
4507 if (!IS_PLAYER(x, y))
4510 DrawPlayer(PLAYERINFO(x, y));
4513 // ----------------------------------------------------------------------------
4515 void WaitForEventToContinue(void)
4517 boolean first_wait = TRUE;
4518 boolean still_wait = TRUE;
4520 if (program.headless)
4523 // simulate releasing mouse button over last gadget, if still pressed
4525 HandleGadgets(-1, -1, 0);
4527 button_status = MB_RELEASED;
4530 ClearPlayerAction();
4536 if (NextValidEvent(&event))
4540 case EVENT_BUTTONPRESS:
4541 case EVENT_FINGERPRESS:
4545 case EVENT_BUTTONRELEASE:
4546 case EVENT_FINGERRELEASE:
4547 still_wait = first_wait;
4550 case EVENT_KEYPRESS:
4551 case SDL_CONTROLLERBUTTONDOWN:
4552 case SDL_JOYBUTTONDOWN:
4557 HandleOtherEvents(&event);
4561 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4566 if (!PendingEvent())
4571 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4573 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4574 int draw_buffer_last = GetDrawtoField();
4575 int width = request.width;
4576 int height = request.height;
4580 setRequestPosition(&sx, &sy, FALSE);
4582 button_status = MB_RELEASED;
4584 request_gadget_id = -1;
4591 SetDrawtoField(draw_buffer_game);
4593 HandleGameActions();
4595 SetDrawtoField(DRAW_TO_BACKBUFFER);
4602 while (NextValidEvent(&event))
4606 case EVENT_BUTTONPRESS:
4607 case EVENT_BUTTONRELEASE:
4608 case EVENT_MOTIONNOTIFY:
4610 DrawBuffer *drawto_last = drawto;
4613 if (event.type == EVENT_MOTIONNOTIFY)
4618 motion_status = TRUE;
4619 mx = ((MotionEvent *) &event)->x;
4620 my = ((MotionEvent *) &event)->y;
4624 motion_status = FALSE;
4625 mx = ((ButtonEvent *) &event)->x;
4626 my = ((ButtonEvent *) &event)->y;
4627 if (event.type == EVENT_BUTTONPRESS)
4628 button_status = ((ButtonEvent *) &event)->button;
4630 button_status = MB_RELEASED;
4633 if (global.use_envelope_request)
4635 // draw changed button states to temporary bitmap
4636 drawto = bitmap_db_store_1;
4639 // this sets 'request_gadget_id'
4640 HandleGadgets(mx, my, button_status);
4642 if (global.use_envelope_request)
4644 // restore pointer to drawing buffer
4645 drawto = drawto_last;
4647 // prepare complete envelope request from temporary bitmap
4648 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy,
4652 switch (request_gadget_id)
4654 case TOOL_CTRL_ID_YES:
4655 case TOOL_CTRL_ID_TOUCH_YES:
4658 case TOOL_CTRL_ID_NO:
4659 case TOOL_CTRL_ID_TOUCH_NO:
4662 case TOOL_CTRL_ID_CONFIRM:
4663 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4664 result = TRUE | FALSE;
4667 case TOOL_CTRL_ID_PLAYER_1:
4670 case TOOL_CTRL_ID_PLAYER_2:
4673 case TOOL_CTRL_ID_PLAYER_3:
4676 case TOOL_CTRL_ID_PLAYER_4:
4684 // only needed to handle clickable pointer animations here
4685 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4690 case SDL_WINDOWEVENT:
4691 HandleWindowEvent((WindowEvent *) &event);
4694 case SDL_APP_WILLENTERBACKGROUND:
4695 case SDL_APP_DIDENTERBACKGROUND:
4696 case SDL_APP_WILLENTERFOREGROUND:
4697 case SDL_APP_DIDENTERFOREGROUND:
4698 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4701 case EVENT_KEYPRESS:
4703 Key key = GetEventKey((KeyEvent *)&event);
4708 if (req_state & REQ_CONFIRM)
4717 #if defined(KSYM_Rewind)
4718 case KSYM_Rewind: // for Amazon Fire TV remote
4727 #if defined(KSYM_FastForward)
4728 case KSYM_FastForward: // for Amazon Fire TV remote
4734 HandleKeysDebug(key, KEY_PRESSED);
4738 if (req_state & REQ_PLAYER)
4740 int old_player_nr = setup.network_player_nr;
4743 result = old_player_nr + 1;
4748 result = old_player_nr + 1;
4779 case EVENT_FINGERRELEASE:
4780 case EVENT_KEYRELEASE:
4781 ClearPlayerAction();
4784 case SDL_CONTROLLERBUTTONDOWN:
4785 switch (event.cbutton.button)
4787 case SDL_CONTROLLER_BUTTON_A:
4788 case SDL_CONTROLLER_BUTTON_X:
4789 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4790 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4794 case SDL_CONTROLLER_BUTTON_B:
4795 case SDL_CONTROLLER_BUTTON_Y:
4796 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4797 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4798 case SDL_CONTROLLER_BUTTON_BACK:
4803 if (req_state & REQ_PLAYER)
4805 int old_player_nr = setup.network_player_nr;
4808 result = old_player_nr + 1;
4810 switch (event.cbutton.button)
4812 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4813 case SDL_CONTROLLER_BUTTON_Y:
4817 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4818 case SDL_CONTROLLER_BUTTON_B:
4822 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4823 case SDL_CONTROLLER_BUTTON_A:
4827 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4828 case SDL_CONTROLLER_BUTTON_X:
4839 case SDL_CONTROLLERBUTTONUP:
4840 HandleJoystickEvent(&event);
4841 ClearPlayerAction();
4845 HandleOtherEvents(&event);
4850 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4852 int joy = AnyJoystick();
4854 if (joy & JOY_BUTTON_1)
4856 else if (joy & JOY_BUTTON_2)
4859 else if (AnyJoystick())
4861 int joy = AnyJoystick();
4863 if (req_state & REQ_PLAYER)
4867 else if (joy & JOY_RIGHT)
4869 else if (joy & JOY_DOWN)
4871 else if (joy & JOY_LEFT)
4879 SetDrawtoField(draw_buffer_last);
4884 static void DoRequestBefore(void)
4886 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4888 // when showing request dialog after game ended, deactivate game panel
4890 game.panel.active = FALSE;
4892 if (game_status == GAME_MODE_PLAYING)
4893 BlitScreenToBitmap(backbuffer);
4895 // disable deactivated drawing when quick-loading level tape recording
4896 if (tape.playing && tape.deactivate_display)
4897 TapeDeactivateDisplayOff(TRUE);
4899 SetMouseCursor(CURSOR_DEFAULT);
4901 // pause network game while waiting for request to answer
4902 if (network.enabled &&
4903 game_status == GAME_MODE_PLAYING &&
4904 !game.all_players_gone)
4905 SendToServer_PausePlaying();
4907 // simulate releasing mouse button over last gadget, if still pressed
4909 HandleGadgets(-1, -1, 0);
4914 static void DoRequestAfter(void)
4918 if (game_status == GAME_MODE_PLAYING)
4920 SetPanelBackground();
4921 SetDrawBackgroundMask(REDRAW_DOOR_1);
4925 SetDrawBackgroundMask(REDRAW_FIELD);
4928 // continue network game after request
4929 if (network.enabled &&
4930 game_status == GAME_MODE_PLAYING &&
4931 !game.all_players_gone)
4932 SendToServer_ContinuePlaying();
4934 // restore deactivated drawing when quick-loading level tape recording
4935 if (tape.playing && tape.deactivate_display)
4936 TapeDeactivateDisplayOn();
4939 static void setRequestDoorTextProperties(char *text,
4944 int *set_max_line_length)
4946 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
4947 struct TextPosInfo *pos = &request.button.confirm;
4948 int button_ypos = pos->y;
4949 int font_nr = FONT_TEXT_2;
4950 int font_width = getFontWidth(font_nr);
4951 int font_height = getFontHeight(font_nr);
4952 int line_height = font_height + line_spacing;
4953 int max_text_width = vp_door_1->width;
4954 int max_text_height = button_ypos - 2 * text_spacing;
4955 int max_line_length = max_text_width / font_width;
4956 int max_lines = max_text_height / line_height;
4958 if (maxWordLengthInRequestString(text) > max_line_length)
4960 font_nr = FONT_TEXT_1;
4961 font_width = getFontWidth(font_nr);
4962 max_line_length = max_text_width / font_width;
4965 *set_font_nr = font_nr;
4966 *set_max_lines = max_lines;
4967 *set_max_line_length = max_line_length;
4970 static void DrawRequestDoorText(char *text)
4972 char *text_ptr = text;
4973 int text_spacing = 8;
4974 int line_spacing = 2;
4975 int max_request_lines;
4976 int max_request_line_len;
4980 // force DOOR font inside door area
4981 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4983 setRequestDoorTextProperties(text, text_spacing, line_spacing, &font_nr,
4984 &max_request_lines, &max_request_line_len);
4986 for (text_ptr = text, ty = 0; ty < max_request_lines; ty++)
4988 char text_line[max_request_line_len + 1];
4994 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4996 tc = *(text_ptr + tx);
4997 if (!tc || tc == ' ' || tc == '?' || tc == '!')
5001 if ((tc == '?' || tc == '!') && tl == 0)
5011 strncpy(text_line, text_ptr, tl);
5014 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
5015 DY + text_spacing + ty * (getFontHeight(font_nr) + line_spacing),
5016 text_line, font_nr);
5018 text_ptr += tl + (tc == ' ' ? 1 : 0);
5024 static int RequestDoor(char *text, unsigned int req_state)
5026 unsigned int old_door_state = GetDoorState();
5027 int draw_buffer_last = GetDrawtoField();
5030 if (old_door_state & DOOR_OPEN_1)
5032 CloseDoor(DOOR_CLOSE_1);
5034 // save old door content
5035 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5036 0, 0, DXSIZE, DYSIZE, DXSIZE, 0);
5039 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5040 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5042 // clear door drawing field
5043 DrawBackground(DX, DY, DXSIZE, DYSIZE);
5045 // write text for request
5046 DrawRequestDoorText(text);
5048 MapToolButtons(req_state);
5050 // copy request gadgets to door backbuffer
5051 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
5053 OpenDoor(DOOR_OPEN_1);
5055 // ---------- handle request buttons ----------
5056 result = RequestHandleEvents(req_state, draw_buffer_last);
5060 if (!(req_state & REQ_STAY_OPEN))
5062 CloseDoor(DOOR_CLOSE_1);
5064 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
5065 (req_state & REQ_REOPEN))
5066 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
5072 static int RequestEnvelope(char *text, unsigned int req_state)
5074 int draw_buffer_last = GetDrawtoField();
5077 DrawEnvelopeRequest(text, req_state);
5078 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5080 // ---------- handle request buttons ----------
5081 result = RequestHandleEvents(req_state, draw_buffer_last);
5085 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5090 int Request(char *text, unsigned int req_state)
5092 boolean overlay_enabled = GetOverlayEnabled();
5095 game.request_active = TRUE;
5097 SetOverlayEnabled(FALSE);
5101 if (global.use_envelope_request)
5102 result = RequestEnvelope(text, req_state);
5104 result = RequestDoor(text, req_state);
5108 SetOverlayEnabled(overlay_enabled);
5110 game.request_active = FALSE;
5115 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5117 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5118 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5121 if (dpo1->sort_priority != dpo2->sort_priority)
5122 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5124 compare_result = dpo1->nr - dpo2->nr;
5126 return compare_result;
5129 void InitGraphicCompatibilityInfo_Doors(void)
5135 struct DoorInfo *door;
5139 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5140 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5142 { -1, -1, -1, NULL }
5144 struct Rect door_rect_list[] =
5146 { DX, DY, DXSIZE, DYSIZE },
5147 { VX, VY, VXSIZE, VYSIZE }
5151 for (i = 0; doors[i].door_token != -1; i++)
5153 int door_token = doors[i].door_token;
5154 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5155 int part_1 = doors[i].part_1;
5156 int part_8 = doors[i].part_8;
5157 int part_2 = part_1 + 1;
5158 int part_3 = part_1 + 2;
5159 struct DoorInfo *door = doors[i].door;
5160 struct Rect *door_rect = &door_rect_list[door_index];
5161 boolean door_gfx_redefined = FALSE;
5163 // check if any door part graphic definitions have been redefined
5165 for (j = 0; door_part_controls[j].door_token != -1; j++)
5167 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5168 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5170 if (dpc->door_token == door_token && fi->redefined)
5171 door_gfx_redefined = TRUE;
5174 // check for old-style door graphic/animation modifications
5176 if (!door_gfx_redefined)
5178 if (door->anim_mode & ANIM_STATIC_PANEL)
5180 door->panel.step_xoffset = 0;
5181 door->panel.step_yoffset = 0;
5184 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5186 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5187 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5188 int num_door_steps, num_panel_steps;
5190 // remove door part graphics other than the two default wings
5192 for (j = 0; door_part_controls[j].door_token != -1; j++)
5194 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5195 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5197 if (dpc->graphic >= part_3 &&
5198 dpc->graphic <= part_8)
5202 // set graphics and screen positions of the default wings
5204 g_part_1->width = door_rect->width;
5205 g_part_1->height = door_rect->height;
5206 g_part_2->width = door_rect->width;
5207 g_part_2->height = door_rect->height;
5208 g_part_2->src_x = door_rect->width;
5209 g_part_2->src_y = g_part_1->src_y;
5211 door->part_2.x = door->part_1.x;
5212 door->part_2.y = door->part_1.y;
5214 if (door->width != -1)
5216 g_part_1->width = door->width;
5217 g_part_2->width = door->width;
5219 // special treatment for graphics and screen position of right wing
5220 g_part_2->src_x += door_rect->width - door->width;
5221 door->part_2.x += door_rect->width - door->width;
5224 if (door->height != -1)
5226 g_part_1->height = door->height;
5227 g_part_2->height = door->height;
5229 // special treatment for graphics and screen position of bottom wing
5230 g_part_2->src_y += door_rect->height - door->height;
5231 door->part_2.y += door_rect->height - door->height;
5234 // set animation delays for the default wings and panels
5236 door->part_1.step_delay = door->step_delay;
5237 door->part_2.step_delay = door->step_delay;
5238 door->panel.step_delay = door->step_delay;
5240 // set animation draw order for the default wings
5242 door->part_1.sort_priority = 2; // draw left wing over ...
5243 door->part_2.sort_priority = 1; // ... right wing
5245 // set animation draw offset for the default wings
5247 if (door->anim_mode & ANIM_HORIZONTAL)
5249 door->part_1.step_xoffset = door->step_offset;
5250 door->part_1.step_yoffset = 0;
5251 door->part_2.step_xoffset = door->step_offset * -1;
5252 door->part_2.step_yoffset = 0;
5254 num_door_steps = g_part_1->width / door->step_offset;
5256 else // ANIM_VERTICAL
5258 door->part_1.step_xoffset = 0;
5259 door->part_1.step_yoffset = door->step_offset;
5260 door->part_2.step_xoffset = 0;
5261 door->part_2.step_yoffset = door->step_offset * -1;
5263 num_door_steps = g_part_1->height / door->step_offset;
5266 // set animation draw offset for the default panels
5268 if (door->step_offset > 1)
5270 num_panel_steps = 2 * door_rect->height / door->step_offset;
5271 door->panel.start_step = num_panel_steps - num_door_steps;
5272 door->panel.start_step_closing = door->panel.start_step;
5276 num_panel_steps = door_rect->height / door->step_offset;
5277 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5278 door->panel.start_step_closing = door->panel.start_step;
5279 door->panel.step_delay *= 2;
5286 void InitDoors(void)
5290 for (i = 0; door_part_controls[i].door_token != -1; i++)
5292 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5293 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5295 // initialize "start_step_opening" and "start_step_closing", if needed
5296 if (dpc->pos->start_step_opening == 0 &&
5297 dpc->pos->start_step_closing == 0)
5299 // dpc->pos->start_step_opening = dpc->pos->start_step;
5300 dpc->pos->start_step_closing = dpc->pos->start_step;
5303 // fill structure for door part draw order (sorted below)
5305 dpo->sort_priority = dpc->pos->sort_priority;
5308 // sort door part controls according to sort_priority and graphic number
5309 qsort(door_part_order, MAX_DOOR_PARTS,
5310 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5313 unsigned int OpenDoor(unsigned int door_state)
5315 if (door_state & DOOR_COPY_BACK)
5317 if (door_state & DOOR_OPEN_1)
5318 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5319 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5321 if (door_state & DOOR_OPEN_2)
5322 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5323 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5325 door_state &= ~DOOR_COPY_BACK;
5328 return MoveDoor(door_state);
5331 unsigned int CloseDoor(unsigned int door_state)
5333 unsigned int old_door_state = GetDoorState();
5335 if (!(door_state & DOOR_NO_COPY_BACK))
5337 if (old_door_state & DOOR_OPEN_1)
5338 BlitBitmap(backbuffer, bitmap_db_door_1,
5339 DX, DY, DXSIZE, DYSIZE, 0, 0);
5341 if (old_door_state & DOOR_OPEN_2)
5342 BlitBitmap(backbuffer, bitmap_db_door_2,
5343 VX, VY, VXSIZE, VYSIZE, 0, 0);
5345 door_state &= ~DOOR_NO_COPY_BACK;
5348 return MoveDoor(door_state);
5351 unsigned int GetDoorState(void)
5353 return MoveDoor(DOOR_GET_STATE);
5356 unsigned int SetDoorState(unsigned int door_state)
5358 return MoveDoor(door_state | DOOR_SET_STATE);
5361 static int euclid(int a, int b)
5363 return (b ? euclid(b, a % b) : a);
5366 unsigned int MoveDoor(unsigned int door_state)
5368 struct Rect door_rect_list[] =
5370 { DX, DY, DXSIZE, DYSIZE },
5371 { VX, VY, VXSIZE, VYSIZE }
5373 static int door1 = DOOR_CLOSE_1;
5374 static int door2 = DOOR_CLOSE_2;
5375 DelayCounter door_delay = { 0 };
5378 if (door_state == DOOR_GET_STATE)
5379 return (door1 | door2);
5381 if (door_state & DOOR_SET_STATE)
5383 if (door_state & DOOR_ACTION_1)
5384 door1 = door_state & DOOR_ACTION_1;
5385 if (door_state & DOOR_ACTION_2)
5386 door2 = door_state & DOOR_ACTION_2;
5388 return (door1 | door2);
5391 if (!(door_state & DOOR_FORCE_REDRAW))
5393 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5394 door_state &= ~DOOR_OPEN_1;
5395 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5396 door_state &= ~DOOR_CLOSE_1;
5397 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5398 door_state &= ~DOOR_OPEN_2;
5399 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5400 door_state &= ~DOOR_CLOSE_2;
5403 if (global.autoplay_leveldir)
5405 door_state |= DOOR_NO_DELAY;
5406 door_state &= ~DOOR_CLOSE_ALL;
5409 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5410 door_state |= DOOR_NO_DELAY;
5412 if (door_state & DOOR_ACTION)
5414 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
5415 boolean door_panel_drawn[NUM_DOORS];
5416 boolean panel_has_doors[NUM_DOORS];
5417 boolean door_part_skip[MAX_DOOR_PARTS];
5418 boolean door_part_done[MAX_DOOR_PARTS];
5419 boolean door_part_done_all;
5420 int num_steps[MAX_DOOR_PARTS];
5421 int max_move_delay = 0; // delay for complete animations of all doors
5422 int max_step_delay = 0; // delay (ms) between two animation frames
5423 int num_move_steps = 0; // number of animation steps for all doors
5424 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5425 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5429 for (i = 0; i < NUM_DOORS; i++)
5430 panel_has_doors[i] = FALSE;
5432 for (i = 0; i < MAX_DOOR_PARTS; i++)
5434 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5435 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5436 int door_token = dpc->door_token;
5438 door_part_done[i] = FALSE;
5439 door_part_skip[i] = (!(door_state & door_token) ||
5443 for (i = 0; i < MAX_DOOR_PARTS; i++)
5445 int nr = door_part_order[i].nr;
5446 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5447 struct DoorPartPosInfo *pos = dpc->pos;
5448 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5449 int door_token = dpc->door_token;
5450 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5451 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5452 int step_xoffset = ABS(pos->step_xoffset);
5453 int step_yoffset = ABS(pos->step_yoffset);
5454 int step_delay = pos->step_delay;
5455 int current_door_state = door_state & door_token;
5456 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5457 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5458 boolean part_opening = (is_panel ? door_closing : door_opening);
5459 int start_step = (part_opening ? pos->start_step_opening :
5460 pos->start_step_closing);
5461 float move_xsize = (step_xoffset ? g->width : 0);
5462 float move_ysize = (step_yoffset ? g->height : 0);
5463 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5464 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5465 int move_steps = (move_xsteps && move_ysteps ?
5466 MIN(move_xsteps, move_ysteps) :
5467 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5468 int move_delay = move_steps * step_delay;
5470 if (door_part_skip[nr])
5473 max_move_delay = MAX(max_move_delay, move_delay);
5474 max_step_delay = (max_step_delay == 0 ? step_delay :
5475 euclid(max_step_delay, step_delay));
5476 num_steps[nr] = move_steps;
5480 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5482 panel_has_doors[door_index] = TRUE;
5486 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5488 num_move_steps = max_move_delay / max_step_delay;
5489 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5491 door_delay.value = max_step_delay;
5493 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5495 start = num_move_steps - 1;
5499 // opening door sound has priority over simultaneously closing door
5500 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5502 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5504 if (door_state & DOOR_OPEN_1)
5505 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5506 if (door_state & DOOR_OPEN_2)
5507 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5509 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5511 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5513 if (door_state & DOOR_CLOSE_1)
5514 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5515 if (door_state & DOOR_CLOSE_2)
5516 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5520 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5522 game.any_door_active = TRUE;
5524 for (k = start; k < num_move_steps; k++)
5526 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5528 door_part_done_all = TRUE;
5530 for (i = 0; i < NUM_DOORS; i++)
5531 door_panel_drawn[i] = FALSE;
5533 for (i = 0; i < MAX_DOOR_PARTS; i++)
5535 int nr = door_part_order[i].nr;
5536 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5537 struct DoorPartPosInfo *pos = dpc->pos;
5538 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5539 int door_token = dpc->door_token;
5540 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5541 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5542 boolean is_panel_and_door_has_closed = FALSE;
5543 struct Rect *door_rect = &door_rect_list[door_index];
5544 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5546 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5547 int current_door_state = door_state & door_token;
5548 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5549 boolean door_closing = !door_opening;
5550 boolean part_opening = (is_panel ? door_closing : door_opening);
5551 boolean part_closing = !part_opening;
5552 int start_step = (part_opening ? pos->start_step_opening :
5553 pos->start_step_closing);
5554 int step_delay = pos->step_delay;
5555 int step_factor = step_delay / max_step_delay;
5556 int k1 = (step_factor ? k / step_factor + 1 : k);
5557 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5558 int kk = MAX(0, k2);
5561 int src_x, src_y, src_xx, src_yy;
5562 int dst_x, dst_y, dst_xx, dst_yy;
5565 if (door_part_skip[nr])
5568 if (!(door_state & door_token))
5576 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5577 int kk_door = MAX(0, k2_door);
5578 int sync_frame = kk_door * door_delay.value;
5579 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5581 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5582 &g_src_x, &g_src_y);
5587 if (!door_panel_drawn[door_index])
5589 ClearRectangleOnBackground(drawto, door_rect->x, door_rect->y,
5590 door_rect->width, door_rect->height);
5592 door_panel_drawn[door_index] = TRUE;
5595 // draw opening or closing door parts
5597 if (pos->step_xoffset < 0) // door part on right side
5600 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5603 if (dst_xx + width > door_rect->width)
5604 width = door_rect->width - dst_xx;
5606 else // door part on left side
5609 dst_xx = pos->x - kk * pos->step_xoffset;
5613 src_xx = ABS(dst_xx);
5617 width = g->width - src_xx;
5619 if (width > door_rect->width)
5620 width = door_rect->width;
5622 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5625 if (pos->step_yoffset < 0) // door part on bottom side
5628 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5631 if (dst_yy + height > door_rect->height)
5632 height = door_rect->height - dst_yy;
5634 else // door part on top side
5637 dst_yy = pos->y - kk * pos->step_yoffset;
5641 src_yy = ABS(dst_yy);
5645 height = g->height - src_yy;
5648 src_x = g_src_x + src_xx;
5649 src_y = g_src_y + src_yy;
5651 dst_x = door_rect->x + dst_xx;
5652 dst_y = door_rect->y + dst_yy;
5654 is_panel_and_door_has_closed =
5657 panel_has_doors[door_index] &&
5658 k >= num_move_steps_doors_only - 1);
5660 if (width >= 0 && width <= g->width &&
5661 height >= 0 && height <= g->height &&
5662 !is_panel_and_door_has_closed)
5664 if (is_panel || !pos->draw_masked)
5665 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5668 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5672 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5674 if ((part_opening && (width < 0 || height < 0)) ||
5675 (part_closing && (width >= g->width && height >= g->height)))
5676 door_part_done[nr] = TRUE;
5678 // continue door part animations, but not panel after door has closed
5679 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5680 door_part_done_all = FALSE;
5683 if (!(door_state & DOOR_NO_DELAY))
5686 HandleGameActions();
5690 SkipUntilDelayReached(&door_delay, &k, last_frame);
5692 // prevent OS (Windows) from complaining about program not responding
5696 if (door_part_done_all)
5700 if (!(door_state & DOOR_NO_DELAY))
5702 // wait for specified door action post delay
5703 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5704 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5705 else if (door_state & DOOR_ACTION_1)
5706 door_delay.value = door_1.post_delay;
5707 else if (door_state & DOOR_ACTION_2)
5708 door_delay.value = door_2.post_delay;
5710 while (!DelayReached(&door_delay))
5713 HandleGameActions();
5719 game.any_door_active = FALSE;
5722 if (door_state & DOOR_ACTION_1)
5723 door1 = door_state & DOOR_ACTION_1;
5724 if (door_state & DOOR_ACTION_2)
5725 door2 = door_state & DOOR_ACTION_2;
5727 // draw masked border over door area
5728 DrawMaskedBorder(REDRAW_DOOR_1);
5729 DrawMaskedBorder(REDRAW_DOOR_2);
5731 ClearAutoRepeatKeyEvents();
5733 return (door1 | door2);
5736 static boolean useSpecialEditorDoor(void)
5738 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5739 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5741 // do not draw special editor door if editor border defined or redefined
5742 if (graphic_info[graphic].bitmap != NULL || redefined)
5745 // do not draw special editor door if global border defined to be empty
5746 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5749 // do not draw special editor door if viewport definitions do not match
5753 EY + EYSIZE != VY + VYSIZE)
5759 void DrawSpecialEditorDoor(void)
5761 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5762 int top_border_width = gfx1->width;
5763 int top_border_height = gfx1->height;
5764 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5765 int ex = EX - outer_border;
5766 int ey = EY - outer_border;
5767 int vy = VY - outer_border;
5768 int exsize = EXSIZE + 2 * outer_border;
5770 if (!useSpecialEditorDoor())
5773 // draw bigger level editor toolbox window
5774 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5775 top_border_width, top_border_height, ex, ey - top_border_height);
5776 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5777 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5779 redraw_mask |= REDRAW_ALL;
5782 void UndrawSpecialEditorDoor(void)
5784 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5785 int top_border_width = gfx1->width;
5786 int top_border_height = gfx1->height;
5787 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5788 int ex = EX - outer_border;
5789 int ey = EY - outer_border;
5790 int ey_top = ey - top_border_height;
5791 int exsize = EXSIZE + 2 * outer_border;
5792 int eysize = EYSIZE + 2 * outer_border;
5794 if (!useSpecialEditorDoor())
5797 // draw normal tape recorder window
5798 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5800 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5801 ex, ey_top, top_border_width, top_border_height,
5803 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5804 ex, ey, exsize, eysize, ex, ey);
5808 // if screen background is set to "[NONE]", clear editor toolbox window
5809 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5810 ClearRectangle(drawto, ex, ey, exsize, eysize);
5813 redraw_mask |= REDRAW_ALL;
5817 // ---------- new tool button stuff -------------------------------------------
5822 struct TextPosInfo *pos;
5824 boolean is_touch_button;
5826 } toolbutton_info[NUM_TOOL_BUTTONS] =
5829 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5830 TOOL_CTRL_ID_YES, FALSE, "yes"
5833 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5834 TOOL_CTRL_ID_NO, FALSE, "no"
5837 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5838 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5841 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5842 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5845 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5846 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5849 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5850 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5853 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5854 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5857 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5858 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5861 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5862 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5865 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5866 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5870 void CreateToolButtons(void)
5874 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5876 int graphic = toolbutton_info[i].graphic;
5877 struct GraphicInfo *gfx = &graphic_info[graphic];
5878 struct TextPosInfo *pos = toolbutton_info[i].pos;
5879 struct GadgetInfo *gi;
5880 Bitmap *deco_bitmap = None;
5881 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5882 unsigned int event_mask = GD_EVENT_RELEASED;
5883 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5884 int base_x = (is_touch_button ? 0 : DX);
5885 int base_y = (is_touch_button ? 0 : DY);
5886 int gd_x = gfx->src_x;
5887 int gd_y = gfx->src_y;
5888 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5889 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5894 // do not use touch buttons if overlay touch buttons are disabled
5895 if (is_touch_button && !setup.touch.overlay_buttons)
5898 if (global.use_envelope_request && !is_touch_button)
5900 setRequestPosition(&base_x, &base_y, TRUE);
5902 // check if request buttons are outside of envelope and fix, if needed
5903 if (x < 0 || x + gfx->width > request.width ||
5904 y < 0 || y + gfx->height > request.height)
5906 if (id == TOOL_CTRL_ID_YES)
5909 y = request.height - 2 * request.border_size - gfx->height;
5911 else if (id == TOOL_CTRL_ID_NO)
5913 x = request.width - 2 * request.border_size - gfx->width;
5914 y = request.height - 2 * request.border_size - gfx->height;
5916 else if (id == TOOL_CTRL_ID_CONFIRM)
5918 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5919 y = request.height - 2 * request.border_size - gfx->height;
5921 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5923 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5925 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5926 y = request.height - 2 * request.border_size - gfx->height * 2;
5928 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5929 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5934 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5937 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5939 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5940 pos->size, &deco_bitmap, &deco_x, &deco_y);
5941 deco_xpos = (gfx->width - pos->size) / 2;
5942 deco_ypos = (gfx->height - pos->size) / 2;
5945 gi = CreateGadget(GDI_CUSTOM_ID, id,
5946 GDI_IMAGE_ID, graphic,
5947 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5950 GDI_WIDTH, gfx->width,
5951 GDI_HEIGHT, gfx->height,
5952 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5953 GDI_STATE, GD_BUTTON_UNPRESSED,
5954 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5955 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5956 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5957 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5958 GDI_DECORATION_SIZE, pos->size, pos->size,
5959 GDI_DECORATION_SHIFTING, 1, 1,
5960 GDI_DIRECT_DRAW, FALSE,
5961 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5962 GDI_EVENT_MASK, event_mask,
5963 GDI_CALLBACK_ACTION, HandleToolButtons,
5967 Fail("cannot create gadget");
5969 tool_gadget[id] = gi;
5973 void FreeToolButtons(void)
5977 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5978 FreeGadget(tool_gadget[i]);
5981 static void MapToolButtons(unsigned int req_state)
5983 if (req_state & REQ_ASK)
5985 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
5986 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
5987 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
5988 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
5990 else if (req_state & REQ_CONFIRM)
5992 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
5993 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
5995 else if (req_state & REQ_PLAYER)
5997 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
5998 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
5999 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
6000 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
6004 static void UnmapToolButtons(void)
6008 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
6009 UnmapGadget(tool_gadget[i]);
6012 static void HandleToolButtons(struct GadgetInfo *gi)
6014 request_gadget_id = gi->custom_id;
6017 static int getEngineElement_Ext(int element, int game_engine_type, boolean is_drawing_element)
6026 if (game_engine_type == -1)
6027 game_engine_type = level.game_engine_type;
6029 if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
6031 el_empty = EL_EMPTY;
6032 el_player = EL_BDX_PLAYER;
6033 el_sand = EL_BDX_SAND_1;
6034 el_wall = EL_BDX_WALL;
6035 el_steelwall = EL_BDX_STEELWALL;
6036 el_exit_closed = EL_BDX_EXIT_CLOSED;
6038 else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
6040 el_empty = EL_EMPTY;
6041 el_player = EL_PLAYER_1;
6044 el_steelwall = EL_STEELWALL;
6045 el_exit_closed = EL_EM_EXIT_CLOSED;
6047 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
6049 el_empty = EL_EMPTY;
6050 el_player = EL_SP_MURPHY;
6051 el_sand = EL_SP_BASE;
6052 el_wall = EL_SP_CHIP_SINGLE;
6053 el_steelwall = EL_SP_HARDWARE_GRAY;
6054 el_exit_closed = EL_SP_EXIT_CLOSED;
6056 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
6058 el_empty = EL_EMPTY;
6059 el_player = EL_MM_MCDUFFIN_DOWN;
6061 el_wall = EL_MM_WOODEN_WALL;
6062 el_steelwall = EL_MM_STEEL_WALL;
6063 el_exit_closed = EL_MM_EXIT_CLOSED;
6065 if (is_drawing_element)
6067 el_wall = EL_MM_MIRROR_START;
6068 el_sand = EL_MM_WOODEN_WALL;
6073 el_empty = EL_EMPTY;
6074 el_player = EL_PLAYER_1;
6077 el_steelwall = EL_STEELWALL;
6078 el_exit_closed = EL_EXIT_CLOSED;
6081 return (element == EL_EMPTY ? el_empty :
6082 element == EL_PLAYER_1 ? el_player :
6083 element == EL_SAND ? el_sand :
6084 element == EL_WALL ? el_wall :
6085 element == EL_STEELWALL ? el_steelwall :
6086 element == EL_EXIT_CLOSED ? el_exit_closed : EL_EMPTY);
6089 int getEngineElement(int element)
6091 return getEngineElement_Ext(element, -1, FALSE);
6094 int getDrawingElement(int element)
6096 return getEngineElement_Ext(element, -1, TRUE);
6099 static struct Mapping_BD_to_RND_object
6102 boolean is_rnd_to_bd_mapping; // unique mapping BD <-> RND
6108 bd_object_mapping_list[] =
6110 // additional RND style elements mapped to BD style elements (must be listed first)
6126 EL_STEELWALL, -1, -1
6130 EL_BD_DIAMOND, -1, -1
6150 EL_EXIT_CLOSED, -1, -1
6153 // BD style elements with their corresponding RND style elements
6161 EL_BDX_SAND_1, -1, -1
6164 O_DIRT_SLOPED_UP_RIGHT, TRUE,
6165 EL_BDX_SAND_SLOPED_UP_RIGHT, -1, -1
6168 O_DIRT_SLOPED_UP_LEFT, TRUE,
6169 EL_BDX_SAND_SLOPED_UP_LEFT, -1, -1
6172 O_DIRT_SLOPED_DOWN_LEFT, TRUE,
6173 EL_BDX_SAND_SLOPED_DOWN_LEFT, -1, -1
6176 O_DIRT_SLOPED_DOWN_RIGHT, TRUE,
6177 EL_BDX_SAND_SLOPED_DOWN_RIGHT, -1, -1
6181 EL_BDX_SAND_BALL, -1, -1
6184 O_DIRT_BALL_F, TRUE,
6185 EL_BDX_SAND_BALL_FALLING, -1, -1
6188 O_DIRT_BALL_F, FALSE,
6189 EL_BDX_SAND_BALL, ACTION_FALLING, -1
6193 EL_BDX_SAND_LOOSE, -1, -1
6196 O_DIRT_LOOSE_F, TRUE,
6197 EL_BDX_SAND_LOOSE_FALLING, -1, -1
6200 O_DIRT_LOOSE_F, FALSE,
6201 EL_BDX_SAND_LOOSE, ACTION_FALLING, -1
6205 EL_BDX_SAND_2, -1, -1
6212 O_BRICK_SLOPED_UP_RIGHT, TRUE,
6213 EL_BDX_WALL_SLOPED_UP_RIGHT, -1, -1
6216 O_BRICK_SLOPED_UP_LEFT, TRUE,
6217 EL_BDX_WALL_SLOPED_UP_LEFT, -1, -1
6220 O_BRICK_SLOPED_DOWN_LEFT, TRUE,
6221 EL_BDX_WALL_SLOPED_DOWN_LEFT, -1, -1
6224 O_BRICK_SLOPED_DOWN_RIGHT, TRUE,
6225 EL_BDX_WALL_SLOPED_DOWN_RIGHT, -1, -1
6228 O_BRICK_NON_SLOPED, TRUE,
6229 EL_BDX_WALL_NON_SLOPED, -1, -1
6233 EL_BDX_MAGIC_WALL, ACTION_ACTIVE, -1
6237 EL_BDX_EXIT_CLOSED, -1, -1
6241 EL_BDX_EXIT_OPEN, -1, -1
6244 O_PRE_INVIS_OUTBOX, TRUE,
6245 EL_BDX_INVISIBLE_EXIT_CLOSED, -1, -1
6248 O_INVIS_OUTBOX, TRUE,
6249 EL_BDX_INVISIBLE_EXIT_OPEN, -1, -1
6253 EL_BDX_STEELWALL, -1, -1
6256 O_STEEL_SLOPED_UP_RIGHT, TRUE,
6257 EL_BDX_STEELWALL_SLOPED_UP_RIGHT, -1, -1
6260 O_STEEL_SLOPED_UP_LEFT, TRUE,
6261 EL_BDX_STEELWALL_SLOPED_UP_LEFT, -1, -1
6264 O_STEEL_SLOPED_DOWN_LEFT, TRUE,
6265 EL_BDX_STEELWALL_SLOPED_DOWN_LEFT, -1, -1
6268 O_STEEL_SLOPED_DOWN_RIGHT, TRUE,
6269 EL_BDX_STEELWALL_SLOPED_DOWN_RIGHT, -1, -1
6272 O_STEEL_EXPLODABLE, TRUE,
6273 EL_BDX_STEELWALL_EXPLODABLE, -1, -1
6276 O_STEEL_EATABLE, TRUE,
6277 EL_BDX_STEELWALL_DIGGABLE, -1, -1
6280 O_BRICK_EATABLE, TRUE,
6281 EL_BDX_WALL_DIGGABLE, -1, -1
6289 EL_BDX_ROCK_FALLING, -1, -1
6293 EL_BDX_ROCK, ACTION_FALLING, -1
6296 O_FLYING_STONE, TRUE,
6297 EL_BDX_FLYING_ROCK, -1, -1
6300 O_FLYING_STONE_F, TRUE,
6301 EL_BDX_FLYING_ROCK_FLYING, -1, -1
6304 O_FLYING_STONE_F, FALSE,
6305 EL_BDX_FLYING_ROCK, ACTION_FLYING, -1
6309 EL_BDX_MEGA_ROCK, -1, -1
6312 O_MEGA_STONE_F, TRUE,
6313 EL_BDX_MEGA_ROCK_FALLING, -1, -1
6316 O_MEGA_STONE_F, FALSE,
6317 EL_BDX_MEGA_ROCK, ACTION_FALLING, -1
6321 EL_BDX_DIAMOND, -1, -1
6325 EL_BDX_DIAMOND_FALLING, -1, -1
6329 EL_BDX_DIAMOND, ACTION_FALLING, -1
6332 O_FLYING_DIAMOND, TRUE,
6333 EL_BDX_FLYING_DIAMOND, -1, -1
6336 O_FLYING_DIAMOND_F, TRUE,
6337 EL_BDX_FLYING_DIAMOND_FLYING, -1, -1
6340 O_FLYING_DIAMOND_F, FALSE,
6341 EL_BDX_FLYING_DIAMOND, ACTION_FLYING, -1
6349 EL_BDX_NUT_FALLING, -1, -1
6353 EL_BDX_NUT, ACTION_FALLING, -1
6356 O_BLADDER_SPENDER, TRUE,
6357 EL_BDX_TRAPPED_BUBBLE, -1, -1
6361 EL_BDX_INBOX, -1, -1
6364 O_H_EXPANDING_WALL, TRUE,
6365 EL_BDX_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6368 O_V_EXPANDING_WALL, TRUE,
6369 EL_BDX_EXPANDABLE_WALL_VERTICAL, -1, -1
6372 O_EXPANDING_WALL, TRUE,
6373 EL_BDX_EXPANDABLE_WALL_ANY, -1, -1
6376 O_H_EXPANDING_STEEL_WALL, TRUE,
6377 EL_BDX_EXPANDABLE_STEELWALL_HORIZONTAL, -1, -1
6380 O_V_EXPANDING_STEEL_WALL, TRUE,
6381 EL_BDX_EXPANDABLE_STEELWALL_VERTICAL, -1, -1
6384 O_EXPANDING_STEEL_WALL, TRUE,
6385 EL_BDX_EXPANDABLE_STEELWALL_ANY, -1, -1
6388 O_EXPANDING_WALL_SWITCH, TRUE,
6389 EL_BDX_EXPANDABLE_WALL_SWITCH, -1, -1
6392 O_CREATURE_SWITCH, TRUE,
6393 EL_BDX_CREATURE_SWITCH, -1, -1
6396 O_BITER_SWITCH, TRUE,
6397 EL_BDX_BITER_SWITCH_1, -1, -1
6400 O_REPLICATOR_SWITCH, TRUE,
6401 EL_BDX_REPLICATOR_SWITCH, -1, -1
6404 O_CONVEYOR_SWITCH, TRUE,
6405 EL_BDX_CONVEYOR_SWITCH, -1, -1
6408 O_CONVEYOR_DIR_SWITCH, TRUE,
6409 EL_BDX_CONVEYOR_DIR_SWITCH, -1, -1
6416 O_FALLING_WALL, TRUE,
6417 EL_BDX_FALLING_WALL, -1, -1
6420 O_FALLING_WALL_F, TRUE,
6421 EL_BDX_FALLING_WALL_FALLING, -1, -1
6424 O_FALLING_WALL_F, FALSE,
6425 EL_BDX_FALLING_WALL, ACTION_FALLING, -1
6432 O_TIME_PENALTY, TRUE,
6433 EL_BDX_TIME_PENALTY, -1, -1
6437 EL_BDX_GRAVESTONE, -1, -1
6440 O_STONE_GLUED, TRUE,
6441 EL_BDX_ROCK_GLUED, -1, -1
6444 O_DIAMOND_GLUED, TRUE,
6445 EL_BDX_DIAMOND_GLUED, -1, -1
6448 O_DIAMOND_KEY, TRUE,
6449 EL_BDX_DIAMOND_KEY, -1, -1
6452 O_TRAPPED_DIAMOND, TRUE,
6453 EL_BDX_TRAPPED_DIAMOND, -1, -1
6457 EL_BDX_CLOCK, -1, -1
6461 EL_BDX_SAND_GLUED, -1, -1
6465 EL_BDX_KEY_1, -1, -1
6469 EL_BDX_KEY_2, -1, -1
6473 EL_BDX_KEY_3, -1, -1
6477 EL_BDX_GATE_1, -1, -1
6481 EL_BDX_GATE_2, -1, -1
6485 EL_BDX_GATE_3, -1, -1
6492 O_GRAVITY_SWITCH, TRUE,
6493 EL_BDX_GRAVITY_SWITCH, -1, -1
6496 O_PNEUMATIC_HAMMER, TRUE,
6497 EL_BDX_PNEUMATIC_HAMMER, -1, -1
6501 EL_BDX_TELEPORTER, -1, -1
6505 EL_BDX_SKELETON, -1, -1
6509 EL_BDX_WATER, -1, -1
6513 EL_BDX_WATER_1, -1, -1
6517 EL_BDX_WATER, -1, -1
6521 EL_BDX_WATER_2, -1, -1
6525 EL_BDX_WATER, -1, -1
6529 EL_BDX_WATER_3, -1, -1
6533 EL_BDX_WATER, -1, -1
6537 EL_BDX_WATER_4, -1, -1
6541 EL_BDX_WATER, -1, -1
6545 EL_BDX_WATER_5, -1, -1
6549 EL_BDX_WATER, -1, -1
6553 EL_BDX_WATER_6, -1, -1
6557 EL_BDX_WATER, -1, -1
6561 EL_BDX_WATER_7, -1, -1
6565 EL_BDX_WATER, -1, -1
6569 EL_BDX_WATER_8, -1, -1
6573 EL_BDX_WATER, -1, -1
6577 EL_BDX_WATER_9, -1, -1
6581 EL_BDX_WATER, -1, -1
6585 EL_BDX_WATER_10, -1, -1
6589 EL_BDX_WATER, -1, -1
6593 EL_BDX_WATER_11, -1, -1
6597 EL_BDX_WATER, -1, -1
6601 EL_BDX_WATER_12, -1, -1
6605 EL_BDX_WATER, -1, -1
6609 EL_BDX_WATER_13, -1, -1
6613 EL_BDX_WATER, -1, -1
6617 EL_BDX_WATER_14, -1, -1
6621 EL_BDX_WATER, -1, -1
6625 EL_BDX_WATER_15, -1, -1
6629 EL_BDX_WATER, -1, -1
6633 EL_BDX_WATER_16, -1, -1
6637 EL_BDX_WATER, -1, -1
6641 EL_BDX_COW_LEFT, -1, -1
6645 EL_BDX_COW_UP, -1, -1
6649 EL_BDX_COW_RIGHT, -1, -1
6653 EL_BDX_COW_DOWN, -1, -1
6656 O_COW_ENCLOSED_1, TRUE,
6657 EL_BDX_COW_ENCLOSED_1, -1, -1
6660 O_COW_ENCLOSED_1, FALSE,
6661 EL_BDX_COW_DOWN, -1, -1
6664 O_COW_ENCLOSED_2, TRUE,
6665 EL_BDX_COW_ENCLOSED_2, -1, -1
6668 O_COW_ENCLOSED_2, FALSE,
6669 EL_BDX_COW_DOWN, -1, -1
6672 O_COW_ENCLOSED_3, TRUE,
6673 EL_BDX_COW_ENCLOSED_3, -1, -1
6676 O_COW_ENCLOSED_3, FALSE,
6677 EL_BDX_COW_DOWN, -1, -1
6680 O_COW_ENCLOSED_4, TRUE,
6681 EL_BDX_COW_ENCLOSED_4, -1, -1
6684 O_COW_ENCLOSED_4, FALSE,
6685 EL_BDX_COW_DOWN, -1, -1
6688 O_COW_ENCLOSED_5, TRUE,
6689 EL_BDX_COW_ENCLOSED_5, -1, -1
6692 O_COW_ENCLOSED_5, FALSE,
6693 EL_BDX_COW_DOWN, -1, -1
6696 O_COW_ENCLOSED_6, TRUE,
6697 EL_BDX_COW_ENCLOSED_6, -1, -1
6700 O_COW_ENCLOSED_6, FALSE,
6701 EL_BDX_COW_DOWN, -1, -1
6704 O_COW_ENCLOSED_7, TRUE,
6705 EL_BDX_COW_ENCLOSED_7, -1, -1
6708 O_COW_ENCLOSED_7, FALSE,
6709 EL_BDX_COW_DOWN, -1, -1
6712 O_WALLED_DIAMOND, TRUE,
6713 EL_BDX_WALL_DIAMOND, -1, -1
6716 O_WALLED_KEY_1, TRUE,
6717 EL_BDX_WALL_KEY_1, -1, -1
6720 O_WALLED_KEY_2, TRUE,
6721 EL_BDX_WALL_KEY_2, -1, -1
6724 O_WALLED_KEY_3, TRUE,
6725 EL_BDX_WALL_KEY_3, -1, -1
6729 EL_BDX_AMOEBA_1, -1, -1
6733 EL_BDX_AMOEBA_2, -1, -1
6737 EL_BDX_REPLICATOR, -1, -1
6740 O_CONVEYOR_LEFT, TRUE,
6741 EL_BDX_CONVEYOR_LEFT, -1, -1
6744 O_CONVEYOR_RIGHT, TRUE,
6745 EL_BDX_CONVEYOR_RIGHT, -1, -1
6753 EL_BDX_SWEET, -1, -1
6757 EL_BDX_VOODOO_DOLL, -1, -1
6761 EL_BDX_SLIME, -1, -1
6765 EL_BDX_BUBBLE, -1, -1
6769 EL_BDX_BUBBLE_1, -1, -1
6773 EL_BDX_BUBBLE, -1, -1
6777 EL_BDX_BUBBLE_2, -1, -1
6781 EL_BDX_BUBBLE, -1, -1
6785 EL_BDX_BUBBLE_3, -1, -1
6789 EL_BDX_BUBBLE, -1, -1
6793 EL_BDX_BUBBLE_4, -1, -1
6797 EL_BDX_BUBBLE, -1, -1
6801 EL_BDX_BUBBLE_5, -1, -1
6805 EL_BDX_BUBBLE, -1, -1
6809 EL_BDX_BUBBLE_6, -1, -1
6813 EL_BDX_BUBBLE, -1, -1
6817 EL_BDX_BUBBLE_7, -1, -1
6821 EL_BDX_BUBBLE, -1, -1
6825 EL_BDX_BUBBLE_8, -1, -1
6829 EL_BDX_BUBBLE, -1, -1
6832 O_WAITING_STONE, TRUE,
6833 EL_BDX_WAITING_ROCK, -1, -1
6836 O_CHASING_STONE, TRUE,
6837 EL_BDX_CHASING_ROCK, -1, -1
6841 EL_BDX_GHOST, -1, -1
6845 EL_BDX_FIREFLY_1_LEFT, -1, -1
6849 EL_BDX_FIREFLY_1_UP, -1, -1
6853 EL_BDX_FIREFLY_1_RIGHT, -1, -1
6857 EL_BDX_FIREFLY_1_DOWN, -1, -1
6860 O_ALT_FIREFLY_1, TRUE,
6861 EL_BDX_FIREFLY_2_LEFT, -1, -1
6864 O_ALT_FIREFLY_2, TRUE,
6865 EL_BDX_FIREFLY_2_UP, -1, -1
6868 O_ALT_FIREFLY_3, TRUE,
6869 EL_BDX_FIREFLY_2_RIGHT, -1, -1
6872 O_ALT_FIREFLY_4, TRUE,
6873 EL_BDX_FIREFLY_2_DOWN, -1, -1
6877 EL_BDX_BUTTERFLY_1_LEFT, -1, -1
6881 EL_BDX_BUTTERFLY_1_UP, -1, -1
6885 EL_BDX_BUTTERFLY_1_RIGHT, -1, -1
6889 EL_BDX_BUTTERFLY_1_DOWN, -1, -1
6892 O_ALT_BUTTER_1, TRUE,
6893 EL_BDX_BUTTERFLY_2_LEFT, -1, -1
6896 O_ALT_BUTTER_2, TRUE,
6897 EL_BDX_BUTTERFLY_2_UP, -1, -1
6900 O_ALT_BUTTER_3, TRUE,
6901 EL_BDX_BUTTERFLY_2_RIGHT, -1, -1
6904 O_ALT_BUTTER_4, TRUE,
6905 EL_BDX_BUTTERFLY_2_DOWN, -1, -1
6909 EL_BDX_STONEFLY_LEFT, -1, -1
6913 EL_BDX_STONEFLY_UP, -1, -1
6917 EL_BDX_STONEFLY_RIGHT, -1, -1
6921 EL_BDX_STONEFLY_DOWN, -1, -1
6925 EL_BDX_BITER_UP, -1, -1
6929 EL_BDX_BITER_RIGHT, -1, -1
6933 EL_BDX_BITER_DOWN, -1, -1
6937 EL_BDX_BITER_LEFT, -1, -1
6940 O_DRAGONFLY_1, TRUE,
6941 EL_BDX_DRAGONFLY_LEFT, -1, -1
6944 O_DRAGONFLY_2, TRUE,
6945 EL_BDX_DRAGONFLY_UP, -1, -1
6948 O_DRAGONFLY_3, TRUE,
6949 EL_BDX_DRAGONFLY_RIGHT, -1, -1
6952 O_DRAGONFLY_4, TRUE,
6953 EL_BDX_DRAGONFLY_DOWN, -1, -1
6957 EL_BDX_PLAYER_GROWING_1, -1, -1
6961 EL_BDX_PLAYER, ACTION_GROWING, -1
6965 EL_BDX_PLAYER_GROWING_2, -1, -1
6969 EL_BDX_PLAYER, ACTION_GROWING, -1
6973 EL_BDX_PLAYER_GROWING_3, -1, -1
6977 EL_BDX_PLAYER, ACTION_GROWING, -1
6981 EL_BDX_PLAYER, -1, -1
6984 O_PLAYER_BOMB, TRUE,
6985 EL_BDX_PLAYER_WITH_BOMB, -1, -1
6988 O_PLAYER_ROCKET_LAUNCHER, TRUE,
6989 EL_BDX_PLAYER_WITH_ROCKET_LAUNCHER, -1, -1
6992 O_PLAYER_GLUED, TRUE,
6993 EL_BDX_PLAYER_GLUED, -1, -1
6996 O_PLAYER_STIRRING, TRUE,
6997 EL_BDX_PLAYER_STIRRING, -1, -1
7000 O_ROCKET_LAUNCHER, TRUE,
7001 EL_BDX_ROCKET_LAUNCHER, -1, -1
7005 EL_BDX_ROCKET_RIGHT, -1, -1
7009 EL_BDX_ROCKET_UP, -1, -1
7013 EL_BDX_ROCKET_LEFT, -1, -1
7017 EL_BDX_ROCKET_DOWN, -1, -1
7024 O_BOMB_TICK_1, TRUE,
7025 EL_BDX_BOMB_TICKING_1, -1, -1
7028 O_BOMB_TICK_1, FALSE,
7029 EL_BDX_BOMB, ACTION_ACTIVE, -1
7032 O_BOMB_TICK_2, TRUE,
7033 EL_BDX_BOMB_TICKING_2, -1, -1
7036 O_BOMB_TICK_2, FALSE,
7037 EL_BDX_BOMB, ACTION_ACTIVE, -1
7040 O_BOMB_TICK_3, TRUE,
7041 EL_BDX_BOMB_TICKING_3, -1, -1
7044 O_BOMB_TICK_3, FALSE,
7045 EL_BDX_BOMB, ACTION_ACTIVE, -1
7048 O_BOMB_TICK_4, TRUE,
7049 EL_BDX_BOMB_TICKING_4, -1, -1
7052 O_BOMB_TICK_4, FALSE,
7053 EL_BDX_BOMB, ACTION_ACTIVE, -1
7056 O_BOMB_TICK_5, TRUE,
7057 EL_BDX_BOMB_TICKING_5, -1, -1
7060 O_BOMB_TICK_5, FALSE,
7061 EL_BDX_BOMB, ACTION_ACTIVE, -1
7064 O_BOMB_TICK_6, TRUE,
7065 EL_BDX_BOMB_TICKING_6, -1, -1
7068 O_BOMB_TICK_6, FALSE,
7069 EL_BDX_BOMB, ACTION_ACTIVE, -1
7072 O_BOMB_TICK_7, TRUE,
7073 EL_BDX_BOMB_TICKING_7, -1, -1
7076 O_BOMB_TICK_7, FALSE,
7077 EL_BDX_BOMB, ACTION_ACTIVE, -1
7081 EL_BDX_NITRO_PACK, -1, -1
7084 O_NITRO_PACK_F, TRUE,
7085 EL_BDX_NITRO_PACK_FALLING, -1, -1
7088 O_NITRO_PACK_F, FALSE,
7089 EL_BDX_NITRO_PACK, ACTION_FALLING, -1
7092 O_PRE_CLOCK_1, TRUE,
7093 EL_BDX_CLOCK_GROWING_1, -1, -1
7096 O_PRE_CLOCK_1, FALSE,
7097 EL_BDX_CLOCK, ACTION_GROWING, -1
7100 O_PRE_CLOCK_2, TRUE,
7101 EL_BDX_CLOCK_GROWING_2, -1, -1
7104 O_PRE_CLOCK_2, FALSE,
7105 EL_BDX_CLOCK, ACTION_GROWING, -1
7108 O_PRE_CLOCK_3, TRUE,
7109 EL_BDX_CLOCK_GROWING_3, -1, -1
7112 O_PRE_CLOCK_3, FALSE,
7113 EL_BDX_CLOCK, ACTION_GROWING, -1
7116 O_PRE_CLOCK_4, TRUE,
7117 EL_BDX_CLOCK_GROWING_4, -1, -1
7120 O_PRE_CLOCK_4, FALSE,
7121 EL_BDX_CLOCK, ACTION_GROWING, -1
7125 EL_BDX_DIAMOND_GROWING_1, -1, -1
7129 EL_BDX_DIAMOND, ACTION_GROWING, -1
7133 EL_BDX_DIAMOND_GROWING_2, -1, -1
7137 EL_BDX_DIAMOND, ACTION_GROWING, -1
7141 EL_BDX_DIAMOND_GROWING_3, -1, -1
7145 EL_BDX_DIAMOND, ACTION_GROWING, -1
7149 EL_BDX_DIAMOND_GROWING_4, -1, -1
7153 EL_BDX_DIAMOND, ACTION_GROWING, -1
7157 EL_BDX_DIAMOND_GROWING_5, -1, -1
7161 EL_BDX_DIAMOND, ACTION_GROWING, -1
7165 EL_BDX_EXPLODING_1, -1, -1
7169 EL_BDX_DEFAULT, ACTION_EXPLODING, -1
7173 EL_BDX_EXPLODING_2, -1, -1
7177 EL_BDX_DEFAULT, ACTION_EXPLODING, -1
7181 EL_BDX_EXPLODING_3, -1, -1
7185 EL_BDX_DEFAULT, ACTION_EXPLODING, -1
7189 EL_BDX_EXPLODING_4, -1, -1
7193 EL_BDX_DEFAULT, ACTION_EXPLODING, -1
7197 EL_BDX_EXPLODING_5, -1, -1
7201 EL_BDX_DEFAULT, ACTION_EXPLODING, -1
7204 O_PRE_STONE_1, TRUE,
7205 EL_BDX_ROCK_GROWING_1, -1, -1
7208 O_PRE_STONE_1, FALSE,
7209 EL_BDX_ROCK, ACTION_GROWING, -1
7212 O_PRE_STONE_2, TRUE,
7213 EL_BDX_ROCK_GROWING_2, -1, -1
7216 O_PRE_STONE_2, FALSE,
7217 EL_BDX_ROCK, ACTION_GROWING, -1
7220 O_PRE_STONE_3, TRUE,
7221 EL_BDX_ROCK_GROWING_3, -1, -1
7224 O_PRE_STONE_3, FALSE,
7225 EL_BDX_ROCK, ACTION_GROWING, -1
7228 O_PRE_STONE_4, TRUE,
7229 EL_BDX_ROCK_GROWING_4, -1, -1
7232 O_PRE_STONE_4, FALSE,
7233 EL_BDX_ROCK, ACTION_GROWING, -1
7236 O_PRE_STEEL_1, TRUE,
7237 EL_BDX_STEELWALL_GROWING_1, -1, -1
7240 O_PRE_STEEL_1, FALSE,
7241 EL_BDX_STEELWALL, ACTION_GROWING, -1
7244 O_PRE_STEEL_2, TRUE,
7245 EL_BDX_STEELWALL_GROWING_2, -1, -1
7248 O_PRE_STEEL_2, FALSE,
7249 EL_BDX_STEELWALL, ACTION_GROWING, -1
7252 O_PRE_STEEL_3, TRUE,
7253 EL_BDX_STEELWALL_GROWING_3, -1, -1
7256 O_PRE_STEEL_3, FALSE,
7257 EL_BDX_STEELWALL, ACTION_GROWING, -1
7260 O_PRE_STEEL_4, TRUE,
7261 EL_BDX_STEELWALL_GROWING_4, -1, -1
7264 O_PRE_STEEL_4, FALSE,
7265 EL_BDX_STEELWALL, ACTION_GROWING, -1
7268 O_GHOST_EXPL_1, TRUE,
7269 EL_BDX_GHOST_EXPLODING_1, -1, -1
7272 O_GHOST_EXPL_1, FALSE,
7273 EL_BDX_GHOST, ACTION_EXPLODING, -1
7276 O_GHOST_EXPL_2, TRUE,
7277 EL_BDX_GHOST_EXPLODING_2, -1, -1
7280 O_GHOST_EXPL_2, FALSE,
7281 EL_BDX_GHOST, ACTION_EXPLODING, -1
7284 O_GHOST_EXPL_3, TRUE,
7285 EL_BDX_GHOST_EXPLODING_3, -1, -1
7288 O_GHOST_EXPL_3, FALSE,
7289 EL_BDX_GHOST, ACTION_EXPLODING, -1
7292 O_GHOST_EXPL_4, TRUE,
7293 EL_BDX_GHOST_EXPLODING_4, -1, -1
7296 O_GHOST_EXPL_4, FALSE,
7297 EL_BDX_GHOST, ACTION_EXPLODING, -1
7300 O_BOMB_EXPL_1, TRUE,
7301 EL_BDX_BOMB_EXPLODING_1, -1, -1
7304 O_BOMB_EXPL_1, FALSE,
7305 EL_BDX_BOMB, ACTION_EXPLODING, -1
7308 O_BOMB_EXPL_2, TRUE,
7309 EL_BDX_BOMB_EXPLODING_2, -1, -1
7312 O_BOMB_EXPL_2, FALSE,
7313 EL_BDX_BOMB, ACTION_EXPLODING, -1
7316 O_BOMB_EXPL_3, TRUE,
7317 EL_BDX_BOMB_EXPLODING_3, -1, -1
7320 O_BOMB_EXPL_3, FALSE,
7321 EL_BDX_BOMB, ACTION_EXPLODING, -1
7324 O_BOMB_EXPL_4, TRUE,
7325 EL_BDX_BOMB_EXPLODING_4, -1, -1
7328 O_BOMB_EXPL_4, FALSE,
7329 EL_BDX_BOMB, ACTION_EXPLODING, -1
7332 O_NITRO_EXPL_1, TRUE,
7333 EL_BDX_NITRO_PACK_EXPLODING_1, -1, -1
7336 O_NITRO_EXPL_1, FALSE,
7337 EL_BDX_NITRO_PACK, ACTION_EXPLODING, -1
7340 O_NITRO_EXPL_2, TRUE,
7341 EL_BDX_NITRO_PACK_EXPLODING_2, -1, -1
7344 O_NITRO_EXPL_2, FALSE,
7345 EL_BDX_NITRO_PACK, ACTION_EXPLODING, -1
7348 O_NITRO_EXPL_3, TRUE,
7349 EL_BDX_NITRO_PACK_EXPLODING_3, -1, -1
7352 O_NITRO_EXPL_3, FALSE,
7353 EL_BDX_NITRO_PACK, ACTION_EXPLODING, -1
7356 O_NITRO_EXPL_4, TRUE,
7357 EL_BDX_NITRO_PACK_EXPLODING_4, -1, -1
7360 O_NITRO_EXPL_4, FALSE,
7361 EL_BDX_NITRO_PACK, ACTION_EXPLODING, -1
7364 O_NITRO_PACK_EXPLODE, TRUE,
7365 EL_BDX_NITRO_PACK_EXPLODING, -1, -1
7368 O_NITRO_PACK_EXPLODE, FALSE,
7369 EL_BDX_NITRO_PACK, ACTION_EXPLODING, -1
7372 O_AMOEBA_2_EXPL_1, TRUE,
7373 EL_BDX_AMOEBA_2_EXPLODING_1, -1, -1
7376 O_AMOEBA_2_EXPL_1, FALSE,
7377 EL_BDX_AMOEBA_2, ACTION_EXPLODING, -1
7380 O_AMOEBA_2_EXPL_2, TRUE,
7381 EL_BDX_AMOEBA_2_EXPLODING_2, -1, -1
7384 O_AMOEBA_2_EXPL_2, FALSE,
7385 EL_BDX_AMOEBA_2, ACTION_EXPLODING, -1
7388 O_AMOEBA_2_EXPL_3, TRUE,
7389 EL_BDX_AMOEBA_2_EXPLODING_3, -1, -1
7392 O_AMOEBA_2_EXPL_3, FALSE,
7393 EL_BDX_AMOEBA_2, ACTION_EXPLODING, -1
7396 O_AMOEBA_2_EXPL_4, TRUE,
7397 EL_BDX_AMOEBA_2_EXPLODING_4, -1, -1
7400 O_AMOEBA_2_EXPL_4, FALSE,
7401 EL_BDX_AMOEBA_2, ACTION_EXPLODING, -1
7405 EL_BDX_NUT_BREAKING_1, -1, -1
7408 O_NUT_EXPL_1, FALSE,
7409 EL_BDX_NUT, ACTION_BREAKING, -1
7413 EL_BDX_NUT_BREAKING_2, -1, -1
7416 O_NUT_EXPL_2, FALSE,
7417 EL_BDX_NUT, ACTION_BREAKING, -1
7421 EL_BDX_NUT_BREAKING_3, -1, -1
7424 O_NUT_EXPL_3, FALSE,
7425 EL_BDX_NUT, ACTION_BREAKING, -1
7429 EL_BDX_NUT_BREAKING_4, -1, -1
7432 O_NUT_EXPL_4, FALSE,
7433 EL_BDX_NUT, ACTION_BREAKING, -1
7436 O_PLAYER_PNEUMATIC_LEFT, FALSE,
7437 EL_BDX_PLAYER, ACTION_HITTING, MV_BIT_LEFT
7440 O_PLAYER_PNEUMATIC_RIGHT, FALSE,
7441 EL_BDX_PLAYER, ACTION_HITTING, MV_BIT_RIGHT
7444 O_PNEUMATIC_ACTIVE_LEFT, FALSE,
7445 EL_BDX_PNEUMATIC_HAMMER, ACTION_HITTING, MV_BIT_LEFT
7448 O_PNEUMATIC_ACTIVE_RIGHT, FALSE,
7449 EL_BDX_PNEUMATIC_HAMMER, ACTION_HITTING, MV_BIT_RIGHT
7452 // helper (runtime) elements
7455 O_FAKE_BONUS, FALSE,
7456 EL_BDX_FAKE_BONUS, -1, -1
7459 O_INBOX_CLOSED, FALSE,
7460 EL_BDX_INBOX, -1, -1
7463 O_INBOX_OPEN, FALSE,
7464 EL_BDX_INBOX, ACTION_OPENING, -1
7467 O_OUTBOX_CLOSED, FALSE,
7468 EL_BDX_EXIT_CLOSED, -1, -1
7471 O_OUTBOX_OPEN, FALSE,
7472 EL_BDX_EXIT_OPEN, -1, -1
7476 EL_BDX_COVERED, -1, -1
7479 O_PLAYER_LEFT, FALSE,
7480 EL_BDX_PLAYER, ACTION_MOVING, MV_BIT_LEFT
7483 O_PLAYER_RIGHT, FALSE,
7484 EL_BDX_PLAYER, ACTION_MOVING, MV_BIT_RIGHT
7488 EL_BDX_PLAYER, ACTION_MOVING, MV_BIT_UP
7491 O_PLAYER_DOWN, FALSE,
7492 EL_BDX_PLAYER, ACTION_MOVING, MV_BIT_DOWN
7495 O_PLAYER_BLINK, FALSE,
7496 EL_BDX_PLAYER, ACTION_BORING_1, -1
7499 O_PLAYER_TAP, FALSE,
7500 EL_BDX_PLAYER, ACTION_BORING_2, -1
7503 O_PLAYER_TAP_BLINK, FALSE,
7504 EL_BDX_PLAYER, ACTION_BORING_3, -1
7507 O_PLAYER_PUSH_LEFT, FALSE,
7508 EL_BDX_PLAYER, ACTION_PUSHING, MV_BIT_LEFT
7511 O_PLAYER_PUSH_RIGHT, FALSE,
7512 EL_BDX_PLAYER, ACTION_PUSHING, MV_BIT_RIGHT
7515 O_CREATURE_SWITCH_ON, FALSE,
7516 EL_BDX_CREATURE_SWITCH_ACTIVE, -1, -1
7519 O_EXPANDING_WALL_SWITCH_HORIZ, FALSE,
7520 EL_BDX_EXPANDABLE_WALL_SWITCH, -1, -1
7523 O_EXPANDING_WALL_SWITCH_VERT, FALSE,
7524 EL_BDX_EXPANDABLE_WALL_SWITCH_ACTIVE, -1, -1
7527 O_GRAVITY_SWITCH_ACTIVE, FALSE,
7528 EL_BDX_GRAVITY_SWITCH_ACTIVE, -1, -1
7531 O_REPLICATOR_SWITCH_OFF, FALSE,
7532 EL_BDX_REPLICATOR_SWITCH, -1, -1
7535 O_REPLICATOR_SWITCH_ON, FALSE,
7536 EL_BDX_REPLICATOR_SWITCH_ACTIVE, -1, -1
7539 O_CONVEYOR_DIR_NORMAL, FALSE,
7540 EL_BDX_CONVEYOR_DIR_SWITCH, -1, -1
7543 O_CONVEYOR_DIR_CHANGED, FALSE,
7544 EL_BDX_CONVEYOR_DIR_SWITCH_ACTIVE, -1, -1
7547 O_CONVEYOR_SWITCH_OFF, FALSE,
7548 EL_BDX_CONVEYOR_SWITCH, -1, -1
7551 O_CONVEYOR_SWITCH_ON, FALSE,
7552 EL_BDX_CONVEYOR_SWITCH_ACTIVE, -1, -1
7555 O_MAGIC_WALL_ACTIVE, FALSE,
7556 EL_BDX_MAGIC_WALL_ACTIVE, -1, -1
7559 O_REPLICATOR_ACTIVE, FALSE,
7560 EL_BDX_REPLICATOR_ACTIVE, -1, -1
7563 O_CONVEYOR_LEFT_ACTIVE, FALSE,
7564 EL_BDX_CONVEYOR_LEFT_ACTIVE, -1, -1
7567 O_CONVEYOR_RIGHT_ACTIVE, FALSE,
7568 EL_BDX_CONVEYOR_RIGHT_ACTIVE, -1, -1
7571 O_BITER_SWITCH_1, FALSE,
7572 EL_BDX_BITER_SWITCH_1, -1, -1
7575 O_BITER_SWITCH_2, FALSE,
7576 EL_BDX_BITER_SWITCH_2, -1, -1
7579 O_BITER_SWITCH_3, FALSE,
7580 EL_BDX_BITER_SWITCH_3, -1, -1
7583 O_BITER_SWITCH_4, FALSE,
7584 EL_BDX_BITER_SWITCH_4, -1, -1
7593 int map_element_RND_to_BD_cave(int element_rnd)
7595 static unsigned short mapping_RND_to_BD[NUM_FILE_ELEMENTS];
7596 static boolean mapping_initialized = FALSE;
7598 if (!mapping_initialized)
7602 // return "O_UNKNOWN" for all undefined elements in mapping array
7603 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7604 mapping_RND_to_BD[i] = O_UNKNOWN;
7606 for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
7607 if (bd_object_mapping_list[i].is_rnd_to_bd_mapping)
7608 mapping_RND_to_BD[bd_object_mapping_list[i].element_rnd] =
7609 bd_object_mapping_list[i].element_bd;
7611 mapping_initialized = TRUE;
7614 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7616 Warn("invalid RND element %d", element_rnd);
7621 return mapping_RND_to_BD[element_rnd];
7624 int map_element_RND_to_BD_effect(int element_rnd, int action)
7626 static unsigned short mapping_RND_to_BD[NUM_FILE_ELEMENTS][NUM_ACTIONS];
7627 static boolean mapping_initialized = FALSE;
7629 if (!mapping_initialized)
7633 // return "O_UNKNOWN" for all undefined elements in mapping array
7634 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7635 for (j = 0; j < NUM_ACTIONS; j++)
7636 mapping_RND_to_BD[i][j] = O_UNKNOWN;
7638 for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
7640 int element_rnd = bd_object_mapping_list[i].element_rnd;
7641 int element_bd = bd_object_mapping_list[i].element_bd;
7642 int action = bd_object_mapping_list[i].action;
7645 mapping_RND_to_BD[element_rnd][action] = element_bd;
7648 mapping_initialized = TRUE;
7651 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7653 Warn("invalid RND element %d", element_rnd);
7658 if (action < 0 || action >= NUM_ACTIONS)
7660 Warn("invalid action %d", action);
7665 return mapping_RND_to_BD[element_rnd][action];
7668 int map_element_BD_to_RND_cave(int element_bd)
7670 static unsigned short mapping_BD_to_RND[O_MAX_ALL];
7671 static boolean mapping_initialized = FALSE;
7673 if (!mapping_initialized)
7677 // return "EL_UNKNOWN" for all undefined elements in mapping array
7678 for (i = 0; i < O_MAX_ALL; i++)
7679 mapping_BD_to_RND[i] = EL_UNKNOWN;
7681 for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
7682 if (bd_object_mapping_list[i].is_rnd_to_bd_mapping)
7683 mapping_BD_to_RND[bd_object_mapping_list[i].element_bd] =
7684 bd_object_mapping_list[i].element_rnd;
7686 mapping_initialized = TRUE;
7689 // always map (scanned) run-time elements to normal elements
7690 element_bd &= O_MASK;
7692 if (element_bd < 0 || element_bd >= O_MAX_ALL)
7694 Warn("invalid BD element %d", element_bd);
7699 return mapping_BD_to_RND[element_bd];
7702 int map_element_BD_to_RND_game(int element_bd)
7704 static unsigned short mapping_BD_to_RND[O_MAX_ALL];
7705 static boolean mapping_initialized = FALSE;
7707 if (!mapping_initialized)
7711 // return "EL_UNKNOWN" for all undefined elements in mapping array
7712 for (i = 0; i < O_MAX_ALL; i++)
7713 mapping_BD_to_RND[i] = EL_UNKNOWN;
7715 for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
7716 mapping_BD_to_RND[bd_object_mapping_list[i].element_bd] =
7717 bd_object_mapping_list[i].element_rnd;
7719 mapping_initialized = TRUE;
7722 // always map (scanned) run-time elements to normal elements
7723 element_bd &= O_MASK;
7725 if (element_bd < 0 || element_bd >= O_MAX_ALL)
7727 Warn("invalid BD element %d", element_bd);
7732 return mapping_BD_to_RND[element_bd];
7735 static struct Mapping_EM_to_RND_object
7738 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
7739 boolean is_backside; // backside of moving element
7745 em_object_mapping_list[GAME_TILE_MAX + 1] =
7748 Zborder, FALSE, FALSE,
7752 Zplayer, FALSE, FALSE,
7761 Ztank, FALSE, FALSE,
7765 Zeater, FALSE, FALSE,
7769 Zdynamite, FALSE, FALSE,
7773 Zboom, FALSE, FALSE,
7778 Xchain, FALSE, FALSE,
7779 EL_DEFAULT, ACTION_EXPLODING, -1
7782 Xboom_bug, FALSE, FALSE,
7783 EL_BUG, ACTION_EXPLODING, -1
7786 Xboom_tank, FALSE, FALSE,
7787 EL_SPACESHIP, ACTION_EXPLODING, -1
7790 Xboom_android, FALSE, FALSE,
7791 EL_EMC_ANDROID, ACTION_OTHER, -1
7794 Xboom_1, FALSE, FALSE,
7795 EL_DEFAULT, ACTION_EXPLODING, -1
7798 Xboom_2, FALSE, FALSE,
7799 EL_DEFAULT, ACTION_EXPLODING, -1
7803 Xblank, TRUE, FALSE,
7808 Xsplash_e, FALSE, FALSE,
7809 EL_ACID_SPLASH_RIGHT, -1, -1
7812 Xsplash_w, FALSE, FALSE,
7813 EL_ACID_SPLASH_LEFT, -1, -1
7817 Xplant, TRUE, FALSE,
7818 EL_EMC_PLANT, -1, -1
7821 Yplant, FALSE, FALSE,
7822 EL_EMC_PLANT, -1, -1
7826 Xacid_1, TRUE, FALSE,
7830 Xacid_2, FALSE, FALSE,
7834 Xacid_3, FALSE, FALSE,
7838 Xacid_4, FALSE, FALSE,
7842 Xacid_5, FALSE, FALSE,
7846 Xacid_6, FALSE, FALSE,
7850 Xacid_7, FALSE, FALSE,
7854 Xacid_8, FALSE, FALSE,
7859 Xfake_acid_1, TRUE, FALSE,
7860 EL_EMC_FAKE_ACID, -1, -1
7863 Xfake_acid_2, FALSE, FALSE,
7864 EL_EMC_FAKE_ACID, -1, -1
7867 Xfake_acid_3, FALSE, FALSE,
7868 EL_EMC_FAKE_ACID, -1, -1
7871 Xfake_acid_4, FALSE, FALSE,
7872 EL_EMC_FAKE_ACID, -1, -1
7875 Xfake_acid_5, FALSE, FALSE,
7876 EL_EMC_FAKE_ACID, -1, -1
7879 Xfake_acid_6, FALSE, FALSE,
7880 EL_EMC_FAKE_ACID, -1, -1
7883 Xfake_acid_7, FALSE, FALSE,
7884 EL_EMC_FAKE_ACID, -1, -1
7887 Xfake_acid_8, FALSE, FALSE,
7888 EL_EMC_FAKE_ACID, -1, -1
7892 Xfake_acid_1_player, FALSE, FALSE,
7893 EL_EMC_FAKE_ACID, -1, -1
7896 Xfake_acid_2_player, FALSE, FALSE,
7897 EL_EMC_FAKE_ACID, -1, -1
7900 Xfake_acid_3_player, FALSE, FALSE,
7901 EL_EMC_FAKE_ACID, -1, -1
7904 Xfake_acid_4_player, FALSE, FALSE,
7905 EL_EMC_FAKE_ACID, -1, -1
7908 Xfake_acid_5_player, FALSE, FALSE,
7909 EL_EMC_FAKE_ACID, -1, -1
7912 Xfake_acid_6_player, FALSE, FALSE,
7913 EL_EMC_FAKE_ACID, -1, -1
7916 Xfake_acid_7_player, FALSE, FALSE,
7917 EL_EMC_FAKE_ACID, -1, -1
7920 Xfake_acid_8_player, FALSE, FALSE,
7921 EL_EMC_FAKE_ACID, -1, -1
7925 Xgrass, TRUE, FALSE,
7926 EL_EMC_GRASS, -1, -1
7929 Ygrass_nB, FALSE, FALSE,
7930 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
7933 Ygrass_eB, FALSE, FALSE,
7934 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
7937 Ygrass_sB, FALSE, FALSE,
7938 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
7941 Ygrass_wB, FALSE, FALSE,
7942 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
7950 Ydirt_nB, FALSE, FALSE,
7951 EL_SAND, ACTION_DIGGING, MV_BIT_UP
7954 Ydirt_eB, FALSE, FALSE,
7955 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
7958 Ydirt_sB, FALSE, FALSE,
7959 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
7962 Ydirt_wB, FALSE, FALSE,
7963 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
7967 Xandroid, TRUE, FALSE,
7968 EL_EMC_ANDROID, ACTION_ACTIVE, -1
7971 Xandroid_1_n, FALSE, FALSE,
7972 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
7975 Xandroid_2_n, FALSE, FALSE,
7976 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
7979 Xandroid_1_e, FALSE, FALSE,
7980 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
7983 Xandroid_2_e, FALSE, FALSE,
7984 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
7987 Xandroid_1_w, FALSE, FALSE,
7988 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
7991 Xandroid_2_w, FALSE, FALSE,
7992 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
7995 Xandroid_1_s, FALSE, FALSE,
7996 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
7999 Xandroid_2_s, FALSE, FALSE,
8000 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
8003 Yandroid_n, FALSE, FALSE,
8004 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
8007 Yandroid_nB, FALSE, TRUE,
8008 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
8011 Yandroid_ne, FALSE, FALSE,
8012 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
8015 Yandroid_neB, FALSE, TRUE,
8016 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
8019 Yandroid_e, FALSE, FALSE,
8020 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
8023 Yandroid_eB, FALSE, TRUE,
8024 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
8027 Yandroid_se, FALSE, FALSE,
8028 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
8031 Yandroid_seB, FALSE, TRUE,
8032 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
8035 Yandroid_s, FALSE, FALSE,
8036 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
8039 Yandroid_sB, FALSE, TRUE,
8040 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
8043 Yandroid_sw, FALSE, FALSE,
8044 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
8047 Yandroid_swB, FALSE, TRUE,
8048 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
8051 Yandroid_w, FALSE, FALSE,
8052 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
8055 Yandroid_wB, FALSE, TRUE,
8056 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
8059 Yandroid_nw, FALSE, FALSE,
8060 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
8063 Yandroid_nwB, FALSE, TRUE,
8064 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
8068 Xeater_n, TRUE, FALSE,
8069 EL_YAMYAM_UP, -1, -1
8072 Xeater_e, TRUE, FALSE,
8073 EL_YAMYAM_RIGHT, -1, -1
8076 Xeater_w, TRUE, FALSE,
8077 EL_YAMYAM_LEFT, -1, -1
8080 Xeater_s, TRUE, FALSE,
8081 EL_YAMYAM_DOWN, -1, -1
8084 Yeater_n, FALSE, FALSE,
8085 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
8088 Yeater_nB, FALSE, TRUE,
8089 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
8092 Yeater_e, FALSE, FALSE,
8093 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
8096 Yeater_eB, FALSE, TRUE,
8097 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
8100 Yeater_s, FALSE, FALSE,
8101 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
8104 Yeater_sB, FALSE, TRUE,
8105 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
8108 Yeater_w, FALSE, FALSE,
8109 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
8112 Yeater_wB, FALSE, TRUE,
8113 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
8116 Yeater_stone, FALSE, FALSE,
8117 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
8120 Yeater_spring, FALSE, FALSE,
8121 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
8125 Xalien, TRUE, FALSE,
8129 Xalien_pause, FALSE, FALSE,
8133 Yalien_n, FALSE, FALSE,
8134 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
8137 Yalien_nB, FALSE, TRUE,
8138 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
8141 Yalien_e, FALSE, FALSE,
8142 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
8145 Yalien_eB, FALSE, TRUE,
8146 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
8149 Yalien_s, FALSE, FALSE,
8150 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
8153 Yalien_sB, FALSE, TRUE,
8154 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
8157 Yalien_w, FALSE, FALSE,
8158 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
8161 Yalien_wB, FALSE, TRUE,
8162 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
8165 Yalien_stone, FALSE, FALSE,
8166 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
8169 Yalien_spring, FALSE, FALSE,
8170 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
8174 Xbug_1_n, TRUE, FALSE,
8178 Xbug_1_e, TRUE, FALSE,
8179 EL_BUG_RIGHT, -1, -1
8182 Xbug_1_s, TRUE, FALSE,
8186 Xbug_1_w, TRUE, FALSE,
8190 Xbug_2_n, FALSE, FALSE,
8194 Xbug_2_e, FALSE, FALSE,
8195 EL_BUG_RIGHT, -1, -1
8198 Xbug_2_s, FALSE, FALSE,
8202 Xbug_2_w, FALSE, FALSE,
8206 Ybug_n, FALSE, FALSE,
8207 EL_BUG, ACTION_MOVING, MV_BIT_UP
8210 Ybug_nB, FALSE, TRUE,
8211 EL_BUG, ACTION_MOVING, MV_BIT_UP
8214 Ybug_e, FALSE, FALSE,
8215 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
8218 Ybug_eB, FALSE, TRUE,
8219 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
8222 Ybug_s, FALSE, FALSE,
8223 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
8226 Ybug_sB, FALSE, TRUE,
8227 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
8230 Ybug_w, FALSE, FALSE,
8231 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
8234 Ybug_wB, FALSE, TRUE,
8235 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
8238 Ybug_w_n, FALSE, FALSE,
8239 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
8242 Ybug_n_e, FALSE, FALSE,
8243 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
8246 Ybug_e_s, FALSE, FALSE,
8247 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
8250 Ybug_s_w, FALSE, FALSE,
8251 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
8254 Ybug_e_n, FALSE, FALSE,
8255 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
8258 Ybug_s_e, FALSE, FALSE,
8259 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
8262 Ybug_w_s, FALSE, FALSE,
8263 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
8266 Ybug_n_w, FALSE, FALSE,
8267 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
8270 Ybug_stone, FALSE, FALSE,
8271 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
8274 Ybug_spring, FALSE, FALSE,
8275 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
8279 Xtank_1_n, TRUE, FALSE,
8280 EL_SPACESHIP_UP, -1, -1
8283 Xtank_1_e, TRUE, FALSE,
8284 EL_SPACESHIP_RIGHT, -1, -1
8287 Xtank_1_s, TRUE, FALSE,
8288 EL_SPACESHIP_DOWN, -1, -1
8291 Xtank_1_w, TRUE, FALSE,
8292 EL_SPACESHIP_LEFT, -1, -1
8295 Xtank_2_n, FALSE, FALSE,
8296 EL_SPACESHIP_UP, -1, -1
8299 Xtank_2_e, FALSE, FALSE,
8300 EL_SPACESHIP_RIGHT, -1, -1
8303 Xtank_2_s, FALSE, FALSE,
8304 EL_SPACESHIP_DOWN, -1, -1
8307 Xtank_2_w, FALSE, FALSE,
8308 EL_SPACESHIP_LEFT, -1, -1
8311 Ytank_n, FALSE, FALSE,
8312 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
8315 Ytank_nB, FALSE, TRUE,
8316 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
8319 Ytank_e, FALSE, FALSE,
8320 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
8323 Ytank_eB, FALSE, TRUE,
8324 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
8327 Ytank_s, FALSE, FALSE,
8328 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
8331 Ytank_sB, FALSE, TRUE,
8332 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
8335 Ytank_w, FALSE, FALSE,
8336 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
8339 Ytank_wB, FALSE, TRUE,
8340 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
8343 Ytank_w_n, FALSE, FALSE,
8344 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
8347 Ytank_n_e, FALSE, FALSE,
8348 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
8351 Ytank_e_s, FALSE, FALSE,
8352 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
8355 Ytank_s_w, FALSE, FALSE,
8356 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
8359 Ytank_e_n, FALSE, FALSE,
8360 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
8363 Ytank_s_e, FALSE, FALSE,
8364 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
8367 Ytank_w_s, FALSE, FALSE,
8368 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
8371 Ytank_n_w, FALSE, FALSE,
8372 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
8375 Ytank_stone, FALSE, FALSE,
8376 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
8379 Ytank_spring, FALSE, FALSE,
8380 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
8384 Xemerald, TRUE, FALSE,
8388 Xemerald_pause, FALSE, FALSE,
8392 Xemerald_fall, FALSE, FALSE,
8396 Xemerald_shine, FALSE, FALSE,
8397 EL_EMERALD, ACTION_TWINKLING, -1
8400 Yemerald_s, FALSE, FALSE,
8401 EL_EMERALD, ACTION_FALLING, -1
8404 Yemerald_sB, FALSE, TRUE,
8405 EL_EMERALD, ACTION_FALLING, -1
8408 Yemerald_e, FALSE, FALSE,
8409 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
8412 Yemerald_eB, FALSE, TRUE,
8413 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
8416 Yemerald_w, FALSE, FALSE,
8417 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
8420 Yemerald_wB, FALSE, TRUE,
8421 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
8424 Yemerald_blank, FALSE, FALSE,
8425 EL_EMERALD, ACTION_COLLECTING, -1
8429 Xdiamond, TRUE, FALSE,
8433 Xdiamond_pause, FALSE, FALSE,
8437 Xdiamond_fall, FALSE, FALSE,
8441 Xdiamond_shine, FALSE, FALSE,
8442 EL_DIAMOND, ACTION_TWINKLING, -1
8445 Ydiamond_s, FALSE, FALSE,
8446 EL_DIAMOND, ACTION_FALLING, -1
8449 Ydiamond_sB, FALSE, TRUE,
8450 EL_DIAMOND, ACTION_FALLING, -1
8453 Ydiamond_e, FALSE, FALSE,
8454 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
8457 Ydiamond_eB, FALSE, TRUE,
8458 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
8461 Ydiamond_w, FALSE, FALSE,
8462 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
8465 Ydiamond_wB, FALSE, TRUE,
8466 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
8469 Ydiamond_blank, FALSE, FALSE,
8470 EL_DIAMOND, ACTION_COLLECTING, -1
8473 Ydiamond_stone, FALSE, FALSE,
8474 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
8478 Xstone, TRUE, FALSE,
8482 Xstone_pause, FALSE, FALSE,
8486 Xstone_fall, FALSE, FALSE,
8490 Ystone_s, FALSE, FALSE,
8491 EL_ROCK, ACTION_FALLING, -1
8494 Ystone_sB, FALSE, TRUE,
8495 EL_ROCK, ACTION_FALLING, -1
8498 Ystone_e, FALSE, FALSE,
8499 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
8502 Ystone_eB, FALSE, TRUE,
8503 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
8506 Ystone_w, FALSE, FALSE,
8507 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
8510 Ystone_wB, FALSE, TRUE,
8511 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
8519 Xbomb_pause, FALSE, FALSE,
8523 Xbomb_fall, FALSE, FALSE,
8527 Ybomb_s, FALSE, FALSE,
8528 EL_BOMB, ACTION_FALLING, -1
8531 Ybomb_sB, FALSE, TRUE,
8532 EL_BOMB, ACTION_FALLING, -1
8535 Ybomb_e, FALSE, FALSE,
8536 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
8539 Ybomb_eB, FALSE, TRUE,
8540 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
8543 Ybomb_w, FALSE, FALSE,
8544 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
8547 Ybomb_wB, FALSE, TRUE,
8548 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
8551 Ybomb_blank, FALSE, FALSE,
8552 EL_BOMB, ACTION_ACTIVATING, -1
8560 Xnut_pause, FALSE, FALSE,
8564 Xnut_fall, FALSE, FALSE,
8568 Ynut_s, FALSE, FALSE,
8569 EL_NUT, ACTION_FALLING, -1
8572 Ynut_sB, FALSE, TRUE,
8573 EL_NUT, ACTION_FALLING, -1
8576 Ynut_e, FALSE, FALSE,
8577 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
8580 Ynut_eB, FALSE, TRUE,
8581 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
8584 Ynut_w, FALSE, FALSE,
8585 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
8588 Ynut_wB, FALSE, TRUE,
8589 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
8592 Ynut_stone, FALSE, FALSE,
8593 EL_NUT, ACTION_BREAKING, -1
8597 Xspring, TRUE, FALSE,
8601 Xspring_pause, FALSE, FALSE,
8605 Xspring_e, TRUE, FALSE,
8606 EL_SPRING_RIGHT, -1, -1
8609 Xspring_w, TRUE, FALSE,
8610 EL_SPRING_LEFT, -1, -1
8613 Xspring_fall, FALSE, FALSE,
8617 Yspring_s, FALSE, FALSE,
8618 EL_SPRING, ACTION_FALLING, -1
8621 Yspring_sB, FALSE, TRUE,
8622 EL_SPRING, ACTION_FALLING, -1
8625 Yspring_e, FALSE, FALSE,
8626 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
8629 Yspring_eB, FALSE, TRUE,
8630 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
8633 Yspring_w, FALSE, FALSE,
8634 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
8637 Yspring_wB, FALSE, TRUE,
8638 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
8641 Yspring_alien_e, FALSE, FALSE,
8642 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
8645 Yspring_alien_eB, FALSE, TRUE,
8646 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
8649 Yspring_alien_w, FALSE, FALSE,
8650 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
8653 Yspring_alien_wB, FALSE, TRUE,
8654 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
8658 Xpush_emerald_e, FALSE, FALSE,
8659 EL_EMERALD, -1, MV_BIT_RIGHT
8662 Xpush_emerald_w, FALSE, FALSE,
8663 EL_EMERALD, -1, MV_BIT_LEFT
8666 Xpush_diamond_e, FALSE, FALSE,
8667 EL_DIAMOND, -1, MV_BIT_RIGHT
8670 Xpush_diamond_w, FALSE, FALSE,
8671 EL_DIAMOND, -1, MV_BIT_LEFT
8674 Xpush_stone_e, FALSE, FALSE,
8675 EL_ROCK, -1, MV_BIT_RIGHT
8678 Xpush_stone_w, FALSE, FALSE,
8679 EL_ROCK, -1, MV_BIT_LEFT
8682 Xpush_bomb_e, FALSE, FALSE,
8683 EL_BOMB, -1, MV_BIT_RIGHT
8686 Xpush_bomb_w, FALSE, FALSE,
8687 EL_BOMB, -1, MV_BIT_LEFT
8690 Xpush_nut_e, FALSE, FALSE,
8691 EL_NUT, -1, MV_BIT_RIGHT
8694 Xpush_nut_w, FALSE, FALSE,
8695 EL_NUT, -1, MV_BIT_LEFT
8698 Xpush_spring_e, FALSE, FALSE,
8699 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
8702 Xpush_spring_w, FALSE, FALSE,
8703 EL_SPRING_LEFT, -1, MV_BIT_LEFT
8707 Xdynamite, TRUE, FALSE,
8708 EL_EM_DYNAMITE, -1, -1
8711 Ydynamite_blank, FALSE, FALSE,
8712 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
8715 Xdynamite_1, TRUE, FALSE,
8716 EL_EM_DYNAMITE_ACTIVE, -1, -1
8719 Xdynamite_2, FALSE, FALSE,
8720 EL_EM_DYNAMITE_ACTIVE, -1, -1
8723 Xdynamite_3, FALSE, FALSE,
8724 EL_EM_DYNAMITE_ACTIVE, -1, -1
8727 Xdynamite_4, FALSE, FALSE,
8728 EL_EM_DYNAMITE_ACTIVE, -1, -1
8732 Xkey_1, TRUE, FALSE,
8736 Xkey_2, TRUE, FALSE,
8740 Xkey_3, TRUE, FALSE,
8744 Xkey_4, TRUE, FALSE,
8748 Xkey_5, TRUE, FALSE,
8749 EL_EMC_KEY_5, -1, -1
8752 Xkey_6, TRUE, FALSE,
8753 EL_EMC_KEY_6, -1, -1
8756 Xkey_7, TRUE, FALSE,
8757 EL_EMC_KEY_7, -1, -1
8760 Xkey_8, TRUE, FALSE,
8761 EL_EMC_KEY_8, -1, -1
8765 Xdoor_1, TRUE, FALSE,
8766 EL_EM_GATE_1, -1, -1
8769 Xdoor_2, TRUE, FALSE,
8770 EL_EM_GATE_2, -1, -1
8773 Xdoor_3, TRUE, FALSE,
8774 EL_EM_GATE_3, -1, -1
8777 Xdoor_4, TRUE, FALSE,
8778 EL_EM_GATE_4, -1, -1
8781 Xdoor_5, TRUE, FALSE,
8782 EL_EMC_GATE_5, -1, -1
8785 Xdoor_6, TRUE, FALSE,
8786 EL_EMC_GATE_6, -1, -1
8789 Xdoor_7, TRUE, FALSE,
8790 EL_EMC_GATE_7, -1, -1
8793 Xdoor_8, TRUE, FALSE,
8794 EL_EMC_GATE_8, -1, -1
8798 Xfake_door_1, TRUE, FALSE,
8799 EL_EM_GATE_1_GRAY, -1, -1
8802 Xfake_door_2, TRUE, FALSE,
8803 EL_EM_GATE_2_GRAY, -1, -1
8806 Xfake_door_3, TRUE, FALSE,
8807 EL_EM_GATE_3_GRAY, -1, -1
8810 Xfake_door_4, TRUE, FALSE,
8811 EL_EM_GATE_4_GRAY, -1, -1
8814 Xfake_door_5, TRUE, FALSE,
8815 EL_EMC_GATE_5_GRAY, -1, -1
8818 Xfake_door_6, TRUE, FALSE,
8819 EL_EMC_GATE_6_GRAY, -1, -1
8822 Xfake_door_7, TRUE, FALSE,
8823 EL_EMC_GATE_7_GRAY, -1, -1
8826 Xfake_door_8, TRUE, FALSE,
8827 EL_EMC_GATE_8_GRAY, -1, -1
8831 Xballoon, TRUE, FALSE,
8835 Yballoon_n, FALSE, FALSE,
8836 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
8839 Yballoon_nB, FALSE, TRUE,
8840 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
8843 Yballoon_e, FALSE, FALSE,
8844 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
8847 Yballoon_eB, FALSE, TRUE,
8848 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
8851 Yballoon_s, FALSE, FALSE,
8852 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
8855 Yballoon_sB, FALSE, TRUE,
8856 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
8859 Yballoon_w, FALSE, FALSE,
8860 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
8863 Yballoon_wB, FALSE, TRUE,
8864 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
8868 Xball_1, TRUE, FALSE,
8869 EL_EMC_MAGIC_BALL, -1, -1
8872 Yball_1, FALSE, FALSE,
8873 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
8876 Xball_2, FALSE, FALSE,
8877 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
8880 Yball_2, FALSE, FALSE,
8881 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
8884 Yball_blank, FALSE, FALSE,
8885 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
8889 Xamoeba_1, TRUE, FALSE,
8890 EL_AMOEBA_DRY, ACTION_OTHER, -1
8893 Xamoeba_2, FALSE, FALSE,
8894 EL_AMOEBA_DRY, ACTION_OTHER, -1
8897 Xamoeba_3, FALSE, FALSE,
8898 EL_AMOEBA_DRY, ACTION_OTHER, -1
8901 Xamoeba_4, FALSE, FALSE,
8902 EL_AMOEBA_DRY, ACTION_OTHER, -1
8905 Xamoeba_5, TRUE, FALSE,
8906 EL_AMOEBA_WET, ACTION_OTHER, -1
8909 Xamoeba_6, FALSE, FALSE,
8910 EL_AMOEBA_WET, ACTION_OTHER, -1
8913 Xamoeba_7, FALSE, FALSE,
8914 EL_AMOEBA_WET, ACTION_OTHER, -1
8917 Xamoeba_8, FALSE, FALSE,
8918 EL_AMOEBA_WET, ACTION_OTHER, -1
8923 EL_AMOEBA_DROP, ACTION_GROWING, -1
8926 Xdrip_fall, FALSE, FALSE,
8927 EL_AMOEBA_DROP, -1, -1
8930 Xdrip_stretch, FALSE, FALSE,
8931 EL_AMOEBA_DROP, ACTION_FALLING, -1
8934 Xdrip_stretchB, FALSE, TRUE,
8935 EL_AMOEBA_DROP, ACTION_FALLING, -1
8938 Ydrip_1_s, FALSE, FALSE,
8939 EL_AMOEBA_DROP, ACTION_FALLING, -1
8942 Ydrip_1_sB, FALSE, TRUE,
8943 EL_AMOEBA_DROP, ACTION_FALLING, -1
8946 Ydrip_2_s, FALSE, FALSE,
8947 EL_AMOEBA_DROP, ACTION_FALLING, -1
8950 Ydrip_2_sB, FALSE, TRUE,
8951 EL_AMOEBA_DROP, ACTION_FALLING, -1
8955 Xwonderwall, TRUE, FALSE,
8956 EL_MAGIC_WALL, -1, -1
8959 Ywonderwall, FALSE, FALSE,
8960 EL_MAGIC_WALL, ACTION_ACTIVE, -1
8964 Xwheel, TRUE, FALSE,
8965 EL_ROBOT_WHEEL, -1, -1
8968 Ywheel, FALSE, FALSE,
8969 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
8973 Xswitch, TRUE, FALSE,
8974 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
8977 Yswitch, FALSE, FALSE,
8978 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
8982 Xbumper, TRUE, FALSE,
8983 EL_EMC_SPRING_BUMPER, -1, -1
8986 Ybumper, FALSE, FALSE,
8987 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
8991 Xacid_nw, TRUE, FALSE,
8992 EL_ACID_POOL_TOPLEFT, -1, -1
8995 Xacid_ne, TRUE, FALSE,
8996 EL_ACID_POOL_TOPRIGHT, -1, -1
8999 Xacid_sw, TRUE, FALSE,
9000 EL_ACID_POOL_BOTTOMLEFT, -1, -1
9003 Xacid_s, TRUE, FALSE,
9004 EL_ACID_POOL_BOTTOM, -1, -1
9007 Xacid_se, TRUE, FALSE,
9008 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
9012 Xfake_blank, TRUE, FALSE,
9013 EL_INVISIBLE_WALL, -1, -1
9016 Yfake_blank, FALSE, FALSE,
9017 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
9021 Xfake_grass, TRUE, FALSE,
9022 EL_EMC_FAKE_GRASS, -1, -1
9025 Yfake_grass, FALSE, FALSE,
9026 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
9030 Xfake_amoeba, TRUE, FALSE,
9031 EL_EMC_DRIPPER, -1, -1
9034 Yfake_amoeba, FALSE, FALSE,
9035 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
9039 Xlenses, TRUE, FALSE,
9040 EL_EMC_LENSES, -1, -1
9044 Xmagnify, TRUE, FALSE,
9045 EL_EMC_MAGNIFIER, -1, -1
9050 EL_QUICKSAND_EMPTY, -1, -1
9053 Xsand_stone, TRUE, FALSE,
9054 EL_QUICKSAND_FULL, -1, -1
9057 Xsand_stonein_1, FALSE, TRUE,
9058 EL_ROCK, ACTION_FILLING, -1
9061 Xsand_stonein_2, FALSE, TRUE,
9062 EL_ROCK, ACTION_FILLING, -1
9065 Xsand_stonein_3, FALSE, TRUE,
9066 EL_ROCK, ACTION_FILLING, -1
9069 Xsand_stonein_4, FALSE, TRUE,
9070 EL_ROCK, ACTION_FILLING, -1
9073 Xsand_sandstone_1, FALSE, FALSE,
9074 EL_QUICKSAND_FILLING, -1, -1
9077 Xsand_sandstone_2, FALSE, FALSE,
9078 EL_QUICKSAND_FILLING, -1, -1
9081 Xsand_sandstone_3, FALSE, FALSE,
9082 EL_QUICKSAND_FILLING, -1, -1
9085 Xsand_sandstone_4, FALSE, FALSE,
9086 EL_QUICKSAND_FILLING, -1, -1
9089 Xsand_stonesand_1, FALSE, FALSE,
9090 EL_QUICKSAND_EMPTYING, -1, -1
9093 Xsand_stonesand_2, FALSE, FALSE,
9094 EL_QUICKSAND_EMPTYING, -1, -1
9097 Xsand_stonesand_3, FALSE, FALSE,
9098 EL_QUICKSAND_EMPTYING, -1, -1
9101 Xsand_stonesand_4, FALSE, FALSE,
9102 EL_QUICKSAND_EMPTYING, -1, -1
9105 Xsand_stoneout_1, FALSE, FALSE,
9106 EL_ROCK, ACTION_EMPTYING, -1
9109 Xsand_stoneout_2, FALSE, FALSE,
9110 EL_ROCK, ACTION_EMPTYING, -1
9113 Xsand_stonesand_quickout_1, FALSE, FALSE,
9114 EL_QUICKSAND_EMPTYING, -1, -1
9117 Xsand_stonesand_quickout_2, FALSE, FALSE,
9118 EL_QUICKSAND_EMPTYING, -1, -1
9122 Xslide_ns, TRUE, FALSE,
9123 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
9126 Yslide_ns_blank, FALSE, FALSE,
9127 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
9130 Xslide_ew, TRUE, FALSE,
9131 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
9134 Yslide_ew_blank, FALSE, FALSE,
9135 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
9139 Xwind_n, TRUE, FALSE,
9140 EL_BALLOON_SWITCH_UP, -1, -1
9143 Xwind_e, TRUE, FALSE,
9144 EL_BALLOON_SWITCH_RIGHT, -1, -1
9147 Xwind_s, TRUE, FALSE,
9148 EL_BALLOON_SWITCH_DOWN, -1, -1
9151 Xwind_w, TRUE, FALSE,
9152 EL_BALLOON_SWITCH_LEFT, -1, -1
9155 Xwind_any, TRUE, FALSE,
9156 EL_BALLOON_SWITCH_ANY, -1, -1
9159 Xwind_stop, TRUE, FALSE,
9160 EL_BALLOON_SWITCH_NONE, -1, -1
9165 EL_EM_EXIT_CLOSED, -1, -1
9168 Xexit_1, TRUE, FALSE,
9169 EL_EM_EXIT_OPEN, -1, -1
9172 Xexit_2, FALSE, FALSE,
9173 EL_EM_EXIT_OPEN, -1, -1
9176 Xexit_3, FALSE, FALSE,
9177 EL_EM_EXIT_OPEN, -1, -1
9181 Xpause, FALSE, FALSE,
9186 Xwall_1, TRUE, FALSE,
9190 Xwall_2, TRUE, FALSE,
9191 EL_EMC_WALL_14, -1, -1
9194 Xwall_3, TRUE, FALSE,
9195 EL_EMC_WALL_15, -1, -1
9198 Xwall_4, TRUE, FALSE,
9199 EL_EMC_WALL_16, -1, -1
9203 Xroundwall_1, TRUE, FALSE,
9204 EL_WALL_SLIPPERY, -1, -1
9207 Xroundwall_2, TRUE, FALSE,
9208 EL_EMC_WALL_SLIPPERY_2, -1, -1
9211 Xroundwall_3, TRUE, FALSE,
9212 EL_EMC_WALL_SLIPPERY_3, -1, -1
9215 Xroundwall_4, TRUE, FALSE,
9216 EL_EMC_WALL_SLIPPERY_4, -1, -1
9220 Xsteel_1, TRUE, FALSE,
9221 EL_STEELWALL, -1, -1
9224 Xsteel_2, TRUE, FALSE,
9225 EL_EMC_STEELWALL_2, -1, -1
9228 Xsteel_3, TRUE, FALSE,
9229 EL_EMC_STEELWALL_3, -1, -1
9232 Xsteel_4, TRUE, FALSE,
9233 EL_EMC_STEELWALL_4, -1, -1
9237 Xdecor_1, TRUE, FALSE,
9238 EL_EMC_WALL_8, -1, -1
9241 Xdecor_2, TRUE, FALSE,
9242 EL_EMC_WALL_6, -1, -1
9245 Xdecor_3, TRUE, FALSE,
9246 EL_EMC_WALL_4, -1, -1
9249 Xdecor_4, TRUE, FALSE,
9250 EL_EMC_WALL_7, -1, -1
9253 Xdecor_5, TRUE, FALSE,
9254 EL_EMC_WALL_5, -1, -1
9257 Xdecor_6, TRUE, FALSE,
9258 EL_EMC_WALL_9, -1, -1
9261 Xdecor_7, TRUE, FALSE,
9262 EL_EMC_WALL_10, -1, -1
9265 Xdecor_8, TRUE, FALSE,
9266 EL_EMC_WALL_1, -1, -1
9269 Xdecor_9, TRUE, FALSE,
9270 EL_EMC_WALL_2, -1, -1
9273 Xdecor_10, TRUE, FALSE,
9274 EL_EMC_WALL_3, -1, -1
9277 Xdecor_11, TRUE, FALSE,
9278 EL_EMC_WALL_11, -1, -1
9281 Xdecor_12, TRUE, FALSE,
9282 EL_EMC_WALL_12, -1, -1
9286 Xalpha_0, TRUE, FALSE,
9287 EL_CHAR('0'), -1, -1
9290 Xalpha_1, TRUE, FALSE,
9291 EL_CHAR('1'), -1, -1
9294 Xalpha_2, TRUE, FALSE,
9295 EL_CHAR('2'), -1, -1
9298 Xalpha_3, TRUE, FALSE,
9299 EL_CHAR('3'), -1, -1
9302 Xalpha_4, TRUE, FALSE,
9303 EL_CHAR('4'), -1, -1
9306 Xalpha_5, TRUE, FALSE,
9307 EL_CHAR('5'), -1, -1
9310 Xalpha_6, TRUE, FALSE,
9311 EL_CHAR('6'), -1, -1
9314 Xalpha_7, TRUE, FALSE,
9315 EL_CHAR('7'), -1, -1
9318 Xalpha_8, TRUE, FALSE,
9319 EL_CHAR('8'), -1, -1
9322 Xalpha_9, TRUE, FALSE,
9323 EL_CHAR('9'), -1, -1
9326 Xalpha_excla, TRUE, FALSE,
9327 EL_CHAR('!'), -1, -1
9330 Xalpha_apost, TRUE, FALSE,
9331 EL_CHAR('\''), -1, -1
9334 Xalpha_comma, TRUE, FALSE,
9335 EL_CHAR(','), -1, -1
9338 Xalpha_minus, TRUE, FALSE,
9339 EL_CHAR('-'), -1, -1
9342 Xalpha_perio, TRUE, FALSE,
9343 EL_CHAR('.'), -1, -1
9346 Xalpha_colon, TRUE, FALSE,
9347 EL_CHAR(':'), -1, -1
9350 Xalpha_quest, TRUE, FALSE,
9351 EL_CHAR('?'), -1, -1
9354 Xalpha_a, TRUE, FALSE,
9355 EL_CHAR('A'), -1, -1
9358 Xalpha_b, TRUE, FALSE,
9359 EL_CHAR('B'), -1, -1
9362 Xalpha_c, TRUE, FALSE,
9363 EL_CHAR('C'), -1, -1
9366 Xalpha_d, TRUE, FALSE,
9367 EL_CHAR('D'), -1, -1
9370 Xalpha_e, TRUE, FALSE,
9371 EL_CHAR('E'), -1, -1
9374 Xalpha_f, TRUE, FALSE,
9375 EL_CHAR('F'), -1, -1
9378 Xalpha_g, TRUE, FALSE,
9379 EL_CHAR('G'), -1, -1
9382 Xalpha_h, TRUE, FALSE,
9383 EL_CHAR('H'), -1, -1
9386 Xalpha_i, TRUE, FALSE,
9387 EL_CHAR('I'), -1, -1
9390 Xalpha_j, TRUE, FALSE,
9391 EL_CHAR('J'), -1, -1
9394 Xalpha_k, TRUE, FALSE,
9395 EL_CHAR('K'), -1, -1
9398 Xalpha_l, TRUE, FALSE,
9399 EL_CHAR('L'), -1, -1
9402 Xalpha_m, TRUE, FALSE,
9403 EL_CHAR('M'), -1, -1
9406 Xalpha_n, TRUE, FALSE,
9407 EL_CHAR('N'), -1, -1
9410 Xalpha_o, TRUE, FALSE,
9411 EL_CHAR('O'), -1, -1
9414 Xalpha_p, TRUE, FALSE,
9415 EL_CHAR('P'), -1, -1
9418 Xalpha_q, TRUE, FALSE,
9419 EL_CHAR('Q'), -1, -1
9422 Xalpha_r, TRUE, FALSE,
9423 EL_CHAR('R'), -1, -1
9426 Xalpha_s, TRUE, FALSE,
9427 EL_CHAR('S'), -1, -1
9430 Xalpha_t, TRUE, FALSE,
9431 EL_CHAR('T'), -1, -1
9434 Xalpha_u, TRUE, FALSE,
9435 EL_CHAR('U'), -1, -1
9438 Xalpha_v, TRUE, FALSE,
9439 EL_CHAR('V'), -1, -1
9442 Xalpha_w, TRUE, FALSE,
9443 EL_CHAR('W'), -1, -1
9446 Xalpha_x, TRUE, FALSE,
9447 EL_CHAR('X'), -1, -1
9450 Xalpha_y, TRUE, FALSE,
9451 EL_CHAR('Y'), -1, -1
9454 Xalpha_z, TRUE, FALSE,
9455 EL_CHAR('Z'), -1, -1
9458 Xalpha_arrow_e, TRUE, FALSE,
9459 EL_CHAR('>'), -1, -1
9462 Xalpha_arrow_w, TRUE, FALSE,
9463 EL_CHAR('<'), -1, -1
9466 Xalpha_copyr, TRUE, FALSE,
9467 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
9471 Ykey_1_blank, FALSE, FALSE,
9472 EL_EM_KEY_1, ACTION_COLLECTING, -1
9475 Ykey_2_blank, FALSE, FALSE,
9476 EL_EM_KEY_2, ACTION_COLLECTING, -1
9479 Ykey_3_blank, FALSE, FALSE,
9480 EL_EM_KEY_3, ACTION_COLLECTING, -1
9483 Ykey_4_blank, FALSE, FALSE,
9484 EL_EM_KEY_4, ACTION_COLLECTING, -1
9487 Ykey_5_blank, FALSE, FALSE,
9488 EL_EMC_KEY_5, ACTION_COLLECTING, -1
9491 Ykey_6_blank, FALSE, FALSE,
9492 EL_EMC_KEY_6, ACTION_COLLECTING, -1
9495 Ykey_7_blank, FALSE, FALSE,
9496 EL_EMC_KEY_7, ACTION_COLLECTING, -1
9499 Ykey_8_blank, FALSE, FALSE,
9500 EL_EMC_KEY_8, ACTION_COLLECTING, -1
9503 Ylenses_blank, FALSE, FALSE,
9504 EL_EMC_LENSES, ACTION_COLLECTING, -1
9507 Ymagnify_blank, FALSE, FALSE,
9508 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
9511 Ygrass_blank, FALSE, FALSE,
9512 EL_EMC_GRASS, ACTION_SNAPPING, -1
9515 Ydirt_blank, FALSE, FALSE,
9516 EL_SAND, ACTION_SNAPPING, -1
9525 static struct Mapping_EM_to_RND_player
9534 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
9538 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
9542 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
9546 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
9550 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
9554 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
9558 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
9562 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
9566 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
9570 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
9574 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
9578 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
9582 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
9586 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
9590 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
9594 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
9598 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
9602 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
9606 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
9610 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
9614 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
9618 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
9622 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
9626 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
9630 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
9634 EL_PLAYER_1, ACTION_DEFAULT, -1,
9638 EL_PLAYER_2, ACTION_DEFAULT, -1,
9642 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
9646 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
9650 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
9654 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
9658 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
9662 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
9666 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
9670 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
9674 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
9678 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
9682 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
9686 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
9690 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
9694 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
9698 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
9702 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
9706 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
9710 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
9714 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
9718 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
9722 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
9726 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
9730 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
9734 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
9738 EL_PLAYER_3, ACTION_DEFAULT, -1,
9742 EL_PLAYER_4, ACTION_DEFAULT, -1,
9751 int map_element_RND_to_EM_cave(int element_rnd)
9753 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
9754 static boolean mapping_initialized = FALSE;
9756 if (!mapping_initialized)
9760 // return "Xalpha_quest" for all undefined elements in mapping array
9761 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9762 mapping_RND_to_EM[i] = Xalpha_quest;
9764 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
9765 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
9766 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
9767 em_object_mapping_list[i].element_em;
9769 mapping_initialized = TRUE;
9772 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
9774 Warn("invalid RND level element %d", element_rnd);
9779 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
9782 int map_element_EM_to_RND_cave(int element_em_cave)
9784 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
9785 static boolean mapping_initialized = FALSE;
9787 if (!mapping_initialized)
9791 // return "EL_UNKNOWN" for all undefined elements in mapping array
9792 for (i = 0; i < GAME_TILE_MAX; i++)
9793 mapping_EM_to_RND[i] = EL_UNKNOWN;
9795 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
9796 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
9797 em_object_mapping_list[i].element_rnd;
9799 mapping_initialized = TRUE;
9802 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
9804 Warn("invalid EM cave element %d", element_em_cave);
9809 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
9812 int map_element_EM_to_RND_game(int element_em_game)
9814 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
9815 static boolean mapping_initialized = FALSE;
9817 if (!mapping_initialized)
9821 // return "EL_UNKNOWN" for all undefined elements in mapping array
9822 for (i = 0; i < GAME_TILE_MAX; i++)
9823 mapping_EM_to_RND[i] = EL_UNKNOWN;
9825 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
9826 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
9827 em_object_mapping_list[i].element_rnd;
9829 mapping_initialized = TRUE;
9832 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
9834 Warn("invalid EM game element %d", element_em_game);
9839 return mapping_EM_to_RND[element_em_game];
9842 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
9844 struct LevelInfo_EM *level_em = level->native_em_level;
9845 struct CAVE *cav = level_em->cav;
9848 for (i = 0; i < GAME_TILE_MAX; i++)
9849 cav->android_array[i] = Cblank;
9851 for (i = 0; i < level->num_android_clone_elements; i++)
9853 int element_rnd = level->android_clone_element[i];
9854 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
9856 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
9857 if (em_object_mapping_list[j].element_rnd == element_rnd)
9858 cav->android_array[em_object_mapping_list[j].element_em] =
9863 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
9865 struct LevelInfo_EM *level_em = level->native_em_level;
9866 struct CAVE *cav = level_em->cav;
9869 level->num_android_clone_elements = 0;
9871 for (i = 0; i < GAME_TILE_MAX; i++)
9873 int element_em_cave = cav->android_array[i];
9875 boolean element_found = FALSE;
9877 if (element_em_cave == Cblank)
9880 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
9882 for (j = 0; j < level->num_android_clone_elements; j++)
9883 if (level->android_clone_element[j] == element_rnd)
9884 element_found = TRUE;
9888 level->android_clone_element[level->num_android_clone_elements++] =
9891 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
9896 if (level->num_android_clone_elements == 0)
9898 level->num_android_clone_elements = 1;
9899 level->android_clone_element[0] = EL_EMPTY;
9903 int map_direction_RND_to_EM(int direction)
9905 return (direction == MV_UP ? 0 :
9906 direction == MV_RIGHT ? 1 :
9907 direction == MV_DOWN ? 2 :
9908 direction == MV_LEFT ? 3 :
9912 int map_direction_EM_to_RND(int direction)
9914 return (direction == 0 ? MV_UP :
9915 direction == 1 ? MV_RIGHT :
9916 direction == 2 ? MV_DOWN :
9917 direction == 3 ? MV_LEFT :
9921 int map_element_RND_to_SP(int element_rnd)
9923 int element_sp = 0x20; // map unknown elements to yellow "hardware"
9925 if (element_rnd >= EL_SP_START &&
9926 element_rnd <= EL_SP_END)
9927 element_sp = element_rnd - EL_SP_START;
9928 else if (element_rnd == EL_EMPTY_SPACE)
9930 else if (element_rnd == EL_INVISIBLE_WALL)
9936 int map_element_SP_to_RND(int element_sp)
9938 int element_rnd = EL_UNKNOWN;
9940 if (element_sp >= 0x00 &&
9942 element_rnd = EL_SP_START + element_sp;
9943 else if (element_sp == 0x28)
9944 element_rnd = EL_INVISIBLE_WALL;
9949 int map_action_SP_to_RND(int action_sp)
9953 case actActive: return ACTION_ACTIVE;
9954 case actImpact: return ACTION_IMPACT;
9955 case actExploding: return ACTION_EXPLODING;
9956 case actDigging: return ACTION_DIGGING;
9957 case actSnapping: return ACTION_SNAPPING;
9958 case actCollecting: return ACTION_COLLECTING;
9959 case actPassing: return ACTION_PASSING;
9960 case actPushing: return ACTION_PUSHING;
9961 case actDropping: return ACTION_DROPPING;
9963 default: return ACTION_DEFAULT;
9967 int map_element_RND_to_MM(int element_rnd)
9969 return (element_rnd >= EL_MM_START_1 &&
9970 element_rnd <= EL_MM_END_1 ?
9971 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
9973 element_rnd >= EL_MM_START_2 &&
9974 element_rnd <= EL_MM_END_2 ?
9975 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
9977 element_rnd >= EL_MM_START_3 &&
9978 element_rnd <= EL_MM_END_3 ?
9979 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
9981 element_rnd >= EL_CHAR_START &&
9982 element_rnd <= EL_CHAR_END ?
9983 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
9985 element_rnd >= EL_MM_RUNTIME_START &&
9986 element_rnd <= EL_MM_RUNTIME_END ?
9987 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
9989 EL_MM_EMPTY_NATIVE);
9992 int map_element_MM_to_RND(int element_mm)
9994 return (element_mm == EL_MM_EMPTY_NATIVE ||
9995 element_mm == EL_DF_EMPTY_NATIVE ?
9998 element_mm >= EL_MM_START_1_NATIVE &&
9999 element_mm <= EL_MM_END_1_NATIVE ?
10000 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
10002 element_mm >= EL_MM_START_2_NATIVE &&
10003 element_mm <= EL_MM_END_2_NATIVE ?
10004 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
10006 element_mm >= EL_MM_START_3_NATIVE &&
10007 element_mm <= EL_MM_END_3_NATIVE ?
10008 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
10010 element_mm >= EL_MM_CHAR_START_NATIVE &&
10011 element_mm <= EL_MM_CHAR_END_NATIVE ?
10012 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
10014 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
10015 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
10016 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
10021 int map_action_MM_to_RND(int action_mm)
10023 // all MM actions are defined to exactly match their RND counterparts
10027 int map_sound_MM_to_RND(int sound_mm)
10031 case SND_MM_GAME_LEVELTIME_CHARGING:
10032 return SND_GAME_LEVELTIME_CHARGING;
10034 case SND_MM_GAME_HEALTH_CHARGING:
10035 return SND_GAME_HEALTH_CHARGING;
10038 return SND_UNDEFINED;
10042 int map_mm_wall_element(int element)
10044 return (element >= EL_MM_STEEL_WALL_START &&
10045 element <= EL_MM_STEEL_WALL_END ?
10048 element >= EL_MM_WOODEN_WALL_START &&
10049 element <= EL_MM_WOODEN_WALL_END ?
10050 EL_MM_WOODEN_WALL :
10052 element >= EL_MM_ICE_WALL_START &&
10053 element <= EL_MM_ICE_WALL_END ?
10056 element >= EL_MM_AMOEBA_WALL_START &&
10057 element <= EL_MM_AMOEBA_WALL_END ?
10058 EL_MM_AMOEBA_WALL :
10060 element >= EL_DF_STEEL_WALL_START &&
10061 element <= EL_DF_STEEL_WALL_END ?
10064 element >= EL_DF_WOODEN_WALL_START &&
10065 element <= EL_DF_WOODEN_WALL_END ?
10066 EL_DF_WOODEN_WALL :
10071 int map_mm_wall_element_editor(int element)
10075 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
10076 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
10077 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
10078 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
10079 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
10080 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
10082 default: return element;
10086 int get_next_element(int element)
10090 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
10091 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
10092 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
10093 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
10094 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
10095 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
10096 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
10097 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
10098 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
10099 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
10100 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
10102 default: return element;
10106 int el2img_mm(int element_mm)
10108 return el2img(map_element_MM_to_RND(element_mm));
10111 int el_act2img_mm(int element_mm, int action)
10113 return el_act2img(map_element_MM_to_RND(element_mm), action);
10116 int el_act_dir2img(int element, int action, int direction)
10118 element = GFX_ELEMENT(element);
10119 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
10121 // direction_graphic[][] == graphic[] for undefined direction graphics
10122 return element_info[element].direction_graphic[action][direction];
10125 static int el_act_dir2crm(int element, int action, int direction)
10127 element = GFX_ELEMENT(element);
10128 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
10130 // direction_graphic[][] == graphic[] for undefined direction graphics
10131 return element_info[element].direction_crumbled[action][direction];
10134 int el_act2img(int element, int action)
10136 element = GFX_ELEMENT(element);
10138 return element_info[element].graphic[action];
10141 int el_act2crm(int element, int action)
10143 element = GFX_ELEMENT(element);
10145 return element_info[element].crumbled[action];
10148 int el_dir2img(int element, int direction)
10150 element = GFX_ELEMENT(element);
10152 return el_act_dir2img(element, ACTION_DEFAULT, direction);
10155 int el2baseimg(int element)
10157 return element_info[element].graphic[ACTION_DEFAULT];
10160 int el2img(int element)
10162 element = GFX_ELEMENT(element);
10164 return element_info[element].graphic[ACTION_DEFAULT];
10167 int el2edimg(int element)
10169 element = GFX_ELEMENT(element);
10171 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
10174 int el2edimg_with_frame(int element, int *graphic, int *frame)
10176 *graphic = el2edimg(element);
10179 if (*graphic == IMG_UNKNOWN)
10181 // no graphic defined -- if BD style, try to get runtime ("effect") element graphics
10182 // (normal BD style elements have graphics, but runtime ("effects") elements do not)
10183 int element_bd = map_element_RND_to_BD_cave(element);
10185 if (element_bd != O_UNKNOWN)
10187 struct GraphicInfo_BD *g_bd = &graphic_info_bd_object[element_bd][0];
10189 *graphic = g_bd->graphic;
10190 *frame = g_bd->frame;
10197 int el2preimg(int element)
10199 element = GFX_ELEMENT(element);
10201 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
10204 int el2panelimg(int element)
10206 element = GFX_ELEMENT(element);
10208 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
10211 int font2baseimg(int font_nr)
10213 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
10216 int getBeltNrFromBeltElement(int element)
10218 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
10219 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
10220 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
10223 int getBeltNrFromBeltActiveElement(int element)
10225 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
10226 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
10227 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
10230 int getBeltNrFromBeltSwitchElement(int element)
10232 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
10233 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
10234 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
10237 int getBeltDirNrFromBeltElement(int element)
10239 static int belt_base_element[4] =
10241 EL_CONVEYOR_BELT_1_LEFT,
10242 EL_CONVEYOR_BELT_2_LEFT,
10243 EL_CONVEYOR_BELT_3_LEFT,
10244 EL_CONVEYOR_BELT_4_LEFT
10247 int belt_nr = getBeltNrFromBeltElement(element);
10248 int belt_dir_nr = element - belt_base_element[belt_nr];
10250 return (belt_dir_nr % 3);
10253 int getBeltDirNrFromBeltSwitchElement(int element)
10255 static int belt_base_element[4] =
10257 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
10258 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
10259 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
10260 EL_CONVEYOR_BELT_4_SWITCH_LEFT
10263 int belt_nr = getBeltNrFromBeltSwitchElement(element);
10264 int belt_dir_nr = element - belt_base_element[belt_nr];
10266 return (belt_dir_nr % 3);
10269 int getBeltDirFromBeltElement(int element)
10271 static int belt_move_dir[3] =
10278 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
10280 return belt_move_dir[belt_dir_nr];
10283 int getBeltDirFromBeltSwitchElement(int element)
10285 static int belt_move_dir[3] =
10292 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
10294 return belt_move_dir[belt_dir_nr];
10297 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
10299 static int belt_base_element[4] =
10301 EL_CONVEYOR_BELT_1_LEFT,
10302 EL_CONVEYOR_BELT_2_LEFT,
10303 EL_CONVEYOR_BELT_3_LEFT,
10304 EL_CONVEYOR_BELT_4_LEFT
10307 return belt_base_element[belt_nr] + belt_dir_nr;
10310 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
10312 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
10314 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
10317 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
10319 static int belt_base_element[4] =
10321 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
10322 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
10323 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
10324 EL_CONVEYOR_BELT_4_SWITCH_LEFT
10327 return belt_base_element[belt_nr] + belt_dir_nr;
10330 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
10332 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
10334 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
10337 boolean swapTiles_EM(boolean is_pre_emc_cave)
10339 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
10342 boolean getTeamMode_EM(void)
10344 return game.team_mode || network_playing;
10347 boolean isActivePlayer_EM(int player_nr)
10349 return stored_player[player_nr].active;
10352 unsigned int InitRND(int seed)
10354 if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
10355 return InitEngineRandom_BD(seed);
10356 else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10357 return InitEngineRandom_EM(seed);
10358 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10359 return InitEngineRandom_SP(seed);
10360 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
10361 return InitEngineRandom_MM(seed);
10363 return InitEngineRandom_RND(seed);
10366 static struct Mapping_BD_to_RND_object bd_object_mapping[O_MAX_ALL];
10367 static struct Mapping_EM_to_RND_object em_object_mapping[GAME_TILE_MAX];
10368 static struct Mapping_EM_to_RND_player em_player_mapping[MAX_PLAYERS][PLY_MAX];
10370 static int get_effective_element_EM(int tile, int frame_em)
10372 int element = em_object_mapping[tile].element_rnd;
10373 int action = em_object_mapping[tile].action;
10374 boolean is_backside = em_object_mapping[tile].is_backside;
10375 boolean action_removing = (action == ACTION_DIGGING ||
10376 action == ACTION_SNAPPING ||
10377 action == ACTION_COLLECTING);
10385 return (frame_em > 5 ? EL_EMPTY : element);
10391 else // frame_em == 7
10402 case Ydiamond_stone:
10405 case Xdrip_stretch:
10406 case Xdrip_stretchB:
10421 case Ylenses_blank:
10422 case Ymagnify_blank:
10425 case Xsand_stonein_1:
10426 case Xsand_stonein_2:
10427 case Xsand_stonein_3:
10428 case Xsand_stonein_4:
10432 return (is_backside || action_removing ? EL_EMPTY : element);
10437 static boolean check_linear_animation_EM(int tile)
10441 case Xsand_stonesand_1:
10442 case Xsand_stonesand_quickout_1:
10443 case Xsand_sandstone_1:
10444 case Xsand_stonein_1:
10445 case Xsand_stoneout_1:
10473 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
10474 boolean has_crumbled_graphics,
10475 int crumbled, int sync_frame)
10477 // if element can be crumbled, but certain action graphics are just empty
10478 // space (like instantly snapping sand to empty space in 1 frame), do not
10479 // treat these empty space graphics as crumbled graphics in EMC engine
10480 if (crumbled == IMG_EMPTY_SPACE)
10481 has_crumbled_graphics = FALSE;
10483 if (has_crumbled_graphics)
10485 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
10486 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
10487 g_crumbled->anim_delay,
10488 g_crumbled->anim_mode,
10489 g_crumbled->anim_start_frame,
10492 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
10493 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
10495 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
10496 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
10498 g_em->has_crumbled_graphics = TRUE;
10502 g_em->crumbled_bitmap = NULL;
10503 g_em->crumbled_src_x = 0;
10504 g_em->crumbled_src_y = 0;
10505 g_em->crumbled_border_size = 0;
10506 g_em->crumbled_tile_size = 0;
10508 g_em->has_crumbled_graphics = FALSE;
10513 void ResetGfxAnimation_EM(int x, int y, int tile)
10515 GfxFrame[x][y] = 0;
10519 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
10520 int tile, int frame_em, int x, int y)
10522 int action = em_object_mapping[tile].action;
10523 int direction = em_object_mapping[tile].direction;
10524 int effective_element = get_effective_element_EM(tile, frame_em);
10525 int graphic = (direction == MV_NONE ?
10526 el_act2img(effective_element, action) :
10527 el_act_dir2img(effective_element, action, direction));
10528 struct GraphicInfo *g = &graphic_info[graphic];
10530 boolean action_removing = (action == ACTION_DIGGING ||
10531 action == ACTION_SNAPPING ||
10532 action == ACTION_COLLECTING);
10533 boolean action_moving = (action == ACTION_FALLING ||
10534 action == ACTION_MOVING ||
10535 action == ACTION_PUSHING ||
10536 action == ACTION_EATING ||
10537 action == ACTION_FILLING ||
10538 action == ACTION_EMPTYING);
10539 boolean action_falling = (action == ACTION_FALLING ||
10540 action == ACTION_FILLING ||
10541 action == ACTION_EMPTYING);
10543 // special case: graphic uses "2nd movement tile" and has defined
10544 // 7 frames for movement animation (or less) => use default graphic
10545 // for last (8th) frame which ends the movement animation
10546 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
10548 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
10549 graphic = (direction == MV_NONE ?
10550 el_act2img(effective_element, action) :
10551 el_act_dir2img(effective_element, action, direction));
10553 g = &graphic_info[graphic];
10556 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
10558 GfxFrame[x][y] = 0;
10560 else if (action_moving)
10562 boolean is_backside = em_object_mapping[tile].is_backside;
10566 int direction = em_object_mapping[tile].direction;
10567 int move_dir = (action_falling ? MV_DOWN : direction);
10572 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
10573 if (g->double_movement && frame_em == 0)
10574 GfxFrame[x][y] = 0;
10577 if (move_dir == MV_LEFT)
10578 GfxFrame[x - 1][y] = GfxFrame[x][y];
10579 else if (move_dir == MV_RIGHT)
10580 GfxFrame[x + 1][y] = GfxFrame[x][y];
10581 else if (move_dir == MV_UP)
10582 GfxFrame[x][y - 1] = GfxFrame[x][y];
10583 else if (move_dir == MV_DOWN)
10584 GfxFrame[x][y + 1] = GfxFrame[x][y];
10591 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
10592 if (tile == Xsand_stonesand_quickout_1 ||
10593 tile == Xsand_stonesand_quickout_2)
10597 if (graphic_info[graphic].anim_global_sync)
10598 sync_frame = FrameCounter;
10599 else if (graphic_info[graphic].anim_global_anim_sync)
10600 sync_frame = getGlobalAnimSyncFrame();
10601 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
10602 sync_frame = GfxFrame[x][y];
10604 sync_frame = 0; // playfield border (pseudo steel)
10606 SetRandomAnimationValue(x, y);
10608 int frame = getAnimationFrame(g->anim_frames,
10611 g->anim_start_frame,
10614 g_em->unique_identifier =
10615 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
10618 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
10619 int tile, int frame_em, int x, int y)
10621 int action = em_object_mapping[tile].action;
10622 int direction = em_object_mapping[tile].direction;
10623 boolean is_backside = em_object_mapping[tile].is_backside;
10624 int effective_element = get_effective_element_EM(tile, frame_em);
10625 int effective_action = action;
10626 int graphic = (direction == MV_NONE ?
10627 el_act2img(effective_element, effective_action) :
10628 el_act_dir2img(effective_element, effective_action,
10630 int crumbled = (direction == MV_NONE ?
10631 el_act2crm(effective_element, effective_action) :
10632 el_act_dir2crm(effective_element, effective_action,
10634 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
10635 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
10636 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
10637 struct GraphicInfo *g = &graphic_info[graphic];
10640 // special case: graphic uses "2nd movement tile" and has defined
10641 // 7 frames for movement animation (or less) => use default graphic
10642 // for last (8th) frame which ends the movement animation
10643 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
10645 effective_action = ACTION_DEFAULT;
10646 graphic = (direction == MV_NONE ?
10647 el_act2img(effective_element, effective_action) :
10648 el_act_dir2img(effective_element, effective_action,
10650 crumbled = (direction == MV_NONE ?
10651 el_act2crm(effective_element, effective_action) :
10652 el_act_dir2crm(effective_element, effective_action,
10655 g = &graphic_info[graphic];
10658 if (graphic_info[graphic].anim_global_sync)
10659 sync_frame = FrameCounter;
10660 else if (graphic_info[graphic].anim_global_anim_sync)
10661 sync_frame = getGlobalAnimSyncFrame();
10662 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
10663 sync_frame = GfxFrame[x][y];
10665 sync_frame = 0; // playfield border (pseudo steel)
10667 SetRandomAnimationValue(x, y);
10669 int frame = getAnimationFrame(g->anim_frames,
10672 g->anim_start_frame,
10675 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
10676 g->double_movement && is_backside);
10678 // (updating the "crumbled" graphic definitions is probably not really needed,
10679 // as animations for crumbled graphics can't be longer than one EMC cycle)
10680 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
10684 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
10685 int player_nr, int anim, int frame_em)
10687 int element = em_player_mapping[player_nr][anim].element_rnd;
10688 int action = em_player_mapping[player_nr][anim].action;
10689 int direction = em_player_mapping[player_nr][anim].direction;
10690 int graphic = (direction == MV_NONE ?
10691 el_act2img(element, action) :
10692 el_act_dir2img(element, action, direction));
10693 struct GraphicInfo *g = &graphic_info[graphic];
10696 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
10698 stored_player[player_nr].StepFrame = frame_em;
10700 sync_frame = stored_player[player_nr].Frame;
10702 int frame = getAnimationFrame(g->anim_frames,
10705 g->anim_start_frame,
10708 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
10709 &g_em->src_x, &g_em->src_y, FALSE);
10712 #define BD_GFX_RANGE(a, n, i) ((i) >= (a) && (i) < (a) + (n))
10713 #define BD_GFX_FRAME(b, i) (((i) - (b)) * 8)
10715 void InitGraphicInfo_BD(void)
10719 if (graphic_info == NULL) // still at startup phase
10722 // always start with reliable default values
10723 for (i = 0; i < O_MAX_ALL; i++)
10725 bd_object_mapping[i].element_rnd = EL_UNKNOWN;
10726 bd_object_mapping[i].action = ACTION_DEFAULT;
10727 bd_object_mapping[i].direction = MV_NONE;
10730 for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
10732 int e = bd_object_mapping_list[i].element_bd;
10734 bd_object_mapping[e].element_rnd = bd_object_mapping_list[i].element_rnd;
10736 if (bd_object_mapping_list[i].action != -1)
10737 bd_object_mapping[e].action = bd_object_mapping_list[i].action;
10739 if (bd_object_mapping_list[i].direction != -1)
10740 bd_object_mapping[e].direction =
10741 MV_DIR_FROM_BIT(bd_object_mapping_list[i].direction);
10744 for (i = 0; i < O_MAX_ALL; i++)
10746 int element = bd_object_mapping[i].element_rnd;
10747 int action = bd_object_mapping[i].action;
10748 int direction = bd_object_mapping[i].direction;
10750 for (j = 0; j < 8; j++)
10752 int effective_element = element;
10753 int effective_action = action;
10754 int graphic = (el_act_dir2img(effective_element, effective_action,
10756 struct GraphicInfo *g = &graphic_info[graphic];
10757 struct GraphicInfo_BD *g_bd = &graphic_info_bd_object[i][j];
10758 Bitmap *src_bitmap;
10760 int sync_frame = (BD_GFX_RANGE(O_PRE_PL_1, 3, i) ? BD_GFX_FRAME(O_PRE_PL_1, i) :
10761 BD_GFX_RANGE(O_PRE_DIA_1, 5, i) ? BD_GFX_FRAME(O_PRE_DIA_1, i) :
10762 BD_GFX_RANGE(O_PRE_STONE_1, 4, i) ? BD_GFX_FRAME(O_PRE_STONE_1, i) :
10763 BD_GFX_RANGE(O_PRE_STEEL_1, 4, i) ? BD_GFX_FRAME(O_PRE_STEEL_1, i) :
10764 BD_GFX_RANGE(O_BOMB_TICK_1, 7, i) ? BD_GFX_FRAME(O_BOMB_TICK_1, i) :
10765 BD_GFX_RANGE(O_BOMB_EXPL_1, 4, i) ? BD_GFX_FRAME(O_BOMB_EXPL_1, i) :
10766 BD_GFX_RANGE(O_NUT_EXPL_1, 4, i) ? BD_GFX_FRAME(O_NUT_EXPL_1, i) :
10767 BD_GFX_RANGE(O_GHOST_EXPL_1, 4, i) ? BD_GFX_FRAME(O_GHOST_EXPL_1, i) :
10768 BD_GFX_RANGE(O_EXPLODE_1, 5, i) ? BD_GFX_FRAME(O_EXPLODE_1, i) :
10769 BD_GFX_RANGE(O_PRE_CLOCK_1, 4, i) ? BD_GFX_FRAME(O_PRE_CLOCK_1, i) :
10770 BD_GFX_RANGE(O_NITRO_EXPL_1, 4, i) ? BD_GFX_FRAME(O_NITRO_EXPL_1, i) :
10771 BD_GFX_RANGE(O_AMOEBA_2_EXPL_1, 4, i) ? BD_GFX_FRAME(O_AMOEBA_2_EXPL_1, i):
10772 i == O_INBOX_OPEN || i == O_OUTBOX_OPEN ? j :
10774 int frame = getAnimationFrame(g->anim_frames,
10777 g->anim_start_frame,
10780 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
10782 g_bd->bitmap = src_bitmap;
10783 g_bd->src_x = src_x;
10784 g_bd->src_y = src_y;
10785 g_bd->width = TILEX;
10786 g_bd->height = TILEY;
10788 g_bd->graphic = graphic;
10789 g_bd->frame = frame;
10793 // game graphics template for level-specific colors for native BD levels
10794 int graphic = IMG_BDX_GAME_GRAPHICS_COLOR_TEMPLATE;
10795 struct GraphicInfo_BD *g_bd = &graphic_info_bd_color_template;
10796 Bitmap *src_bitmap;
10799 getGraphicSourceExt(graphic, 0, &src_bitmap, &src_x, &src_y, FALSE);
10801 g_bd->bitmap = src_bitmap;
10802 g_bd->src_x = src_x;
10803 g_bd->src_y = src_y;
10804 g_bd->width = TILEX;
10805 g_bd->height = TILEY;
10807 g_bd->graphic = graphic;
10811 void InitGraphicInfo_EM(void)
10815 // always start with reliable default values
10816 for (i = 0; i < GAME_TILE_MAX; i++)
10818 em_object_mapping[i].element_rnd = EL_UNKNOWN;
10819 em_object_mapping[i].is_backside = FALSE;
10820 em_object_mapping[i].action = ACTION_DEFAULT;
10821 em_object_mapping[i].direction = MV_NONE;
10824 // always start with reliable default values
10825 for (p = 0; p < MAX_PLAYERS; p++)
10827 for (i = 0; i < PLY_MAX; i++)
10829 em_player_mapping[p][i].element_rnd = EL_UNKNOWN;
10830 em_player_mapping[p][i].action = ACTION_DEFAULT;
10831 em_player_mapping[p][i].direction = MV_NONE;
10835 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
10837 int e = em_object_mapping_list[i].element_em;
10839 em_object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
10840 em_object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
10842 if (em_object_mapping_list[i].action != -1)
10843 em_object_mapping[e].action = em_object_mapping_list[i].action;
10845 if (em_object_mapping_list[i].direction != -1)
10846 em_object_mapping[e].direction =
10847 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
10850 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
10852 int a = em_player_mapping_list[i].action_em;
10853 int p = em_player_mapping_list[i].player_nr;
10855 em_player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
10857 if (em_player_mapping_list[i].action != -1)
10858 em_player_mapping[p][a].action = em_player_mapping_list[i].action;
10860 if (em_player_mapping_list[i].direction != -1)
10861 em_player_mapping[p][a].direction =
10862 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
10865 for (i = 0; i < GAME_TILE_MAX; i++)
10867 int element = em_object_mapping[i].element_rnd;
10868 int action = em_object_mapping[i].action;
10869 int direction = em_object_mapping[i].direction;
10870 boolean is_backside = em_object_mapping[i].is_backside;
10871 boolean action_exploding = ((action == ACTION_EXPLODING ||
10872 action == ACTION_SMASHED_BY_ROCK ||
10873 action == ACTION_SMASHED_BY_SPRING) &&
10874 element != EL_DIAMOND);
10875 boolean action_active = (action == ACTION_ACTIVE);
10876 boolean action_other = (action == ACTION_OTHER);
10878 for (j = 0; j < 8; j++)
10880 int effective_element = get_effective_element_EM(i, j);
10881 int effective_action = (j < 7 ? action :
10882 i == Xdrip_stretch ? action :
10883 i == Xdrip_stretchB ? action :
10884 i == Ydrip_1_s ? action :
10885 i == Ydrip_1_sB ? action :
10886 i == Yball_1 ? action :
10887 i == Xball_2 ? action :
10888 i == Yball_2 ? action :
10889 i == Yball_blank ? action :
10890 i == Ykey_1_blank ? action :
10891 i == Ykey_2_blank ? action :
10892 i == Ykey_3_blank ? action :
10893 i == Ykey_4_blank ? action :
10894 i == Ykey_5_blank ? action :
10895 i == Ykey_6_blank ? action :
10896 i == Ykey_7_blank ? action :
10897 i == Ykey_8_blank ? action :
10898 i == Ylenses_blank ? action :
10899 i == Ymagnify_blank ? action :
10900 i == Ygrass_blank ? action :
10901 i == Ydirt_blank ? action :
10902 i == Xsand_stonein_1 ? action :
10903 i == Xsand_stonein_2 ? action :
10904 i == Xsand_stonein_3 ? action :
10905 i == Xsand_stonein_4 ? action :
10906 i == Xsand_stoneout_1 ? action :
10907 i == Xsand_stoneout_2 ? action :
10908 i == Xboom_android ? ACTION_EXPLODING :
10909 action_exploding ? ACTION_EXPLODING :
10910 action_active ? action :
10911 action_other ? action :
10913 int graphic = (el_act_dir2img(effective_element, effective_action,
10915 int crumbled = (el_act_dir2crm(effective_element, effective_action,
10917 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
10918 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
10919 boolean has_action_graphics = (graphic != base_graphic);
10920 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
10921 struct GraphicInfo *g = &graphic_info[graphic];
10922 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
10923 Bitmap *src_bitmap;
10925 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
10926 boolean special_animation = (action != ACTION_DEFAULT &&
10927 g->anim_frames == 3 &&
10928 g->anim_delay == 2 &&
10929 g->anim_mode & ANIM_LINEAR);
10930 int sync_frame = (i == Xdrip_stretch ? 7 :
10931 i == Xdrip_stretchB ? 7 :
10932 i == Ydrip_2_s ? j + 8 :
10933 i == Ydrip_2_sB ? j + 8 :
10935 i == Xacid_2 ? 10 :
10936 i == Xacid_3 ? 20 :
10937 i == Xacid_4 ? 30 :
10938 i == Xacid_5 ? 40 :
10939 i == Xacid_6 ? 50 :
10940 i == Xacid_7 ? 60 :
10941 i == Xacid_8 ? 70 :
10942 i == Xfake_acid_1 ? 0 :
10943 i == Xfake_acid_2 ? 10 :
10944 i == Xfake_acid_3 ? 20 :
10945 i == Xfake_acid_4 ? 30 :
10946 i == Xfake_acid_5 ? 40 :
10947 i == Xfake_acid_6 ? 50 :
10948 i == Xfake_acid_7 ? 60 :
10949 i == Xfake_acid_8 ? 70 :
10950 i == Xfake_acid_1_player ? 0 :
10951 i == Xfake_acid_2_player ? 10 :
10952 i == Xfake_acid_3_player ? 20 :
10953 i == Xfake_acid_4_player ? 30 :
10954 i == Xfake_acid_5_player ? 40 :
10955 i == Xfake_acid_6_player ? 50 :
10956 i == Xfake_acid_7_player ? 60 :
10957 i == Xfake_acid_8_player ? 70 :
10959 i == Yball_2 ? j + 8 :
10960 i == Yball_blank ? j + 1 :
10961 i == Ykey_1_blank ? j + 1 :
10962 i == Ykey_2_blank ? j + 1 :
10963 i == Ykey_3_blank ? j + 1 :
10964 i == Ykey_4_blank ? j + 1 :
10965 i == Ykey_5_blank ? j + 1 :
10966 i == Ykey_6_blank ? j + 1 :
10967 i == Ykey_7_blank ? j + 1 :
10968 i == Ykey_8_blank ? j + 1 :
10969 i == Ylenses_blank ? j + 1 :
10970 i == Ymagnify_blank ? j + 1 :
10971 i == Ygrass_blank ? j + 1 :
10972 i == Ydirt_blank ? j + 1 :
10973 i == Xamoeba_1 ? 0 :
10974 i == Xamoeba_2 ? 1 :
10975 i == Xamoeba_3 ? 2 :
10976 i == Xamoeba_4 ? 3 :
10977 i == Xamoeba_5 ? 0 :
10978 i == Xamoeba_6 ? 1 :
10979 i == Xamoeba_7 ? 2 :
10980 i == Xamoeba_8 ? 3 :
10981 i == Xexit_2 ? j + 8 :
10982 i == Xexit_3 ? j + 16 :
10983 i == Xdynamite_1 ? 0 :
10984 i == Xdynamite_2 ? 8 :
10985 i == Xdynamite_3 ? 16 :
10986 i == Xdynamite_4 ? 24 :
10987 i == Xsand_stonein_1 ? j + 1 :
10988 i == Xsand_stonein_2 ? j + 9 :
10989 i == Xsand_stonein_3 ? j + 17 :
10990 i == Xsand_stonein_4 ? j + 25 :
10991 i == Xsand_stoneout_1 && j == 0 ? 0 :
10992 i == Xsand_stoneout_1 && j == 1 ? 0 :
10993 i == Xsand_stoneout_1 && j == 2 ? 1 :
10994 i == Xsand_stoneout_1 && j == 3 ? 2 :
10995 i == Xsand_stoneout_1 && j == 4 ? 2 :
10996 i == Xsand_stoneout_1 && j == 5 ? 3 :
10997 i == Xsand_stoneout_1 && j == 6 ? 4 :
10998 i == Xsand_stoneout_1 && j == 7 ? 4 :
10999 i == Xsand_stoneout_2 && j == 0 ? 5 :
11000 i == Xsand_stoneout_2 && j == 1 ? 6 :
11001 i == Xsand_stoneout_2 && j == 2 ? 7 :
11002 i == Xsand_stoneout_2 && j == 3 ? 8 :
11003 i == Xsand_stoneout_2 && j == 4 ? 9 :
11004 i == Xsand_stoneout_2 && j == 5 ? 11 :
11005 i == Xsand_stoneout_2 && j == 6 ? 13 :
11006 i == Xsand_stoneout_2 && j == 7 ? 15 :
11007 i == Xboom_bug && j == 1 ? 2 :
11008 i == Xboom_bug && j == 2 ? 2 :
11009 i == Xboom_bug && j == 3 ? 4 :
11010 i == Xboom_bug && j == 4 ? 4 :
11011 i == Xboom_bug && j == 5 ? 2 :
11012 i == Xboom_bug && j == 6 ? 2 :
11013 i == Xboom_bug && j == 7 ? 0 :
11014 i == Xboom_tank && j == 1 ? 2 :
11015 i == Xboom_tank && j == 2 ? 2 :
11016 i == Xboom_tank && j == 3 ? 4 :
11017 i == Xboom_tank && j == 4 ? 4 :
11018 i == Xboom_tank && j == 5 ? 2 :
11019 i == Xboom_tank && j == 6 ? 2 :
11020 i == Xboom_tank && j == 7 ? 0 :
11021 i == Xboom_android && j == 7 ? 6 :
11022 i == Xboom_1 && j == 1 ? 2 :
11023 i == Xboom_1 && j == 2 ? 2 :
11024 i == Xboom_1 && j == 3 ? 4 :
11025 i == Xboom_1 && j == 4 ? 4 :
11026 i == Xboom_1 && j == 5 ? 6 :
11027 i == Xboom_1 && j == 6 ? 6 :
11028 i == Xboom_1 && j == 7 ? 8 :
11029 i == Xboom_2 && j == 0 ? 8 :
11030 i == Xboom_2 && j == 1 ? 8 :
11031 i == Xboom_2 && j == 2 ? 10 :
11032 i == Xboom_2 && j == 3 ? 10 :
11033 i == Xboom_2 && j == 4 ? 10 :
11034 i == Xboom_2 && j == 5 ? 12 :
11035 i == Xboom_2 && j == 6 ? 12 :
11036 i == Xboom_2 && j == 7 ? 12 :
11037 special_animation && j == 4 ? 3 :
11038 effective_action != action ? 0 :
11040 int frame = getAnimationFrame(g->anim_frames,
11043 g->anim_start_frame,
11046 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
11047 g->double_movement && is_backside);
11049 g_em->bitmap = src_bitmap;
11050 g_em->src_x = src_x;
11051 g_em->src_y = src_y;
11052 g_em->src_offset_x = 0;
11053 g_em->src_offset_y = 0;
11054 g_em->dst_offset_x = 0;
11055 g_em->dst_offset_y = 0;
11056 g_em->width = TILEX;
11057 g_em->height = TILEY;
11059 g_em->preserve_background = FALSE;
11061 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
11064 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
11065 effective_action == ACTION_MOVING ||
11066 effective_action == ACTION_PUSHING ||
11067 effective_action == ACTION_EATING)) ||
11068 (!has_action_graphics && (effective_action == ACTION_FILLING ||
11069 effective_action == ACTION_EMPTYING)))
11072 (effective_action == ACTION_FALLING ||
11073 effective_action == ACTION_FILLING ||
11074 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
11075 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
11076 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
11077 int num_steps = (i == Ydrip_1_s ? 16 :
11078 i == Ydrip_1_sB ? 16 :
11079 i == Ydrip_2_s ? 16 :
11080 i == Ydrip_2_sB ? 16 :
11081 i == Xsand_stonein_1 ? 32 :
11082 i == Xsand_stonein_2 ? 32 :
11083 i == Xsand_stonein_3 ? 32 :
11084 i == Xsand_stonein_4 ? 32 :
11085 i == Xsand_stoneout_1 ? 16 :
11086 i == Xsand_stoneout_2 ? 16 : 8);
11087 int cx = ABS(dx) * (TILEX / num_steps);
11088 int cy = ABS(dy) * (TILEY / num_steps);
11089 int step_frame = (i == Ydrip_2_s ? j + 8 :
11090 i == Ydrip_2_sB ? j + 8 :
11091 i == Xsand_stonein_2 ? j + 8 :
11092 i == Xsand_stonein_3 ? j + 16 :
11093 i == Xsand_stonein_4 ? j + 24 :
11094 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
11095 int step = (is_backside ? step_frame : num_steps - step_frame);
11097 if (is_backside) // tile where movement starts
11099 if (dx < 0 || dy < 0)
11101 g_em->src_offset_x = cx * step;
11102 g_em->src_offset_y = cy * step;
11106 g_em->dst_offset_x = cx * step;
11107 g_em->dst_offset_y = cy * step;
11110 else // tile where movement ends
11112 if (dx < 0 || dy < 0)
11114 g_em->dst_offset_x = cx * step;
11115 g_em->dst_offset_y = cy * step;
11119 g_em->src_offset_x = cx * step;
11120 g_em->src_offset_y = cy * step;
11124 g_em->width = TILEX - cx * step;
11125 g_em->height = TILEY - cy * step;
11128 // create unique graphic identifier to decide if tile must be redrawn
11129 /* bit 31 - 16 (16 bit): EM style graphic
11130 bit 15 - 12 ( 4 bit): EM style frame
11131 bit 11 - 6 ( 6 bit): graphic width
11132 bit 5 - 0 ( 6 bit): graphic height */
11133 g_em->unique_identifier =
11134 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
11138 for (i = 0; i < GAME_TILE_MAX; i++)
11140 for (j = 0; j < 8; j++)
11142 int element = em_object_mapping[i].element_rnd;
11143 int action = em_object_mapping[i].action;
11144 int direction = em_object_mapping[i].direction;
11145 boolean is_backside = em_object_mapping[i].is_backside;
11146 int graphic_action = el_act_dir2img(element, action, direction);
11147 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
11149 if ((action == ACTION_SMASHED_BY_ROCK ||
11150 action == ACTION_SMASHED_BY_SPRING ||
11151 action == ACTION_EATING) &&
11152 graphic_action == graphic_default)
11154 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
11155 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
11156 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
11157 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
11160 // no separate animation for "smashed by rock" -- use rock instead
11161 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
11162 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
11164 g_em->bitmap = g_xx->bitmap;
11165 g_em->src_x = g_xx->src_x;
11166 g_em->src_y = g_xx->src_y;
11167 g_em->src_offset_x = g_xx->src_offset_x;
11168 g_em->src_offset_y = g_xx->src_offset_y;
11169 g_em->dst_offset_x = g_xx->dst_offset_x;
11170 g_em->dst_offset_y = g_xx->dst_offset_y;
11171 g_em->width = g_xx->width;
11172 g_em->height = g_xx->height;
11173 g_em->unique_identifier = g_xx->unique_identifier;
11176 g_em->preserve_background = TRUE;
11181 for (p = 0; p < MAX_PLAYERS; p++)
11183 for (i = 0; i < PLY_MAX; i++)
11185 int element = em_player_mapping[p][i].element_rnd;
11186 int action = em_player_mapping[p][i].action;
11187 int direction = em_player_mapping[p][i].direction;
11189 for (j = 0; j < 8; j++)
11191 int effective_element = element;
11192 int effective_action = action;
11193 int graphic = (direction == MV_NONE ?
11194 el_act2img(effective_element, effective_action) :
11195 el_act_dir2img(effective_element, effective_action,
11197 struct GraphicInfo *g = &graphic_info[graphic];
11198 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
11199 Bitmap *src_bitmap;
11201 int sync_frame = j;
11202 int frame = getAnimationFrame(g->anim_frames,
11205 g->anim_start_frame,
11208 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
11210 g_em->bitmap = src_bitmap;
11211 g_em->src_x = src_x;
11212 g_em->src_y = src_y;
11213 g_em->src_offset_x = 0;
11214 g_em->src_offset_y = 0;
11215 g_em->dst_offset_x = 0;
11216 g_em->dst_offset_y = 0;
11217 g_em->width = TILEX;
11218 g_em->height = TILEY;
11224 static void CheckSaveEngineSnapshot_BD(boolean frame_max,
11225 boolean player_moving,
11226 boolean player_snapping)
11230 if (!local_player->was_waiting)
11232 if (!CheckSaveEngineSnapshotToList())
11235 local_player->was_waiting = TRUE;
11238 else if (player_moving || player_snapping)
11240 local_player->was_waiting = FALSE;
11244 static void CheckSaveEngineSnapshot_EM(int frame,
11245 boolean any_player_moving,
11246 boolean any_player_snapping,
11247 boolean any_player_dropping)
11249 if (frame == 7 && !any_player_dropping)
11251 if (!local_player->was_waiting)
11253 if (!CheckSaveEngineSnapshotToList())
11256 local_player->was_waiting = TRUE;
11259 else if (any_player_moving || any_player_snapping || any_player_dropping)
11261 local_player->was_waiting = FALSE;
11265 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
11266 boolean murphy_is_dropping)
11268 if (murphy_is_waiting)
11270 if (!local_player->was_waiting)
11272 if (!CheckSaveEngineSnapshotToList())
11275 local_player->was_waiting = TRUE;
11280 local_player->was_waiting = FALSE;
11284 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
11285 boolean button_released)
11287 if (button_released)
11289 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
11290 CheckSaveEngineSnapshotToList();
11292 else if (element_clicked)
11294 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
11295 CheckSaveEngineSnapshotToList();
11297 game.snapshot.changed_action = TRUE;
11301 boolean CheckSingleStepMode_BD(boolean frame_max,
11302 boolean player_moving,
11303 boolean player_snapping)
11305 if (tape.single_step && tape.recording && !tape.pausing)
11306 if (frame_max && FrameCounter > 6)
11307 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11309 CheckSaveEngineSnapshot_BD(frame_max, player_moving, player_snapping);
11311 return tape.pausing;
11314 boolean CheckSingleStepMode_EM(int frame,
11315 boolean any_player_moving,
11316 boolean any_player_snapping,
11317 boolean any_player_dropping)
11319 if (tape.single_step && tape.recording && !tape.pausing)
11320 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
11321 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11323 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
11324 any_player_snapping, any_player_dropping);
11326 return tape.pausing;
11329 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
11330 boolean murphy_is_dropping)
11332 boolean murphy_starts_dropping = FALSE;
11335 for (i = 0; i < MAX_PLAYERS; i++)
11336 if (stored_player[i].force_dropping)
11337 murphy_starts_dropping = TRUE;
11339 if (tape.single_step && tape.recording && !tape.pausing)
11340 if (murphy_is_waiting && !murphy_starts_dropping)
11341 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11343 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
11346 void CheckSingleStepMode_MM(boolean element_clicked,
11347 boolean button_released)
11349 if (tape.single_step && tape.recording && !tape.pausing)
11350 if (button_released)
11351 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11353 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
11356 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
11357 int graphic, int sync_frame)
11359 int frame = getGraphicAnimationFrame(graphic, sync_frame);
11361 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
11364 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
11366 return (IS_NEXT_FRAME(sync_frame, graphic));
11369 int getGraphicInfo_Delay(int graphic)
11371 return graphic_info[graphic].anim_delay;
11374 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
11376 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
11379 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
11385 void PlayMenuSoundExt(int sound)
11387 if (sound == SND_UNDEFINED)
11390 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
11391 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
11394 if (IS_LOOP_SOUND(sound))
11395 PlaySoundLoop(sound);
11400 void PlayMenuSound(void)
11402 PlayMenuSoundExt(menu.sound[game_status]);
11405 void PlayMenuSoundStereo(int sound, int stereo_position)
11407 if (sound == SND_UNDEFINED)
11410 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
11411 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
11414 if (IS_LOOP_SOUND(sound))
11415 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
11417 PlaySoundStereo(sound, stereo_position);
11420 void PlayMenuSoundIfLoopExt(int sound)
11422 if (sound == SND_UNDEFINED)
11425 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
11426 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
11429 if (IS_LOOP_SOUND(sound))
11430 PlaySoundLoop(sound);
11433 void PlayMenuSoundIfLoop(void)
11435 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
11438 void PlayMenuMusicExt(int music)
11440 if (music == MUS_UNDEFINED)
11443 if (!setup.sound_music)
11446 if (IS_LOOP_MUSIC(music))
11447 PlayMusicLoop(music);
11452 void PlayMenuMusic(void)
11454 char *curr_music = getCurrentlyPlayingMusicFilename();
11455 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
11457 if (!strEqual(curr_music, next_music))
11458 PlayMenuMusicExt(menu.music[game_status]);
11461 void PlayMenuSoundsAndMusic(void)
11467 static void FadeMenuSounds(void)
11472 static void FadeMenuMusic(void)
11474 char *curr_music = getCurrentlyPlayingMusicFilename();
11475 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
11477 if (!strEqual(curr_music, next_music))
11481 void FadeMenuSoundsAndMusic(void)
11487 void PlaySoundActivating(void)
11490 PlaySound(SND_MENU_ITEM_ACTIVATING);
11494 void PlaySoundSelecting(void)
11497 PlaySound(SND_MENU_ITEM_SELECTING);
11501 void ToggleAudioSampleRateIfNeeded(void)
11503 int setup_audio_sample_rate = (setup.audio_sample_rate_44100 ? 44100 : 22050);
11505 // if setup and audio sample rate are already matching, nothing do do
11506 if ((setup_audio_sample_rate == audio.sample_rate) ||
11507 !audio.sound_available)
11511 // apparently changing the audio output sample rate does not work at runtime,
11512 // so currently the program has to be restarted to apply the new sample rate
11513 Request("Please restart the program to change audio sample rate!", REQ_CONFIRM);
11517 // set setup value according to successfully changed audio sample rate
11518 setup.audio_sample_rate_44100 = (audio.sample_rate == 44100);
11522 void ToggleFullscreenIfNeeded(void)
11524 // if setup and video fullscreen state are already matching, nothing do do
11525 if (setup.fullscreen == video.fullscreen_enabled ||
11526 !video.fullscreen_available)
11529 SDLSetWindowFullscreen(setup.fullscreen);
11531 // set setup value according to successfully changed fullscreen mode
11532 setup.fullscreen = video.fullscreen_enabled;
11535 void ChangeWindowScalingIfNeeded(void)
11537 // if setup and video window scaling are already matching, nothing do do
11538 if (setup.window_scaling_percent == video.window_scaling_percent ||
11539 video.fullscreen_enabled)
11542 SDLSetWindowScaling(setup.window_scaling_percent);
11544 // set setup value according to successfully changed window scaling
11545 setup.window_scaling_percent = video.window_scaling_percent;
11548 void ChangeVsyncModeIfNeeded(void)
11550 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
11551 int video_vsync_mode = video.vsync_mode;
11553 // if setup and video vsync mode are already matching, nothing do do
11554 if (setup_vsync_mode == video_vsync_mode)
11557 // if renderer is using OpenGL, vsync mode can directly be changed
11558 SDLSetScreenVsyncMode(setup.vsync_mode);
11560 // if vsync mode unchanged, try re-creating renderer to set vsync mode
11561 if (video.vsync_mode == video_vsync_mode)
11563 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
11565 // save backbuffer content which gets lost when re-creating screen
11566 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
11568 // force re-creating screen and renderer to set new vsync mode
11569 video.fullscreen_enabled = !setup.fullscreen;
11571 // when creating new renderer, destroy textures linked to old renderer
11572 FreeAllImageTextures(); // needs old renderer to free the textures
11574 // re-create screen and renderer (including change of vsync mode)
11575 ChangeVideoModeIfNeeded(setup.fullscreen);
11577 // set setup value according to successfully changed fullscreen mode
11578 setup.fullscreen = video.fullscreen_enabled;
11580 // restore backbuffer content from temporary backbuffer backup bitmap
11581 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
11582 FreeBitmap(tmp_backbuffer);
11584 // update visible window/screen
11585 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
11587 // when changing vsync mode, re-create textures for new renderer
11588 InitImageTextures();
11591 // set setup value according to successfully changed vsync mode
11592 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
11595 static void JoinRectangles(int *x, int *y, int *width, int *height,
11596 int x2, int y2, int width2, int height2)
11598 // do not join with "off-screen" rectangle
11599 if (x2 == -1 || y2 == -1)
11604 *width = MAX(*width, width2);
11605 *height = MAX(*height, height2);
11608 void SetAnimStatus(int anim_status_new)
11610 if (anim_status_new == GAME_MODE_MAIN)
11611 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
11612 else if (anim_status_new == GAME_MODE_NAMES)
11613 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
11614 else if (anim_status_new == GAME_MODE_SCORES)
11615 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
11617 global.anim_status_next = anim_status_new;
11619 // directly set screen modes that are entered without fading
11620 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
11621 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
11622 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
11623 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
11624 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
11625 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
11626 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
11627 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
11628 global.anim_status = global.anim_status_next;
11631 void SetGameStatus(int game_status_new)
11633 if (game_status_new != game_status)
11634 game_status_last_screen = game_status;
11636 game_status = game_status_new;
11638 SetAnimStatus(game_status_new);
11641 void SetFontStatus(int game_status_new)
11643 static int last_game_status = -1;
11645 if (game_status_new != -1)
11647 // set game status for font use after storing last game status
11648 last_game_status = game_status;
11649 game_status = game_status_new;
11653 // reset game status after font use from last stored game status
11654 game_status = last_game_status;
11658 void ResetFontStatus(void)
11663 void SetLevelSetInfo(char *identifier, int level_nr)
11665 setString(&levelset.identifier, identifier);
11667 levelset.level_nr = level_nr;
11670 boolean CheckIfAllViewportsHaveChanged(void)
11672 // if game status has not changed, viewports have not changed either
11673 if (game_status == game_status_last)
11676 // check if all viewports have changed with current game status
11678 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
11679 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
11680 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
11681 int new_real_sx = vp_playfield->x;
11682 int new_real_sy = vp_playfield->y;
11683 int new_full_sxsize = vp_playfield->width;
11684 int new_full_sysize = vp_playfield->height;
11685 int new_dx = vp_door_1->x;
11686 int new_dy = vp_door_1->y;
11687 int new_dxsize = vp_door_1->width;
11688 int new_dysize = vp_door_1->height;
11689 int new_vx = vp_door_2->x;
11690 int new_vy = vp_door_2->y;
11691 int new_vxsize = vp_door_2->width;
11692 int new_vysize = vp_door_2->height;
11694 boolean playfield_viewport_has_changed =
11695 (new_real_sx != REAL_SX ||
11696 new_real_sy != REAL_SY ||
11697 new_full_sxsize != FULL_SXSIZE ||
11698 new_full_sysize != FULL_SYSIZE);
11700 boolean door_1_viewport_has_changed =
11703 new_dxsize != DXSIZE ||
11704 new_dysize != DYSIZE);
11706 boolean door_2_viewport_has_changed =
11709 new_vxsize != VXSIZE ||
11710 new_vysize != VYSIZE ||
11711 game_status_last == GAME_MODE_EDITOR);
11713 return (playfield_viewport_has_changed &&
11714 door_1_viewport_has_changed &&
11715 door_2_viewport_has_changed);
11718 boolean CheckFadeAll(void)
11720 return (CheckIfGlobalBorderHasChanged() ||
11721 CheckIfAllViewportsHaveChanged());
11724 void ChangeViewportPropertiesIfNeeded(void)
11726 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
11727 FALSE : setup.small_game_graphics);
11728 int gfx_game_mode = getGlobalGameStatus(game_status);
11729 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
11731 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
11732 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
11733 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
11734 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
11735 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
11736 int new_win_xsize = vp_window->width;
11737 int new_win_ysize = vp_window->height;
11738 int border_left = vp_playfield->border_left;
11739 int border_right = vp_playfield->border_right;
11740 int border_top = vp_playfield->border_top;
11741 int border_bottom = vp_playfield->border_bottom;
11742 int new_sx = vp_playfield->x + border_left;
11743 int new_sy = vp_playfield->y + border_top;
11744 int new_sxsize = vp_playfield->width - border_left - border_right;
11745 int new_sysize = vp_playfield->height - border_top - border_bottom;
11746 int new_real_sx = vp_playfield->x;
11747 int new_real_sy = vp_playfield->y;
11748 int new_full_sxsize = vp_playfield->width;
11749 int new_full_sysize = vp_playfield->height;
11750 int new_dx = vp_door_1->x;
11751 int new_dy = vp_door_1->y;
11752 int new_dxsize = vp_door_1->width;
11753 int new_dysize = vp_door_1->height;
11754 int new_vx = vp_door_2->x;
11755 int new_vy = vp_door_2->y;
11756 int new_vxsize = vp_door_2->width;
11757 int new_vysize = vp_door_2->height;
11758 int new_ex = vp_door_3->x;
11759 int new_ey = vp_door_3->y;
11760 int new_exsize = vp_door_3->width;
11761 int new_eysize = vp_door_3->height;
11762 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
11763 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
11764 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
11765 int new_scr_fieldx = new_sxsize / tilesize;
11766 int new_scr_fieldy = new_sysize / tilesize;
11767 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
11768 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
11769 boolean init_gfx_buffers = FALSE;
11770 boolean init_video_buffer = FALSE;
11771 boolean init_gadgets_and_anims = FALSE;
11772 boolean init_bd_graphics = FALSE;
11773 boolean init_em_graphics = FALSE;
11775 if (new_win_xsize != WIN_XSIZE ||
11776 new_win_ysize != WIN_YSIZE)
11778 WIN_XSIZE = new_win_xsize;
11779 WIN_YSIZE = new_win_ysize;
11781 init_video_buffer = TRUE;
11782 init_gfx_buffers = TRUE;
11783 init_gadgets_and_anims = TRUE;
11785 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
11788 if (new_scr_fieldx != SCR_FIELDX ||
11789 new_scr_fieldy != SCR_FIELDY)
11791 // this always toggles between MAIN and GAME when using small tile size
11793 SCR_FIELDX = new_scr_fieldx;
11794 SCR_FIELDY = new_scr_fieldy;
11796 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
11799 if (new_sx != SX ||
11807 new_sxsize != SXSIZE ||
11808 new_sysize != SYSIZE ||
11809 new_dxsize != DXSIZE ||
11810 new_dysize != DYSIZE ||
11811 new_vxsize != VXSIZE ||
11812 new_vysize != VYSIZE ||
11813 new_exsize != EXSIZE ||
11814 new_eysize != EYSIZE ||
11815 new_real_sx != REAL_SX ||
11816 new_real_sy != REAL_SY ||
11817 new_full_sxsize != FULL_SXSIZE ||
11818 new_full_sysize != FULL_SYSIZE ||
11819 new_tilesize_var != TILESIZE_VAR
11822 // ------------------------------------------------------------------------
11823 // determine next fading area for changed viewport definitions
11824 // ------------------------------------------------------------------------
11826 // start with current playfield area (default fading area)
11829 FADE_SXSIZE = FULL_SXSIZE;
11830 FADE_SYSIZE = FULL_SYSIZE;
11832 // add new playfield area if position or size has changed
11833 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
11834 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
11836 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11837 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
11840 // add current and new door 1 area if position or size has changed
11841 if (new_dx != DX || new_dy != DY ||
11842 new_dxsize != DXSIZE || new_dysize != DYSIZE)
11844 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11845 DX, DY, DXSIZE, DYSIZE);
11846 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11847 new_dx, new_dy, new_dxsize, new_dysize);
11850 // add current and new door 2 area if position or size has changed
11851 if (new_vx != VX || new_vy != VY ||
11852 new_vxsize != VXSIZE || new_vysize != VYSIZE)
11854 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11855 VX, VY, VXSIZE, VYSIZE);
11856 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11857 new_vx, new_vy, new_vxsize, new_vysize);
11860 // ------------------------------------------------------------------------
11861 // handle changed tile size
11862 // ------------------------------------------------------------------------
11864 if (new_tilesize_var != TILESIZE_VAR)
11866 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
11868 // changing tile size invalidates scroll values of engine snapshots
11869 FreeEngineSnapshotSingle();
11871 // changing tile size requires update of graphic mapping for BD/EM engine
11872 init_bd_graphics = TRUE;
11873 init_em_graphics = TRUE;
11884 SXSIZE = new_sxsize;
11885 SYSIZE = new_sysize;
11886 DXSIZE = new_dxsize;
11887 DYSIZE = new_dysize;
11888 VXSIZE = new_vxsize;
11889 VYSIZE = new_vysize;
11890 EXSIZE = new_exsize;
11891 EYSIZE = new_eysize;
11892 REAL_SX = new_real_sx;
11893 REAL_SY = new_real_sy;
11894 FULL_SXSIZE = new_full_sxsize;
11895 FULL_SYSIZE = new_full_sysize;
11896 TILESIZE_VAR = new_tilesize_var;
11898 init_gfx_buffers = TRUE;
11899 init_gadgets_and_anims = TRUE;
11901 // Debug("tools:viewport", "viewports: init_gfx_buffers");
11902 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
11905 if (init_gfx_buffers)
11907 // Debug("tools:viewport", "init_gfx_buffers");
11909 SCR_FIELDX = new_scr_fieldx_buffers;
11910 SCR_FIELDY = new_scr_fieldy_buffers;
11914 SCR_FIELDX = new_scr_fieldx;
11915 SCR_FIELDY = new_scr_fieldy;
11917 SetDrawDeactivationMask(REDRAW_NONE);
11918 SetDrawBackgroundMask(REDRAW_FIELD);
11921 if (init_video_buffer)
11923 // Debug("tools:viewport", "init_video_buffer");
11925 FreeAllImageTextures(); // needs old renderer to free the textures
11927 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
11928 InitImageTextures();
11931 if (init_gadgets_and_anims)
11933 // Debug("tools:viewport", "init_gadgets_and_anims");
11936 InitGlobalAnimations();
11939 if (init_bd_graphics)
11941 InitGraphicInfo_BD();
11944 if (init_em_graphics)
11946 InitGraphicInfo_EM();
11950 void OpenURL(char *url)
11952 #if SDL_VERSION_ATLEAST(2,0,14)
11955 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
11956 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
11957 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
11961 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
11963 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
11967 // ============================================================================
11969 // ============================================================================
11971 #if defined(PLATFORM_WINDOWS)
11972 /* FILETIME of Jan 1 1970 00:00:00. */
11973 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
11976 * timezone information is stored outside the kernel so tzp isn't used anymore.
11978 * Note: this function is not for Win32 high precision timing purpose. See
11981 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
11983 FILETIME file_time;
11984 SYSTEMTIME system_time;
11985 ULARGE_INTEGER ularge;
11987 GetSystemTime(&system_time);
11988 SystemTimeToFileTime(&system_time, &file_time);
11989 ularge.LowPart = file_time.dwLowDateTime;
11990 ularge.HighPart = file_time.dwHighDateTime;
11992 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
11993 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
11999 static char *test_init_uuid_random_function_simple(void)
12001 static char seed_text[100];
12002 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
12004 sprintf(seed_text, "%d", seed);
12009 static char *test_init_uuid_random_function_better(void)
12011 static char seed_text[100];
12012 struct timeval current_time;
12014 gettimeofday(¤t_time, NULL);
12016 prng_seed_bytes(¤t_time, sizeof(current_time));
12018 sprintf(seed_text, "%ld.%ld",
12019 (long)current_time.tv_sec,
12020 (long)current_time.tv_usec);
12025 #if defined(PLATFORM_WINDOWS)
12026 static char *test_init_uuid_random_function_better_windows(void)
12028 static char seed_text[100];
12029 struct timeval current_time;
12031 gettimeofday_windows(¤t_time, NULL);
12033 prng_seed_bytes(¤t_time, sizeof(current_time));
12035 sprintf(seed_text, "%ld.%ld",
12036 (long)current_time.tv_sec,
12037 (long)current_time.tv_usec);
12043 static unsigned int test_uuid_random_function_simple(int max)
12045 return GetSimpleRandom(max);
12048 static unsigned int test_uuid_random_function_better(int max)
12050 return (max > 0 ? prng_get_uint() % max : 0);
12053 #if defined(PLATFORM_WINDOWS)
12054 #define NUM_UUID_TESTS 3
12056 #define NUM_UUID_TESTS 2
12059 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
12061 HashTable *hash_seeds =
12062 create_hashtable(get_hash_from_string, hash_key_strings_are_equal, free, NULL);
12063 HashTable *hash_uuids =
12064 create_hashtable(get_hash_from_string, hash_key_strings_are_equal, free, NULL);
12065 static char message[100];
12068 char *random_name = (nr == 0 ? "simple" : "better");
12069 char *random_type = (always_seed ? "always" : "only once");
12070 char *(*init_random_function)(void) =
12072 test_init_uuid_random_function_simple :
12073 test_init_uuid_random_function_better);
12074 unsigned int (*random_function)(int) =
12076 test_uuid_random_function_simple :
12077 test_uuid_random_function_better);
12080 #if defined(PLATFORM_WINDOWS)
12083 random_name = "windows";
12084 init_random_function = test_init_uuid_random_function_better_windows;
12090 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
12091 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
12093 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
12094 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
12095 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
12097 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
12101 // always initialize random number generator at least once
12102 init_random_function();
12104 unsigned int time_start = SDL_GetTicks();
12106 for (i = 0; i < num_uuids; i++)
12110 char *seed = getStringCopy(init_random_function());
12112 hashtable_remove(hash_seeds, seed);
12113 hashtable_insert(hash_seeds, seed, "1");
12116 char *uuid = getStringCopy(getUUIDExt(random_function));
12118 hashtable_remove(hash_uuids, uuid);
12119 hashtable_insert(hash_uuids, uuid, "1");
12122 int num_unique_seeds = hashtable_count(hash_seeds);
12123 int num_unique_uuids = hashtable_count(hash_uuids);
12125 unsigned int time_needed = SDL_GetTicks() - time_start;
12127 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
12129 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
12132 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
12134 if (nr == NUM_UUID_TESTS - 1 && always_seed)
12135 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
12137 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
12139 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
12141 Request(message, REQ_CONFIRM);
12143 hashtable_destroy(hash_seeds);
12144 hashtable_destroy(hash_uuids);
12147 void TestGeneratingUUIDs(void)
12149 int num_uuids = 1000000;
12152 for (i = 0; i < NUM_UUID_TESTS; i++)
12153 for (j = 0; j < 2; j++)
12154 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
12156 CloseAllAndExit(0);