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_BD_GAME_GRAPHICS_COLOR_TEMPLATE;
3860 int graphic_template = el2preimg(element_template);
3861 int element_default = EL_BD_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_BD_PLAYER;
6033 el_sand = EL_BD_SAND;
6034 el_wall = EL_BD_WALL;
6035 el_steelwall = EL_BD_STEELWALL;
6036 el_exit_closed = EL_BD_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
6164 O_DIRT_SLOPED_UP_RIGHT, TRUE,
6165 EL_BD_SAND_SLOPED_UP_RIGHT, -1, -1
6168 O_DIRT_SLOPED_UP_LEFT, TRUE,
6169 EL_BD_SAND_SLOPED_UP_LEFT, -1, -1
6172 O_DIRT_SLOPED_DOWN_LEFT, TRUE,
6173 EL_BD_SAND_SLOPED_DOWN_LEFT, -1, -1
6176 O_DIRT_SLOPED_DOWN_RIGHT, TRUE,
6177 EL_BD_SAND_SLOPED_DOWN_RIGHT, -1, -1
6181 EL_BD_SAND_BALL, -1, -1
6184 O_DIRT_BALL_F, TRUE,
6185 EL_BD_SAND_BALL_FALLING, -1, -1
6188 O_DIRT_BALL_F, FALSE,
6189 EL_BD_SAND_BALL, ACTION_FALLING, -1
6193 EL_BD_SAND_LOOSE, -1, -1
6196 O_DIRT_LOOSE_F, TRUE,
6197 EL_BD_SAND_LOOSE_FALLING, -1, -1
6200 O_DIRT_LOOSE_F, FALSE,
6201 EL_BD_SAND_LOOSE, ACTION_FALLING, -1
6205 EL_BD_SAND_2, -1, -1
6212 O_BRICK_SLOPED_UP_RIGHT, TRUE,
6213 EL_BD_WALL_SLOPED_UP_RIGHT, -1, -1
6216 O_BRICK_SLOPED_UP_LEFT, TRUE,
6217 EL_BD_WALL_SLOPED_UP_LEFT, -1, -1
6220 O_BRICK_SLOPED_DOWN_LEFT, TRUE,
6221 EL_BD_WALL_SLOPED_DOWN_LEFT, -1, -1
6224 O_BRICK_SLOPED_DOWN_RIGHT, TRUE,
6225 EL_BD_WALL_SLOPED_DOWN_RIGHT, -1, -1
6228 O_BRICK_NON_SLOPED, TRUE,
6229 EL_BD_WALL_NON_SLOPED, -1, -1
6233 EL_BD_MAGIC_WALL, ACTION_ACTIVE, -1
6237 EL_BD_EXIT_CLOSED, -1, -1
6241 EL_BD_EXIT_OPEN, -1, -1
6244 O_PRE_INVIS_OUTBOX, TRUE,
6245 EL_BD_INVISIBLE_EXIT_CLOSED, -1, -1
6248 O_INVIS_OUTBOX, TRUE,
6249 EL_BD_INVISIBLE_EXIT_OPEN, -1, -1
6253 EL_BD_STEELWALL, -1, -1
6256 O_STEEL_SLOPED_UP_RIGHT, TRUE,
6257 EL_BD_STEELWALL_SLOPED_UP_RIGHT, -1, -1
6260 O_STEEL_SLOPED_UP_LEFT, TRUE,
6261 EL_BD_STEELWALL_SLOPED_UP_LEFT, -1, -1
6264 O_STEEL_SLOPED_DOWN_LEFT, TRUE,
6265 EL_BD_STEELWALL_SLOPED_DOWN_LEFT, -1, -1
6268 O_STEEL_SLOPED_DOWN_RIGHT, TRUE,
6269 EL_BD_STEELWALL_SLOPED_DOWN_RIGHT, -1, -1
6272 O_STEEL_EXPLODABLE, TRUE,
6273 EL_BD_STEELWALL_EXPLODABLE, -1, -1
6276 O_STEEL_EATABLE, TRUE,
6277 EL_BD_STEELWALL_DIGGABLE, -1, -1
6280 O_BRICK_EATABLE, TRUE,
6281 EL_BD_WALL_DIGGABLE, -1, -1
6289 EL_BD_ROCK_FALLING, -1, -1
6293 EL_BD_ROCK, ACTION_FALLING, -1
6296 O_FLYING_STONE, TRUE,
6297 EL_BD_FLYING_ROCK, -1, -1
6300 O_FLYING_STONE_F, TRUE,
6301 EL_BD_FLYING_ROCK_FLYING, -1, -1
6304 O_FLYING_STONE_F, FALSE,
6305 EL_BD_FLYING_ROCK, ACTION_FLYING, -1
6309 EL_BD_MEGA_ROCK, -1, -1
6312 O_MEGA_STONE_F, TRUE,
6313 EL_BD_MEGA_ROCK_FALLING, -1, -1
6316 O_MEGA_STONE_F, FALSE,
6317 EL_BD_MEGA_ROCK, ACTION_FALLING, -1
6321 EL_BD_DIAMOND, -1, -1
6325 EL_BD_DIAMOND_FALLING, -1, -1
6329 EL_BD_DIAMOND, ACTION_FALLING, -1
6332 O_FLYING_DIAMOND, TRUE,
6333 EL_BD_FLYING_DIAMOND, -1, -1
6336 O_FLYING_DIAMOND_F, TRUE,
6337 EL_BD_FLYING_DIAMOND_FLYING, -1, -1
6340 O_FLYING_DIAMOND_F, FALSE,
6341 EL_BD_FLYING_DIAMOND, ACTION_FLYING, -1
6349 EL_BD_NUT_FALLING, -1, -1
6353 EL_BD_NUT, ACTION_FALLING, -1
6356 O_BLADDER_SPENDER, TRUE,
6357 EL_BD_BLADDER_SPENDER, -1, -1
6364 O_H_EXPANDING_WALL, TRUE,
6365 EL_BD_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6368 O_V_EXPANDING_WALL, TRUE,
6369 EL_BD_EXPANDABLE_WALL_VERTICAL, -1, -1
6372 O_EXPANDING_WALL, TRUE,
6373 EL_BD_EXPANDABLE_WALL_ANY, -1, -1
6376 O_H_EXPANDING_STEEL_WALL, TRUE,
6377 EL_BD_EXPANDABLE_STEELWALL_HORIZONTAL, -1, -1
6380 O_V_EXPANDING_STEEL_WALL, TRUE,
6381 EL_BD_EXPANDABLE_STEELWALL_VERTICAL, -1, -1
6384 O_EXPANDING_STEEL_WALL, TRUE,
6385 EL_BD_EXPANDABLE_STEELWALL_ANY, -1, -1
6388 O_EXPANDING_WALL_SWITCH, TRUE,
6389 EL_BD_EXPANDABLE_WALL_SWITCH, -1, -1
6392 O_CREATURE_SWITCH, TRUE,
6393 EL_BD_CREATURE_SWITCH, -1, -1
6396 O_BITER_SWITCH, TRUE,
6397 EL_BD_BITER_SWITCH_1, -1, -1
6400 O_REPLICATOR_SWITCH, TRUE,
6401 EL_BD_REPLICATOR_SWITCH, -1, -1
6404 O_CONVEYOR_SWITCH, TRUE,
6405 EL_BD_CONVEYOR_SWITCH, -1, -1
6408 O_CONVEYOR_DIR_SWITCH, TRUE,
6409 EL_BD_CONVEYOR_DIR_SWITCH, -1, -1
6416 O_FALLING_WALL, TRUE,
6417 EL_BD_FALLING_WALL, -1, -1
6420 O_FALLING_WALL_F, TRUE,
6421 EL_BD_FALLING_WALL_FALLING, -1, -1
6424 O_FALLING_WALL_F, FALSE,
6425 EL_BD_FALLING_WALL, ACTION_FALLING, -1
6432 O_TIME_PENALTY, TRUE,
6433 EL_BD_TIME_PENALTY, -1, -1
6437 EL_BD_GRAVESTONE, -1, -1
6440 O_STONE_GLUED, TRUE,
6441 EL_BD_ROCK_GLUED, -1, -1
6444 O_DIAMOND_GLUED, TRUE,
6445 EL_BD_DIAMOND_GLUED, -1, -1
6448 O_DIAMOND_KEY, TRUE,
6449 EL_BD_DIAMOND_KEY, -1, -1
6452 O_TRAPPED_DIAMOND, TRUE,
6453 EL_BD_TRAPPED_DIAMOND, -1, -1
6461 EL_BD_SAND_GLUED, -1, -1
6477 EL_BD_GATE_1, -1, -1
6481 EL_BD_GATE_2, -1, -1
6485 EL_BD_GATE_3, -1, -1
6492 O_GRAVITY_SWITCH, TRUE,
6493 EL_BD_GRAVITY_SWITCH, -1, -1
6496 O_PNEUMATIC_HAMMER, TRUE,
6497 EL_BD_PNEUMATIC_HAMMER, -1, -1
6501 EL_BD_TELEPORTER, -1, -1
6505 EL_BD_SKELETON, -1, -1
6513 EL_BD_WATER_1, -1, -1
6521 EL_BD_WATER_2, -1, -1
6529 EL_BD_WATER_3, -1, -1
6537 EL_BD_WATER_4, -1, -1
6545 EL_BD_WATER_5, -1, -1
6553 EL_BD_WATER_6, -1, -1
6561 EL_BD_WATER_7, -1, -1
6569 EL_BD_WATER_8, -1, -1
6577 EL_BD_WATER_9, -1, -1
6585 EL_BD_WATER_10, -1, -1
6593 EL_BD_WATER_11, -1, -1
6601 EL_BD_WATER_12, -1, -1
6609 EL_BD_WATER_13, -1, -1
6617 EL_BD_WATER_14, -1, -1
6625 EL_BD_WATER_15, -1, -1
6633 EL_BD_WATER_16, -1, -1
6641 EL_BD_COW_LEFT, -1, -1
6645 EL_BD_COW_UP, -1, -1
6649 EL_BD_COW_RIGHT, -1, -1
6653 EL_BD_COW_DOWN, -1, -1
6656 O_COW_ENCLOSED_1, TRUE,
6657 EL_BD_COW_ENCLOSED_1, -1, -1
6660 O_COW_ENCLOSED_1, FALSE,
6661 EL_BD_COW_DOWN, -1, -1
6664 O_COW_ENCLOSED_2, TRUE,
6665 EL_BD_COW_ENCLOSED_2, -1, -1
6668 O_COW_ENCLOSED_2, FALSE,
6669 EL_BD_COW_DOWN, -1, -1
6672 O_COW_ENCLOSED_3, TRUE,
6673 EL_BD_COW_ENCLOSED_3, -1, -1
6676 O_COW_ENCLOSED_3, FALSE,
6677 EL_BD_COW_DOWN, -1, -1
6680 O_COW_ENCLOSED_4, TRUE,
6681 EL_BD_COW_ENCLOSED_4, -1, -1
6684 O_COW_ENCLOSED_4, FALSE,
6685 EL_BD_COW_DOWN, -1, -1
6688 O_COW_ENCLOSED_5, TRUE,
6689 EL_BD_COW_ENCLOSED_5, -1, -1
6692 O_COW_ENCLOSED_5, FALSE,
6693 EL_BD_COW_DOWN, -1, -1
6696 O_COW_ENCLOSED_6, TRUE,
6697 EL_BD_COW_ENCLOSED_6, -1, -1
6700 O_COW_ENCLOSED_6, FALSE,
6701 EL_BD_COW_DOWN, -1, -1
6704 O_COW_ENCLOSED_7, TRUE,
6705 EL_BD_COW_ENCLOSED_7, -1, -1
6708 O_COW_ENCLOSED_7, FALSE,
6709 EL_BD_COW_DOWN, -1, -1
6712 O_WALLED_DIAMOND, TRUE,
6713 EL_BD_WALL_DIAMOND, -1, -1
6716 O_WALLED_KEY_1, TRUE,
6717 EL_BD_WALL_KEY_1, -1, -1
6720 O_WALLED_KEY_2, TRUE,
6721 EL_BD_WALL_KEY_2, -1, -1
6724 O_WALLED_KEY_3, TRUE,
6725 EL_BD_WALL_KEY_3, -1, -1
6729 EL_BD_AMOEBA, -1, -1
6733 EL_BD_AMOEBA_2, -1, -1
6737 EL_BD_REPLICATOR, -1, -1
6740 O_CONVEYOR_LEFT, TRUE,
6741 EL_BD_CONVEYOR_LEFT, -1, -1
6744 O_CONVEYOR_RIGHT, TRUE,
6745 EL_BD_CONVEYOR_RIGHT, -1, -1
6757 EL_BD_VOODOO_DOLL, -1, -1
6765 EL_BD_BLADDER, -1, -1
6769 EL_BD_BLADDER_1, -1, -1
6773 EL_BD_BLADDER, -1, -1
6777 EL_BD_BLADDER_2, -1, -1
6781 EL_BD_BLADDER, -1, -1
6785 EL_BD_BLADDER_3, -1, -1
6789 EL_BD_BLADDER, -1, -1
6793 EL_BD_BLADDER_4, -1, -1
6797 EL_BD_BLADDER, -1, -1
6801 EL_BD_BLADDER_5, -1, -1
6805 EL_BD_BLADDER, -1, -1
6809 EL_BD_BLADDER_6, -1, -1
6813 EL_BD_BLADDER, -1, -1
6817 EL_BD_BLADDER_7, -1, -1
6821 EL_BD_BLADDER, -1, -1
6825 EL_BD_BLADDER_8, -1, -1
6829 EL_BD_BLADDER, -1, -1
6832 O_WAITING_STONE, TRUE,
6833 EL_BD_WAITING_ROCK, -1, -1
6836 O_CHASING_STONE, TRUE,
6837 EL_BD_CHASING_ROCK, -1, -1
6845 EL_BD_FIREFLY_LEFT, -1, -1
6849 EL_BD_FIREFLY_UP, -1, -1
6853 EL_BD_FIREFLY_RIGHT, -1, -1
6857 EL_BD_FIREFLY_DOWN, -1, -1
6860 O_ALT_FIREFLY_1, TRUE,
6861 EL_BD_FIREFLY_2_LEFT, -1, -1
6864 O_ALT_FIREFLY_2, TRUE,
6865 EL_BD_FIREFLY_2_UP, -1, -1
6868 O_ALT_FIREFLY_3, TRUE,
6869 EL_BD_FIREFLY_2_RIGHT, -1, -1
6872 O_ALT_FIREFLY_4, TRUE,
6873 EL_BD_FIREFLY_2_DOWN, -1, -1
6877 EL_BD_BUTTERFLY_LEFT, -1, -1
6881 EL_BD_BUTTERFLY_UP, -1, -1
6885 EL_BD_BUTTERFLY_RIGHT, -1, -1
6889 EL_BD_BUTTERFLY_DOWN, -1, -1
6892 O_ALT_BUTTER_1, TRUE,
6893 EL_BD_BUTTERFLY_2_LEFT, -1, -1
6896 O_ALT_BUTTER_2, TRUE,
6897 EL_BD_BUTTERFLY_2_UP, -1, -1
6900 O_ALT_BUTTER_3, TRUE,
6901 EL_BD_BUTTERFLY_2_RIGHT, -1, -1
6904 O_ALT_BUTTER_4, TRUE,
6905 EL_BD_BUTTERFLY_2_DOWN, -1, -1
6909 EL_BD_STONEFLY_LEFT, -1, -1
6913 EL_BD_STONEFLY_UP, -1, -1
6917 EL_BD_STONEFLY_RIGHT, -1, -1
6921 EL_BD_STONEFLY_DOWN, -1, -1
6925 EL_BD_BITER_UP, -1, -1
6929 EL_BD_BITER_RIGHT, -1, -1
6933 EL_BD_BITER_DOWN, -1, -1
6937 EL_BD_BITER_LEFT, -1, -1
6940 O_DRAGONFLY_1, TRUE,
6941 EL_BD_DRAGONFLY_LEFT, -1, -1
6944 O_DRAGONFLY_2, TRUE,
6945 EL_BD_DRAGONFLY_UP, -1, -1
6948 O_DRAGONFLY_3, TRUE,
6949 EL_BD_DRAGONFLY_RIGHT, -1, -1
6952 O_DRAGONFLY_4, TRUE,
6953 EL_BD_DRAGONFLY_DOWN, -1, -1
6957 EL_BD_PLAYER_GROWING_1, -1, -1
6961 EL_BD_PLAYER, ACTION_GROWING, -1
6965 EL_BD_PLAYER_GROWING_2, -1, -1
6969 EL_BD_PLAYER, ACTION_GROWING, -1
6973 EL_BD_PLAYER_GROWING_3, -1, -1
6977 EL_BD_PLAYER, ACTION_GROWING, -1
6981 EL_BD_PLAYER, -1, -1
6984 O_PLAYER_BOMB, TRUE,
6985 EL_BD_PLAYER_WITH_BOMB, -1, -1
6988 O_PLAYER_ROCKET_LAUNCHER, TRUE,
6989 EL_BD_PLAYER_WITH_ROCKET_LAUNCHER, -1, -1
6992 O_PLAYER_GLUED, TRUE,
6993 EL_BD_PLAYER_GLUED, -1, -1
6996 O_PLAYER_STIRRING, TRUE,
6997 EL_BD_PLAYER_STIRRING, -1, -1
7000 O_ROCKET_LAUNCHER, TRUE,
7001 EL_BD_ROCKET_LAUNCHER, -1, -1
7005 EL_BD_ROCKET_RIGHT, -1, -1
7009 EL_BD_ROCKET_UP, -1, -1
7013 EL_BD_ROCKET_LEFT, -1, -1
7017 EL_BD_ROCKET_DOWN, -1, -1
7024 O_BOMB_TICK_1, TRUE,
7025 EL_BD_BOMB_TICKING_1, -1, -1
7028 O_BOMB_TICK_1, FALSE,
7029 EL_BD_BOMB, ACTION_ACTIVE, -1
7032 O_BOMB_TICK_2, TRUE,
7033 EL_BD_BOMB_TICKING_2, -1, -1
7036 O_BOMB_TICK_2, FALSE,
7037 EL_BD_BOMB, ACTION_ACTIVE, -1
7040 O_BOMB_TICK_3, TRUE,
7041 EL_BD_BOMB_TICKING_3, -1, -1
7044 O_BOMB_TICK_3, FALSE,
7045 EL_BD_BOMB, ACTION_ACTIVE, -1
7048 O_BOMB_TICK_4, TRUE,
7049 EL_BD_BOMB_TICKING_4, -1, -1
7052 O_BOMB_TICK_4, FALSE,
7053 EL_BD_BOMB, ACTION_ACTIVE, -1
7056 O_BOMB_TICK_5, TRUE,
7057 EL_BD_BOMB_TICKING_5, -1, -1
7060 O_BOMB_TICK_5, FALSE,
7061 EL_BD_BOMB, ACTION_ACTIVE, -1
7064 O_BOMB_TICK_6, TRUE,
7065 EL_BD_BOMB_TICKING_6, -1, -1
7068 O_BOMB_TICK_6, FALSE,
7069 EL_BD_BOMB, ACTION_ACTIVE, -1
7072 O_BOMB_TICK_7, TRUE,
7073 EL_BD_BOMB_TICKING_7, -1, -1
7076 O_BOMB_TICK_7, FALSE,
7077 EL_BD_BOMB, ACTION_ACTIVE, -1
7081 EL_BD_NITRO_PACK, -1, -1
7084 O_NITRO_PACK_F, TRUE,
7085 EL_BD_NITRO_PACK_FALLING, -1, -1
7088 O_NITRO_PACK_F, FALSE,
7089 EL_BD_NITRO_PACK, ACTION_FALLING, -1
7092 O_PRE_CLOCK_1, TRUE,
7093 EL_BD_CLOCK_GROWING_1, -1, -1
7096 O_PRE_CLOCK_1, FALSE,
7097 EL_BD_CLOCK, ACTION_GROWING, -1
7100 O_PRE_CLOCK_2, TRUE,
7101 EL_BD_CLOCK_GROWING_2, -1, -1
7104 O_PRE_CLOCK_2, FALSE,
7105 EL_BD_CLOCK, ACTION_GROWING, -1
7108 O_PRE_CLOCK_3, TRUE,
7109 EL_BD_CLOCK_GROWING_3, -1, -1
7112 O_PRE_CLOCK_3, FALSE,
7113 EL_BD_CLOCK, ACTION_GROWING, -1
7116 O_PRE_CLOCK_4, TRUE,
7117 EL_BD_CLOCK_GROWING_4, -1, -1
7120 O_PRE_CLOCK_4, FALSE,
7121 EL_BD_CLOCK, ACTION_GROWING, -1
7125 EL_BD_DIAMOND_GROWING_1, -1, -1
7129 EL_BD_DIAMOND, ACTION_GROWING, -1
7133 EL_BD_DIAMOND_GROWING_2, -1, -1
7137 EL_BD_DIAMOND, ACTION_GROWING, -1
7141 EL_BD_DIAMOND_GROWING_3, -1, -1
7145 EL_BD_DIAMOND, ACTION_GROWING, -1
7149 EL_BD_DIAMOND_GROWING_4, -1, -1
7153 EL_BD_DIAMOND, ACTION_GROWING, -1
7157 EL_BD_DIAMOND_GROWING_5, -1, -1
7161 EL_BD_DIAMOND, ACTION_GROWING, -1
7165 EL_BD_EXPLODING_1, -1, -1
7169 EL_DEFAULT, ACTION_EXPLODING, -1
7173 EL_BD_EXPLODING_2, -1, -1
7177 EL_DEFAULT, ACTION_EXPLODING, -1
7181 EL_BD_EXPLODING_3, -1, -1
7185 EL_DEFAULT, ACTION_EXPLODING, -1
7189 EL_BD_EXPLODING_4, -1, -1
7193 EL_DEFAULT, ACTION_EXPLODING, -1
7197 EL_BD_EXPLODING_5, -1, -1
7201 EL_DEFAULT, ACTION_EXPLODING, -1
7204 O_PRE_STONE_1, TRUE,
7205 EL_BD_ROCK_GROWING_1, -1, -1
7208 O_PRE_STONE_1, FALSE,
7209 EL_BD_ROCK, ACTION_GROWING, -1
7212 O_PRE_STONE_2, TRUE,
7213 EL_BD_ROCK_GROWING_2, -1, -1
7216 O_PRE_STONE_2, FALSE,
7217 EL_BD_ROCK, ACTION_GROWING, -1
7220 O_PRE_STONE_3, TRUE,
7221 EL_BD_ROCK_GROWING_3, -1, -1
7224 O_PRE_STONE_3, FALSE,
7225 EL_BD_ROCK, ACTION_GROWING, -1
7228 O_PRE_STONE_4, TRUE,
7229 EL_BD_ROCK_GROWING_4, -1, -1
7232 O_PRE_STONE_4, FALSE,
7233 EL_BD_ROCK, ACTION_GROWING, -1
7236 O_PRE_STEEL_1, TRUE,
7237 EL_BD_STEELWALL_GROWING_1, -1, -1
7240 O_PRE_STEEL_1, FALSE,
7241 EL_BD_STEELWALL, ACTION_GROWING, -1
7244 O_PRE_STEEL_2, TRUE,
7245 EL_BD_STEELWALL_GROWING_2, -1, -1
7248 O_PRE_STEEL_2, FALSE,
7249 EL_BD_STEELWALL, ACTION_GROWING, -1
7252 O_PRE_STEEL_3, TRUE,
7253 EL_BD_STEELWALL_GROWING_3, -1, -1
7256 O_PRE_STEEL_3, FALSE,
7257 EL_BD_STEELWALL, ACTION_GROWING, -1
7260 O_PRE_STEEL_4, TRUE,
7261 EL_BD_STEELWALL_GROWING_4, -1, -1
7264 O_PRE_STEEL_4, FALSE,
7265 EL_BD_STEELWALL, ACTION_GROWING, -1
7268 O_GHOST_EXPL_1, TRUE,
7269 EL_BD_GHOST_EXPLODING_1, -1, -1
7272 O_GHOST_EXPL_1, FALSE,
7273 EL_BD_GHOST, ACTION_EXPLODING, -1
7276 O_GHOST_EXPL_2, TRUE,
7277 EL_BD_GHOST_EXPLODING_2, -1, -1
7280 O_GHOST_EXPL_2, FALSE,
7281 EL_BD_GHOST, ACTION_EXPLODING, -1
7284 O_GHOST_EXPL_3, TRUE,
7285 EL_BD_GHOST_EXPLODING_3, -1, -1
7288 O_GHOST_EXPL_3, FALSE,
7289 EL_BD_GHOST, ACTION_EXPLODING, -1
7292 O_GHOST_EXPL_4, TRUE,
7293 EL_BD_GHOST_EXPLODING_4, -1, -1
7296 O_GHOST_EXPL_4, FALSE,
7297 EL_BD_GHOST, ACTION_EXPLODING, -1
7300 O_BOMB_EXPL_1, TRUE,
7301 EL_BD_BOMB_EXPLODING_1, -1, -1
7304 O_BOMB_EXPL_1, FALSE,
7305 EL_BD_BOMB, ACTION_EXPLODING, -1
7308 O_BOMB_EXPL_2, TRUE,
7309 EL_BD_BOMB_EXPLODING_2, -1, -1
7312 O_BOMB_EXPL_2, FALSE,
7313 EL_BD_BOMB, ACTION_EXPLODING, -1
7316 O_BOMB_EXPL_3, TRUE,
7317 EL_BD_BOMB_EXPLODING_3, -1, -1
7320 O_BOMB_EXPL_3, FALSE,
7321 EL_BD_BOMB, ACTION_EXPLODING, -1
7324 O_BOMB_EXPL_4, TRUE,
7325 EL_BD_BOMB_EXPLODING_4, -1, -1
7328 O_BOMB_EXPL_4, FALSE,
7329 EL_BD_BOMB, ACTION_EXPLODING, -1
7332 O_NITRO_EXPL_1, TRUE,
7333 EL_BD_NITRO_PACK_EXPLODING_1, -1, -1
7336 O_NITRO_EXPL_1, FALSE,
7337 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
7340 O_NITRO_EXPL_2, TRUE,
7341 EL_BD_NITRO_PACK_EXPLODING_2, -1, -1
7344 O_NITRO_EXPL_2, FALSE,
7345 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
7348 O_NITRO_EXPL_3, TRUE,
7349 EL_BD_NITRO_PACK_EXPLODING_3, -1, -1
7352 O_NITRO_EXPL_3, FALSE,
7353 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
7356 O_NITRO_EXPL_4, TRUE,
7357 EL_BD_NITRO_PACK_EXPLODING_4, -1, -1
7360 O_NITRO_EXPL_4, FALSE,
7361 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
7364 O_NITRO_PACK_EXPLODE, TRUE,
7365 EL_BD_NITRO_PACK_EXPLODING, -1, -1
7368 O_NITRO_PACK_EXPLODE, FALSE,
7369 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
7372 O_AMOEBA_2_EXPL_1, TRUE,
7373 EL_BD_AMOEBA_2_EXPLODING_1, -1, -1
7376 O_AMOEBA_2_EXPL_1, FALSE,
7377 EL_BD_AMOEBA_2, ACTION_EXPLODING, -1
7380 O_AMOEBA_2_EXPL_2, TRUE,
7381 EL_BD_AMOEBA_2_EXPLODING_2, -1, -1
7384 O_AMOEBA_2_EXPL_2, FALSE,
7385 EL_BD_AMOEBA_2, ACTION_EXPLODING, -1
7388 O_AMOEBA_2_EXPL_3, TRUE,
7389 EL_BD_AMOEBA_2_EXPLODING_3, -1, -1
7392 O_AMOEBA_2_EXPL_3, FALSE,
7393 EL_BD_AMOEBA_2, ACTION_EXPLODING, -1
7396 O_AMOEBA_2_EXPL_4, TRUE,
7397 EL_BD_AMOEBA_2_EXPLODING_4, -1, -1
7400 O_AMOEBA_2_EXPL_4, FALSE,
7401 EL_BD_AMOEBA_2, ACTION_EXPLODING, -1
7405 EL_BD_NUT_BREAKING_1, -1, -1
7408 O_NUT_EXPL_1, FALSE,
7409 EL_BD_NUT, ACTION_BREAKING, -1
7413 EL_BD_NUT_BREAKING_2, -1, -1
7416 O_NUT_EXPL_2, FALSE,
7417 EL_BD_NUT, ACTION_BREAKING, -1
7421 EL_BD_NUT_BREAKING_3, -1, -1
7424 O_NUT_EXPL_3, FALSE,
7425 EL_BD_NUT, ACTION_BREAKING, -1
7429 EL_BD_NUT_BREAKING_4, -1, -1
7432 O_NUT_EXPL_4, FALSE,
7433 EL_BD_NUT, ACTION_BREAKING, -1
7436 O_PLAYER_PNEUMATIC_LEFT, FALSE,
7437 EL_BD_PLAYER, ACTION_HITTING, MV_BIT_LEFT
7440 O_PLAYER_PNEUMATIC_RIGHT, FALSE,
7441 EL_BD_PLAYER, ACTION_HITTING, MV_BIT_RIGHT
7444 O_PNEUMATIC_ACTIVE_LEFT, FALSE,
7445 EL_BD_PNEUMATIC_HAMMER, ACTION_HITTING, MV_BIT_LEFT
7448 O_PNEUMATIC_ACTIVE_RIGHT, FALSE,
7449 EL_BD_PNEUMATIC_HAMMER, ACTION_HITTING, MV_BIT_RIGHT
7452 // helper (runtime) elements
7455 O_FAKE_BONUS, FALSE,
7456 EL_BD_FAKE_BONUS, -1, -1
7459 O_INBOX_CLOSED, FALSE,
7463 O_INBOX_OPEN, FALSE,
7464 EL_BD_INBOX, ACTION_OPENING, -1
7467 O_OUTBOX_CLOSED, FALSE,
7468 EL_BD_EXIT_CLOSED, -1, -1
7471 O_OUTBOX_OPEN, FALSE,
7472 EL_BD_EXIT_OPEN, -1, -1
7476 EL_BD_COVERED, -1, -1
7479 O_PLAYER_LEFT, FALSE,
7480 EL_BD_PLAYER, ACTION_MOVING, MV_BIT_LEFT
7483 O_PLAYER_RIGHT, FALSE,
7484 EL_BD_PLAYER, ACTION_MOVING, MV_BIT_RIGHT
7488 EL_BD_PLAYER, ACTION_MOVING, MV_BIT_UP
7491 O_PLAYER_DOWN, FALSE,
7492 EL_BD_PLAYER, ACTION_MOVING, MV_BIT_DOWN
7495 O_PLAYER_BLINK, FALSE,
7496 EL_BD_PLAYER, ACTION_BORING_1, -1
7499 O_PLAYER_TAP, FALSE,
7500 EL_BD_PLAYER, ACTION_BORING_2, -1
7503 O_PLAYER_TAP_BLINK, FALSE,
7504 EL_BD_PLAYER, ACTION_BORING_3, -1
7507 O_PLAYER_PUSH_LEFT, FALSE,
7508 EL_BD_PLAYER, ACTION_PUSHING, MV_BIT_LEFT
7511 O_PLAYER_PUSH_RIGHT, FALSE,
7512 EL_BD_PLAYER, ACTION_PUSHING, MV_BIT_RIGHT
7515 O_CREATURE_SWITCH_ON, FALSE,
7516 EL_BD_CREATURE_SWITCH_ACTIVE, -1, -1
7519 O_EXPANDING_WALL_SWITCH_HORIZ, FALSE,
7520 EL_BD_EXPANDABLE_WALL_SWITCH, -1, -1
7523 O_EXPANDING_WALL_SWITCH_VERT, FALSE,
7524 EL_BD_EXPANDABLE_WALL_SWITCH_ACTIVE, -1, -1
7527 O_GRAVITY_SWITCH_ACTIVE, FALSE,
7528 EL_BD_GRAVITY_SWITCH_ACTIVE, -1, -1
7531 O_REPLICATOR_SWITCH_OFF, FALSE,
7532 EL_BD_REPLICATOR_SWITCH, -1, -1
7535 O_REPLICATOR_SWITCH_ON, FALSE,
7536 EL_BD_REPLICATOR_SWITCH_ACTIVE, -1, -1
7539 O_CONVEYOR_DIR_NORMAL, FALSE,
7540 EL_BD_CONVEYOR_DIR_SWITCH, -1, -1
7543 O_CONVEYOR_DIR_CHANGED, FALSE,
7544 EL_BD_CONVEYOR_DIR_SWITCH_ACTIVE, -1, -1
7547 O_CONVEYOR_SWITCH_OFF, FALSE,
7548 EL_BD_CONVEYOR_SWITCH, -1, -1
7551 O_CONVEYOR_SWITCH_ON, FALSE,
7552 EL_BD_CONVEYOR_SWITCH_ACTIVE, -1, -1
7555 O_MAGIC_WALL_ACTIVE, FALSE,
7556 EL_BD_MAGIC_WALL_ACTIVE, -1, -1
7559 O_REPLICATOR_ACTIVE, FALSE,
7560 EL_BD_REPLICATOR_ACTIVE, -1, -1
7563 O_CONVEYOR_LEFT_ACTIVE, FALSE,
7564 EL_BD_CONVEYOR_LEFT_ACTIVE, -1, -1
7567 O_CONVEYOR_RIGHT_ACTIVE, FALSE,
7568 EL_BD_CONVEYOR_RIGHT_ACTIVE, -1, -1
7571 O_BITER_SWITCH_1, FALSE,
7572 EL_BD_BITER_SWITCH_1, -1, -1
7575 O_BITER_SWITCH_2, FALSE,
7576 EL_BD_BITER_SWITCH_2, -1, -1
7579 O_BITER_SWITCH_3, FALSE,
7580 EL_BD_BITER_SWITCH_3, -1, -1
7583 O_BITER_SWITCH_4, FALSE,
7584 EL_BD_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 if (element_bd < 0 || element_bd >= O_MAX_ALL)
7691 Warn("invalid BD element %d", element_bd);
7696 return mapping_BD_to_RND[element_bd];
7699 int map_element_BD_to_RND_game(int element_bd)
7701 static unsigned short mapping_BD_to_RND[O_MAX_ALL];
7702 static boolean mapping_initialized = FALSE;
7704 if (!mapping_initialized)
7708 // return "EL_UNKNOWN" for all undefined elements in mapping array
7709 for (i = 0; i < O_MAX_ALL; i++)
7710 mapping_BD_to_RND[i] = EL_UNKNOWN;
7712 for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
7713 mapping_BD_to_RND[bd_object_mapping_list[i].element_bd] =
7714 bd_object_mapping_list[i].element_rnd;
7716 mapping_initialized = TRUE;
7719 if (element_bd < 0 || element_bd >= O_MAX_ALL)
7721 Warn("invalid BD element %d", element_bd);
7726 return mapping_BD_to_RND[element_bd];
7729 static struct Mapping_EM_to_RND_object
7732 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
7733 boolean is_backside; // backside of moving element
7739 em_object_mapping_list[GAME_TILE_MAX + 1] =
7742 Zborder, FALSE, FALSE,
7746 Zplayer, FALSE, FALSE,
7755 Ztank, FALSE, FALSE,
7759 Zeater, FALSE, FALSE,
7763 Zdynamite, FALSE, FALSE,
7767 Zboom, FALSE, FALSE,
7772 Xchain, FALSE, FALSE,
7773 EL_DEFAULT, ACTION_EXPLODING, -1
7776 Xboom_bug, FALSE, FALSE,
7777 EL_BUG, ACTION_EXPLODING, -1
7780 Xboom_tank, FALSE, FALSE,
7781 EL_SPACESHIP, ACTION_EXPLODING, -1
7784 Xboom_android, FALSE, FALSE,
7785 EL_EMC_ANDROID, ACTION_OTHER, -1
7788 Xboom_1, FALSE, FALSE,
7789 EL_DEFAULT, ACTION_EXPLODING, -1
7792 Xboom_2, FALSE, FALSE,
7793 EL_DEFAULT, ACTION_EXPLODING, -1
7797 Xblank, TRUE, FALSE,
7802 Xsplash_e, FALSE, FALSE,
7803 EL_ACID_SPLASH_RIGHT, -1, -1
7806 Xsplash_w, FALSE, FALSE,
7807 EL_ACID_SPLASH_LEFT, -1, -1
7811 Xplant, TRUE, FALSE,
7812 EL_EMC_PLANT, -1, -1
7815 Yplant, FALSE, FALSE,
7816 EL_EMC_PLANT, -1, -1
7820 Xacid_1, TRUE, FALSE,
7824 Xacid_2, FALSE, FALSE,
7828 Xacid_3, FALSE, FALSE,
7832 Xacid_4, FALSE, FALSE,
7836 Xacid_5, FALSE, FALSE,
7840 Xacid_6, FALSE, FALSE,
7844 Xacid_7, FALSE, FALSE,
7848 Xacid_8, FALSE, FALSE,
7853 Xfake_acid_1, TRUE, FALSE,
7854 EL_EMC_FAKE_ACID, -1, -1
7857 Xfake_acid_2, FALSE, FALSE,
7858 EL_EMC_FAKE_ACID, -1, -1
7861 Xfake_acid_3, FALSE, FALSE,
7862 EL_EMC_FAKE_ACID, -1, -1
7865 Xfake_acid_4, FALSE, FALSE,
7866 EL_EMC_FAKE_ACID, -1, -1
7869 Xfake_acid_5, FALSE, FALSE,
7870 EL_EMC_FAKE_ACID, -1, -1
7873 Xfake_acid_6, FALSE, FALSE,
7874 EL_EMC_FAKE_ACID, -1, -1
7877 Xfake_acid_7, FALSE, FALSE,
7878 EL_EMC_FAKE_ACID, -1, -1
7881 Xfake_acid_8, FALSE, FALSE,
7882 EL_EMC_FAKE_ACID, -1, -1
7886 Xfake_acid_1_player, FALSE, FALSE,
7887 EL_EMC_FAKE_ACID, -1, -1
7890 Xfake_acid_2_player, FALSE, FALSE,
7891 EL_EMC_FAKE_ACID, -1, -1
7894 Xfake_acid_3_player, FALSE, FALSE,
7895 EL_EMC_FAKE_ACID, -1, -1
7898 Xfake_acid_4_player, FALSE, FALSE,
7899 EL_EMC_FAKE_ACID, -1, -1
7902 Xfake_acid_5_player, FALSE, FALSE,
7903 EL_EMC_FAKE_ACID, -1, -1
7906 Xfake_acid_6_player, FALSE, FALSE,
7907 EL_EMC_FAKE_ACID, -1, -1
7910 Xfake_acid_7_player, FALSE, FALSE,
7911 EL_EMC_FAKE_ACID, -1, -1
7914 Xfake_acid_8_player, FALSE, FALSE,
7915 EL_EMC_FAKE_ACID, -1, -1
7919 Xgrass, TRUE, FALSE,
7920 EL_EMC_GRASS, -1, -1
7923 Ygrass_nB, FALSE, FALSE,
7924 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
7927 Ygrass_eB, FALSE, FALSE,
7928 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
7931 Ygrass_sB, FALSE, FALSE,
7932 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
7935 Ygrass_wB, FALSE, FALSE,
7936 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
7944 Ydirt_nB, FALSE, FALSE,
7945 EL_SAND, ACTION_DIGGING, MV_BIT_UP
7948 Ydirt_eB, FALSE, FALSE,
7949 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
7952 Ydirt_sB, FALSE, FALSE,
7953 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
7956 Ydirt_wB, FALSE, FALSE,
7957 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
7961 Xandroid, TRUE, FALSE,
7962 EL_EMC_ANDROID, ACTION_ACTIVE, -1
7965 Xandroid_1_n, FALSE, FALSE,
7966 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
7969 Xandroid_2_n, FALSE, FALSE,
7970 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
7973 Xandroid_1_e, FALSE, FALSE,
7974 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
7977 Xandroid_2_e, FALSE, FALSE,
7978 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
7981 Xandroid_1_w, FALSE, FALSE,
7982 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
7985 Xandroid_2_w, FALSE, FALSE,
7986 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
7989 Xandroid_1_s, FALSE, FALSE,
7990 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
7993 Xandroid_2_s, FALSE, FALSE,
7994 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
7997 Yandroid_n, FALSE, FALSE,
7998 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
8001 Yandroid_nB, FALSE, TRUE,
8002 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
8005 Yandroid_ne, FALSE, FALSE,
8006 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
8009 Yandroid_neB, FALSE, TRUE,
8010 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
8013 Yandroid_e, FALSE, FALSE,
8014 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
8017 Yandroid_eB, FALSE, TRUE,
8018 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
8021 Yandroid_se, FALSE, FALSE,
8022 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
8025 Yandroid_seB, FALSE, TRUE,
8026 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
8029 Yandroid_s, FALSE, FALSE,
8030 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
8033 Yandroid_sB, FALSE, TRUE,
8034 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
8037 Yandroid_sw, FALSE, FALSE,
8038 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
8041 Yandroid_swB, FALSE, TRUE,
8042 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
8045 Yandroid_w, FALSE, FALSE,
8046 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
8049 Yandroid_wB, FALSE, TRUE,
8050 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
8053 Yandroid_nw, FALSE, FALSE,
8054 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
8057 Yandroid_nwB, FALSE, TRUE,
8058 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
8062 Xeater_n, TRUE, FALSE,
8063 EL_YAMYAM_UP, -1, -1
8066 Xeater_e, TRUE, FALSE,
8067 EL_YAMYAM_RIGHT, -1, -1
8070 Xeater_w, TRUE, FALSE,
8071 EL_YAMYAM_LEFT, -1, -1
8074 Xeater_s, TRUE, FALSE,
8075 EL_YAMYAM_DOWN, -1, -1
8078 Yeater_n, FALSE, FALSE,
8079 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
8082 Yeater_nB, FALSE, TRUE,
8083 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
8086 Yeater_e, FALSE, FALSE,
8087 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
8090 Yeater_eB, FALSE, TRUE,
8091 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
8094 Yeater_s, FALSE, FALSE,
8095 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
8098 Yeater_sB, FALSE, TRUE,
8099 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
8102 Yeater_w, FALSE, FALSE,
8103 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
8106 Yeater_wB, FALSE, TRUE,
8107 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
8110 Yeater_stone, FALSE, FALSE,
8111 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
8114 Yeater_spring, FALSE, FALSE,
8115 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
8119 Xalien, TRUE, FALSE,
8123 Xalien_pause, FALSE, FALSE,
8127 Yalien_n, FALSE, FALSE,
8128 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
8131 Yalien_nB, FALSE, TRUE,
8132 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
8135 Yalien_e, FALSE, FALSE,
8136 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
8139 Yalien_eB, FALSE, TRUE,
8140 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
8143 Yalien_s, FALSE, FALSE,
8144 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
8147 Yalien_sB, FALSE, TRUE,
8148 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
8151 Yalien_w, FALSE, FALSE,
8152 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
8155 Yalien_wB, FALSE, TRUE,
8156 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
8159 Yalien_stone, FALSE, FALSE,
8160 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
8163 Yalien_spring, FALSE, FALSE,
8164 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
8168 Xbug_1_n, TRUE, FALSE,
8172 Xbug_1_e, TRUE, FALSE,
8173 EL_BUG_RIGHT, -1, -1
8176 Xbug_1_s, TRUE, FALSE,
8180 Xbug_1_w, TRUE, FALSE,
8184 Xbug_2_n, FALSE, FALSE,
8188 Xbug_2_e, FALSE, FALSE,
8189 EL_BUG_RIGHT, -1, -1
8192 Xbug_2_s, FALSE, FALSE,
8196 Xbug_2_w, FALSE, FALSE,
8200 Ybug_n, FALSE, FALSE,
8201 EL_BUG, ACTION_MOVING, MV_BIT_UP
8204 Ybug_nB, FALSE, TRUE,
8205 EL_BUG, ACTION_MOVING, MV_BIT_UP
8208 Ybug_e, FALSE, FALSE,
8209 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
8212 Ybug_eB, FALSE, TRUE,
8213 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
8216 Ybug_s, FALSE, FALSE,
8217 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
8220 Ybug_sB, FALSE, TRUE,
8221 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
8224 Ybug_w, FALSE, FALSE,
8225 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
8228 Ybug_wB, FALSE, TRUE,
8229 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
8232 Ybug_w_n, FALSE, FALSE,
8233 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
8236 Ybug_n_e, FALSE, FALSE,
8237 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
8240 Ybug_e_s, FALSE, FALSE,
8241 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
8244 Ybug_s_w, FALSE, FALSE,
8245 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
8248 Ybug_e_n, FALSE, FALSE,
8249 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
8252 Ybug_s_e, FALSE, FALSE,
8253 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
8256 Ybug_w_s, FALSE, FALSE,
8257 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
8260 Ybug_n_w, FALSE, FALSE,
8261 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
8264 Ybug_stone, FALSE, FALSE,
8265 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
8268 Ybug_spring, FALSE, FALSE,
8269 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
8273 Xtank_1_n, TRUE, FALSE,
8274 EL_SPACESHIP_UP, -1, -1
8277 Xtank_1_e, TRUE, FALSE,
8278 EL_SPACESHIP_RIGHT, -1, -1
8281 Xtank_1_s, TRUE, FALSE,
8282 EL_SPACESHIP_DOWN, -1, -1
8285 Xtank_1_w, TRUE, FALSE,
8286 EL_SPACESHIP_LEFT, -1, -1
8289 Xtank_2_n, FALSE, FALSE,
8290 EL_SPACESHIP_UP, -1, -1
8293 Xtank_2_e, FALSE, FALSE,
8294 EL_SPACESHIP_RIGHT, -1, -1
8297 Xtank_2_s, FALSE, FALSE,
8298 EL_SPACESHIP_DOWN, -1, -1
8301 Xtank_2_w, FALSE, FALSE,
8302 EL_SPACESHIP_LEFT, -1, -1
8305 Ytank_n, FALSE, FALSE,
8306 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
8309 Ytank_nB, FALSE, TRUE,
8310 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
8313 Ytank_e, FALSE, FALSE,
8314 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
8317 Ytank_eB, FALSE, TRUE,
8318 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
8321 Ytank_s, FALSE, FALSE,
8322 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
8325 Ytank_sB, FALSE, TRUE,
8326 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
8329 Ytank_w, FALSE, FALSE,
8330 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
8333 Ytank_wB, FALSE, TRUE,
8334 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
8337 Ytank_w_n, FALSE, FALSE,
8338 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
8341 Ytank_n_e, FALSE, FALSE,
8342 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
8345 Ytank_e_s, FALSE, FALSE,
8346 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
8349 Ytank_s_w, FALSE, FALSE,
8350 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
8353 Ytank_e_n, FALSE, FALSE,
8354 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
8357 Ytank_s_e, FALSE, FALSE,
8358 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
8361 Ytank_w_s, FALSE, FALSE,
8362 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
8365 Ytank_n_w, FALSE, FALSE,
8366 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
8369 Ytank_stone, FALSE, FALSE,
8370 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
8373 Ytank_spring, FALSE, FALSE,
8374 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
8378 Xemerald, TRUE, FALSE,
8382 Xemerald_pause, FALSE, FALSE,
8386 Xemerald_fall, FALSE, FALSE,
8390 Xemerald_shine, FALSE, FALSE,
8391 EL_EMERALD, ACTION_TWINKLING, -1
8394 Yemerald_s, FALSE, FALSE,
8395 EL_EMERALD, ACTION_FALLING, -1
8398 Yemerald_sB, FALSE, TRUE,
8399 EL_EMERALD, ACTION_FALLING, -1
8402 Yemerald_e, FALSE, FALSE,
8403 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
8406 Yemerald_eB, FALSE, TRUE,
8407 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
8410 Yemerald_w, FALSE, FALSE,
8411 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
8414 Yemerald_wB, FALSE, TRUE,
8415 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
8418 Yemerald_blank, FALSE, FALSE,
8419 EL_EMERALD, ACTION_COLLECTING, -1
8423 Xdiamond, TRUE, FALSE,
8427 Xdiamond_pause, FALSE, FALSE,
8431 Xdiamond_fall, FALSE, FALSE,
8435 Xdiamond_shine, FALSE, FALSE,
8436 EL_DIAMOND, ACTION_TWINKLING, -1
8439 Ydiamond_s, FALSE, FALSE,
8440 EL_DIAMOND, ACTION_FALLING, -1
8443 Ydiamond_sB, FALSE, TRUE,
8444 EL_DIAMOND, ACTION_FALLING, -1
8447 Ydiamond_e, FALSE, FALSE,
8448 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
8451 Ydiamond_eB, FALSE, TRUE,
8452 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
8455 Ydiamond_w, FALSE, FALSE,
8456 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
8459 Ydiamond_wB, FALSE, TRUE,
8460 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
8463 Ydiamond_blank, FALSE, FALSE,
8464 EL_DIAMOND, ACTION_COLLECTING, -1
8467 Ydiamond_stone, FALSE, FALSE,
8468 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
8472 Xstone, TRUE, FALSE,
8476 Xstone_pause, FALSE, FALSE,
8480 Xstone_fall, FALSE, FALSE,
8484 Ystone_s, FALSE, FALSE,
8485 EL_ROCK, ACTION_FALLING, -1
8488 Ystone_sB, FALSE, TRUE,
8489 EL_ROCK, ACTION_FALLING, -1
8492 Ystone_e, FALSE, FALSE,
8493 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
8496 Ystone_eB, FALSE, TRUE,
8497 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
8500 Ystone_w, FALSE, FALSE,
8501 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
8504 Ystone_wB, FALSE, TRUE,
8505 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
8513 Xbomb_pause, FALSE, FALSE,
8517 Xbomb_fall, FALSE, FALSE,
8521 Ybomb_s, FALSE, FALSE,
8522 EL_BOMB, ACTION_FALLING, -1
8525 Ybomb_sB, FALSE, TRUE,
8526 EL_BOMB, ACTION_FALLING, -1
8529 Ybomb_e, FALSE, FALSE,
8530 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
8533 Ybomb_eB, FALSE, TRUE,
8534 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
8537 Ybomb_w, FALSE, FALSE,
8538 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
8541 Ybomb_wB, FALSE, TRUE,
8542 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
8545 Ybomb_blank, FALSE, FALSE,
8546 EL_BOMB, ACTION_ACTIVATING, -1
8554 Xnut_pause, FALSE, FALSE,
8558 Xnut_fall, FALSE, FALSE,
8562 Ynut_s, FALSE, FALSE,
8563 EL_NUT, ACTION_FALLING, -1
8566 Ynut_sB, FALSE, TRUE,
8567 EL_NUT, ACTION_FALLING, -1
8570 Ynut_e, FALSE, FALSE,
8571 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
8574 Ynut_eB, FALSE, TRUE,
8575 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
8578 Ynut_w, FALSE, FALSE,
8579 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
8582 Ynut_wB, FALSE, TRUE,
8583 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
8586 Ynut_stone, FALSE, FALSE,
8587 EL_NUT, ACTION_BREAKING, -1
8591 Xspring, TRUE, FALSE,
8595 Xspring_pause, FALSE, FALSE,
8599 Xspring_e, TRUE, FALSE,
8600 EL_SPRING_RIGHT, -1, -1
8603 Xspring_w, TRUE, FALSE,
8604 EL_SPRING_LEFT, -1, -1
8607 Xspring_fall, FALSE, FALSE,
8611 Yspring_s, FALSE, FALSE,
8612 EL_SPRING, ACTION_FALLING, -1
8615 Yspring_sB, FALSE, TRUE,
8616 EL_SPRING, ACTION_FALLING, -1
8619 Yspring_e, FALSE, FALSE,
8620 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
8623 Yspring_eB, FALSE, TRUE,
8624 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
8627 Yspring_w, FALSE, FALSE,
8628 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
8631 Yspring_wB, FALSE, TRUE,
8632 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
8635 Yspring_alien_e, FALSE, FALSE,
8636 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
8639 Yspring_alien_eB, FALSE, TRUE,
8640 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
8643 Yspring_alien_w, FALSE, FALSE,
8644 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
8647 Yspring_alien_wB, FALSE, TRUE,
8648 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
8652 Xpush_emerald_e, FALSE, FALSE,
8653 EL_EMERALD, -1, MV_BIT_RIGHT
8656 Xpush_emerald_w, FALSE, FALSE,
8657 EL_EMERALD, -1, MV_BIT_LEFT
8660 Xpush_diamond_e, FALSE, FALSE,
8661 EL_DIAMOND, -1, MV_BIT_RIGHT
8664 Xpush_diamond_w, FALSE, FALSE,
8665 EL_DIAMOND, -1, MV_BIT_LEFT
8668 Xpush_stone_e, FALSE, FALSE,
8669 EL_ROCK, -1, MV_BIT_RIGHT
8672 Xpush_stone_w, FALSE, FALSE,
8673 EL_ROCK, -1, MV_BIT_LEFT
8676 Xpush_bomb_e, FALSE, FALSE,
8677 EL_BOMB, -1, MV_BIT_RIGHT
8680 Xpush_bomb_w, FALSE, FALSE,
8681 EL_BOMB, -1, MV_BIT_LEFT
8684 Xpush_nut_e, FALSE, FALSE,
8685 EL_NUT, -1, MV_BIT_RIGHT
8688 Xpush_nut_w, FALSE, FALSE,
8689 EL_NUT, -1, MV_BIT_LEFT
8692 Xpush_spring_e, FALSE, FALSE,
8693 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
8696 Xpush_spring_w, FALSE, FALSE,
8697 EL_SPRING_LEFT, -1, MV_BIT_LEFT
8701 Xdynamite, TRUE, FALSE,
8702 EL_EM_DYNAMITE, -1, -1
8705 Ydynamite_blank, FALSE, FALSE,
8706 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
8709 Xdynamite_1, TRUE, FALSE,
8710 EL_EM_DYNAMITE_ACTIVE, -1, -1
8713 Xdynamite_2, FALSE, FALSE,
8714 EL_EM_DYNAMITE_ACTIVE, -1, -1
8717 Xdynamite_3, FALSE, FALSE,
8718 EL_EM_DYNAMITE_ACTIVE, -1, -1
8721 Xdynamite_4, FALSE, FALSE,
8722 EL_EM_DYNAMITE_ACTIVE, -1, -1
8726 Xkey_1, TRUE, FALSE,
8730 Xkey_2, TRUE, FALSE,
8734 Xkey_3, TRUE, FALSE,
8738 Xkey_4, TRUE, FALSE,
8742 Xkey_5, TRUE, FALSE,
8743 EL_EMC_KEY_5, -1, -1
8746 Xkey_6, TRUE, FALSE,
8747 EL_EMC_KEY_6, -1, -1
8750 Xkey_7, TRUE, FALSE,
8751 EL_EMC_KEY_7, -1, -1
8754 Xkey_8, TRUE, FALSE,
8755 EL_EMC_KEY_8, -1, -1
8759 Xdoor_1, TRUE, FALSE,
8760 EL_EM_GATE_1, -1, -1
8763 Xdoor_2, TRUE, FALSE,
8764 EL_EM_GATE_2, -1, -1
8767 Xdoor_3, TRUE, FALSE,
8768 EL_EM_GATE_3, -1, -1
8771 Xdoor_4, TRUE, FALSE,
8772 EL_EM_GATE_4, -1, -1
8775 Xdoor_5, TRUE, FALSE,
8776 EL_EMC_GATE_5, -1, -1
8779 Xdoor_6, TRUE, FALSE,
8780 EL_EMC_GATE_6, -1, -1
8783 Xdoor_7, TRUE, FALSE,
8784 EL_EMC_GATE_7, -1, -1
8787 Xdoor_8, TRUE, FALSE,
8788 EL_EMC_GATE_8, -1, -1
8792 Xfake_door_1, TRUE, FALSE,
8793 EL_EM_GATE_1_GRAY, -1, -1
8796 Xfake_door_2, TRUE, FALSE,
8797 EL_EM_GATE_2_GRAY, -1, -1
8800 Xfake_door_3, TRUE, FALSE,
8801 EL_EM_GATE_3_GRAY, -1, -1
8804 Xfake_door_4, TRUE, FALSE,
8805 EL_EM_GATE_4_GRAY, -1, -1
8808 Xfake_door_5, TRUE, FALSE,
8809 EL_EMC_GATE_5_GRAY, -1, -1
8812 Xfake_door_6, TRUE, FALSE,
8813 EL_EMC_GATE_6_GRAY, -1, -1
8816 Xfake_door_7, TRUE, FALSE,
8817 EL_EMC_GATE_7_GRAY, -1, -1
8820 Xfake_door_8, TRUE, FALSE,
8821 EL_EMC_GATE_8_GRAY, -1, -1
8825 Xballoon, TRUE, FALSE,
8829 Yballoon_n, FALSE, FALSE,
8830 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
8833 Yballoon_nB, FALSE, TRUE,
8834 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
8837 Yballoon_e, FALSE, FALSE,
8838 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
8841 Yballoon_eB, FALSE, TRUE,
8842 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
8845 Yballoon_s, FALSE, FALSE,
8846 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
8849 Yballoon_sB, FALSE, TRUE,
8850 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
8853 Yballoon_w, FALSE, FALSE,
8854 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
8857 Yballoon_wB, FALSE, TRUE,
8858 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
8862 Xball_1, TRUE, FALSE,
8863 EL_EMC_MAGIC_BALL, -1, -1
8866 Yball_1, FALSE, FALSE,
8867 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
8870 Xball_2, FALSE, FALSE,
8871 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
8874 Yball_2, FALSE, FALSE,
8875 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
8878 Yball_blank, FALSE, FALSE,
8879 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
8883 Xamoeba_1, TRUE, FALSE,
8884 EL_AMOEBA_DRY, ACTION_OTHER, -1
8887 Xamoeba_2, FALSE, FALSE,
8888 EL_AMOEBA_DRY, ACTION_OTHER, -1
8891 Xamoeba_3, FALSE, FALSE,
8892 EL_AMOEBA_DRY, ACTION_OTHER, -1
8895 Xamoeba_4, FALSE, FALSE,
8896 EL_AMOEBA_DRY, ACTION_OTHER, -1
8899 Xamoeba_5, TRUE, FALSE,
8900 EL_AMOEBA_WET, ACTION_OTHER, -1
8903 Xamoeba_6, FALSE, FALSE,
8904 EL_AMOEBA_WET, ACTION_OTHER, -1
8907 Xamoeba_7, FALSE, FALSE,
8908 EL_AMOEBA_WET, ACTION_OTHER, -1
8911 Xamoeba_8, FALSE, FALSE,
8912 EL_AMOEBA_WET, ACTION_OTHER, -1
8917 EL_AMOEBA_DROP, ACTION_GROWING, -1
8920 Xdrip_fall, FALSE, FALSE,
8921 EL_AMOEBA_DROP, -1, -1
8924 Xdrip_stretch, FALSE, FALSE,
8925 EL_AMOEBA_DROP, ACTION_FALLING, -1
8928 Xdrip_stretchB, FALSE, TRUE,
8929 EL_AMOEBA_DROP, ACTION_FALLING, -1
8932 Ydrip_1_s, FALSE, FALSE,
8933 EL_AMOEBA_DROP, ACTION_FALLING, -1
8936 Ydrip_1_sB, FALSE, TRUE,
8937 EL_AMOEBA_DROP, ACTION_FALLING, -1
8940 Ydrip_2_s, FALSE, FALSE,
8941 EL_AMOEBA_DROP, ACTION_FALLING, -1
8944 Ydrip_2_sB, FALSE, TRUE,
8945 EL_AMOEBA_DROP, ACTION_FALLING, -1
8949 Xwonderwall, TRUE, FALSE,
8950 EL_MAGIC_WALL, -1, -1
8953 Ywonderwall, FALSE, FALSE,
8954 EL_MAGIC_WALL, ACTION_ACTIVE, -1
8958 Xwheel, TRUE, FALSE,
8959 EL_ROBOT_WHEEL, -1, -1
8962 Ywheel, FALSE, FALSE,
8963 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
8967 Xswitch, TRUE, FALSE,
8968 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
8971 Yswitch, FALSE, FALSE,
8972 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
8976 Xbumper, TRUE, FALSE,
8977 EL_EMC_SPRING_BUMPER, -1, -1
8980 Ybumper, FALSE, FALSE,
8981 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
8985 Xacid_nw, TRUE, FALSE,
8986 EL_ACID_POOL_TOPLEFT, -1, -1
8989 Xacid_ne, TRUE, FALSE,
8990 EL_ACID_POOL_TOPRIGHT, -1, -1
8993 Xacid_sw, TRUE, FALSE,
8994 EL_ACID_POOL_BOTTOMLEFT, -1, -1
8997 Xacid_s, TRUE, FALSE,
8998 EL_ACID_POOL_BOTTOM, -1, -1
9001 Xacid_se, TRUE, FALSE,
9002 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
9006 Xfake_blank, TRUE, FALSE,
9007 EL_INVISIBLE_WALL, -1, -1
9010 Yfake_blank, FALSE, FALSE,
9011 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
9015 Xfake_grass, TRUE, FALSE,
9016 EL_EMC_FAKE_GRASS, -1, -1
9019 Yfake_grass, FALSE, FALSE,
9020 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
9024 Xfake_amoeba, TRUE, FALSE,
9025 EL_EMC_DRIPPER, -1, -1
9028 Yfake_amoeba, FALSE, FALSE,
9029 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
9033 Xlenses, TRUE, FALSE,
9034 EL_EMC_LENSES, -1, -1
9038 Xmagnify, TRUE, FALSE,
9039 EL_EMC_MAGNIFIER, -1, -1
9044 EL_QUICKSAND_EMPTY, -1, -1
9047 Xsand_stone, TRUE, FALSE,
9048 EL_QUICKSAND_FULL, -1, -1
9051 Xsand_stonein_1, FALSE, TRUE,
9052 EL_ROCK, ACTION_FILLING, -1
9055 Xsand_stonein_2, FALSE, TRUE,
9056 EL_ROCK, ACTION_FILLING, -1
9059 Xsand_stonein_3, FALSE, TRUE,
9060 EL_ROCK, ACTION_FILLING, -1
9063 Xsand_stonein_4, FALSE, TRUE,
9064 EL_ROCK, ACTION_FILLING, -1
9067 Xsand_sandstone_1, FALSE, FALSE,
9068 EL_QUICKSAND_FILLING, -1, -1
9071 Xsand_sandstone_2, FALSE, FALSE,
9072 EL_QUICKSAND_FILLING, -1, -1
9075 Xsand_sandstone_3, FALSE, FALSE,
9076 EL_QUICKSAND_FILLING, -1, -1
9079 Xsand_sandstone_4, FALSE, FALSE,
9080 EL_QUICKSAND_FILLING, -1, -1
9083 Xsand_stonesand_1, FALSE, FALSE,
9084 EL_QUICKSAND_EMPTYING, -1, -1
9087 Xsand_stonesand_2, FALSE, FALSE,
9088 EL_QUICKSAND_EMPTYING, -1, -1
9091 Xsand_stonesand_3, FALSE, FALSE,
9092 EL_QUICKSAND_EMPTYING, -1, -1
9095 Xsand_stonesand_4, FALSE, FALSE,
9096 EL_QUICKSAND_EMPTYING, -1, -1
9099 Xsand_stoneout_1, FALSE, FALSE,
9100 EL_ROCK, ACTION_EMPTYING, -1
9103 Xsand_stoneout_2, FALSE, FALSE,
9104 EL_ROCK, ACTION_EMPTYING, -1
9107 Xsand_stonesand_quickout_1, FALSE, FALSE,
9108 EL_QUICKSAND_EMPTYING, -1, -1
9111 Xsand_stonesand_quickout_2, FALSE, FALSE,
9112 EL_QUICKSAND_EMPTYING, -1, -1
9116 Xslide_ns, TRUE, FALSE,
9117 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
9120 Yslide_ns_blank, FALSE, FALSE,
9121 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
9124 Xslide_ew, TRUE, FALSE,
9125 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
9128 Yslide_ew_blank, FALSE, FALSE,
9129 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
9133 Xwind_n, TRUE, FALSE,
9134 EL_BALLOON_SWITCH_UP, -1, -1
9137 Xwind_e, TRUE, FALSE,
9138 EL_BALLOON_SWITCH_RIGHT, -1, -1
9141 Xwind_s, TRUE, FALSE,
9142 EL_BALLOON_SWITCH_DOWN, -1, -1
9145 Xwind_w, TRUE, FALSE,
9146 EL_BALLOON_SWITCH_LEFT, -1, -1
9149 Xwind_any, TRUE, FALSE,
9150 EL_BALLOON_SWITCH_ANY, -1, -1
9153 Xwind_stop, TRUE, FALSE,
9154 EL_BALLOON_SWITCH_NONE, -1, -1
9159 EL_EM_EXIT_CLOSED, -1, -1
9162 Xexit_1, TRUE, FALSE,
9163 EL_EM_EXIT_OPEN, -1, -1
9166 Xexit_2, FALSE, FALSE,
9167 EL_EM_EXIT_OPEN, -1, -1
9170 Xexit_3, FALSE, FALSE,
9171 EL_EM_EXIT_OPEN, -1, -1
9175 Xpause, FALSE, FALSE,
9180 Xwall_1, TRUE, FALSE,
9184 Xwall_2, TRUE, FALSE,
9185 EL_EMC_WALL_14, -1, -1
9188 Xwall_3, TRUE, FALSE,
9189 EL_EMC_WALL_15, -1, -1
9192 Xwall_4, TRUE, FALSE,
9193 EL_EMC_WALL_16, -1, -1
9197 Xroundwall_1, TRUE, FALSE,
9198 EL_WALL_SLIPPERY, -1, -1
9201 Xroundwall_2, TRUE, FALSE,
9202 EL_EMC_WALL_SLIPPERY_2, -1, -1
9205 Xroundwall_3, TRUE, FALSE,
9206 EL_EMC_WALL_SLIPPERY_3, -1, -1
9209 Xroundwall_4, TRUE, FALSE,
9210 EL_EMC_WALL_SLIPPERY_4, -1, -1
9214 Xsteel_1, TRUE, FALSE,
9215 EL_STEELWALL, -1, -1
9218 Xsteel_2, TRUE, FALSE,
9219 EL_EMC_STEELWALL_2, -1, -1
9222 Xsteel_3, TRUE, FALSE,
9223 EL_EMC_STEELWALL_3, -1, -1
9226 Xsteel_4, TRUE, FALSE,
9227 EL_EMC_STEELWALL_4, -1, -1
9231 Xdecor_1, TRUE, FALSE,
9232 EL_EMC_WALL_8, -1, -1
9235 Xdecor_2, TRUE, FALSE,
9236 EL_EMC_WALL_6, -1, -1
9239 Xdecor_3, TRUE, FALSE,
9240 EL_EMC_WALL_4, -1, -1
9243 Xdecor_4, TRUE, FALSE,
9244 EL_EMC_WALL_7, -1, -1
9247 Xdecor_5, TRUE, FALSE,
9248 EL_EMC_WALL_5, -1, -1
9251 Xdecor_6, TRUE, FALSE,
9252 EL_EMC_WALL_9, -1, -1
9255 Xdecor_7, TRUE, FALSE,
9256 EL_EMC_WALL_10, -1, -1
9259 Xdecor_8, TRUE, FALSE,
9260 EL_EMC_WALL_1, -1, -1
9263 Xdecor_9, TRUE, FALSE,
9264 EL_EMC_WALL_2, -1, -1
9267 Xdecor_10, TRUE, FALSE,
9268 EL_EMC_WALL_3, -1, -1
9271 Xdecor_11, TRUE, FALSE,
9272 EL_EMC_WALL_11, -1, -1
9275 Xdecor_12, TRUE, FALSE,
9276 EL_EMC_WALL_12, -1, -1
9280 Xalpha_0, TRUE, FALSE,
9281 EL_CHAR('0'), -1, -1
9284 Xalpha_1, TRUE, FALSE,
9285 EL_CHAR('1'), -1, -1
9288 Xalpha_2, TRUE, FALSE,
9289 EL_CHAR('2'), -1, -1
9292 Xalpha_3, TRUE, FALSE,
9293 EL_CHAR('3'), -1, -1
9296 Xalpha_4, TRUE, FALSE,
9297 EL_CHAR('4'), -1, -1
9300 Xalpha_5, TRUE, FALSE,
9301 EL_CHAR('5'), -1, -1
9304 Xalpha_6, TRUE, FALSE,
9305 EL_CHAR('6'), -1, -1
9308 Xalpha_7, TRUE, FALSE,
9309 EL_CHAR('7'), -1, -1
9312 Xalpha_8, TRUE, FALSE,
9313 EL_CHAR('8'), -1, -1
9316 Xalpha_9, TRUE, FALSE,
9317 EL_CHAR('9'), -1, -1
9320 Xalpha_excla, TRUE, FALSE,
9321 EL_CHAR('!'), -1, -1
9324 Xalpha_apost, TRUE, FALSE,
9325 EL_CHAR('\''), -1, -1
9328 Xalpha_comma, TRUE, FALSE,
9329 EL_CHAR(','), -1, -1
9332 Xalpha_minus, TRUE, FALSE,
9333 EL_CHAR('-'), -1, -1
9336 Xalpha_perio, TRUE, FALSE,
9337 EL_CHAR('.'), -1, -1
9340 Xalpha_colon, TRUE, FALSE,
9341 EL_CHAR(':'), -1, -1
9344 Xalpha_quest, TRUE, FALSE,
9345 EL_CHAR('?'), -1, -1
9348 Xalpha_a, TRUE, FALSE,
9349 EL_CHAR('A'), -1, -1
9352 Xalpha_b, TRUE, FALSE,
9353 EL_CHAR('B'), -1, -1
9356 Xalpha_c, TRUE, FALSE,
9357 EL_CHAR('C'), -1, -1
9360 Xalpha_d, TRUE, FALSE,
9361 EL_CHAR('D'), -1, -1
9364 Xalpha_e, TRUE, FALSE,
9365 EL_CHAR('E'), -1, -1
9368 Xalpha_f, TRUE, FALSE,
9369 EL_CHAR('F'), -1, -1
9372 Xalpha_g, TRUE, FALSE,
9373 EL_CHAR('G'), -1, -1
9376 Xalpha_h, TRUE, FALSE,
9377 EL_CHAR('H'), -1, -1
9380 Xalpha_i, TRUE, FALSE,
9381 EL_CHAR('I'), -1, -1
9384 Xalpha_j, TRUE, FALSE,
9385 EL_CHAR('J'), -1, -1
9388 Xalpha_k, TRUE, FALSE,
9389 EL_CHAR('K'), -1, -1
9392 Xalpha_l, TRUE, FALSE,
9393 EL_CHAR('L'), -1, -1
9396 Xalpha_m, TRUE, FALSE,
9397 EL_CHAR('M'), -1, -1
9400 Xalpha_n, TRUE, FALSE,
9401 EL_CHAR('N'), -1, -1
9404 Xalpha_o, TRUE, FALSE,
9405 EL_CHAR('O'), -1, -1
9408 Xalpha_p, TRUE, FALSE,
9409 EL_CHAR('P'), -1, -1
9412 Xalpha_q, TRUE, FALSE,
9413 EL_CHAR('Q'), -1, -1
9416 Xalpha_r, TRUE, FALSE,
9417 EL_CHAR('R'), -1, -1
9420 Xalpha_s, TRUE, FALSE,
9421 EL_CHAR('S'), -1, -1
9424 Xalpha_t, TRUE, FALSE,
9425 EL_CHAR('T'), -1, -1
9428 Xalpha_u, TRUE, FALSE,
9429 EL_CHAR('U'), -1, -1
9432 Xalpha_v, TRUE, FALSE,
9433 EL_CHAR('V'), -1, -1
9436 Xalpha_w, TRUE, FALSE,
9437 EL_CHAR('W'), -1, -1
9440 Xalpha_x, TRUE, FALSE,
9441 EL_CHAR('X'), -1, -1
9444 Xalpha_y, TRUE, FALSE,
9445 EL_CHAR('Y'), -1, -1
9448 Xalpha_z, TRUE, FALSE,
9449 EL_CHAR('Z'), -1, -1
9452 Xalpha_arrow_e, TRUE, FALSE,
9453 EL_CHAR('>'), -1, -1
9456 Xalpha_arrow_w, TRUE, FALSE,
9457 EL_CHAR('<'), -1, -1
9460 Xalpha_copyr, TRUE, FALSE,
9461 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
9465 Ykey_1_blank, FALSE, FALSE,
9466 EL_EM_KEY_1, ACTION_COLLECTING, -1
9469 Ykey_2_blank, FALSE, FALSE,
9470 EL_EM_KEY_2, ACTION_COLLECTING, -1
9473 Ykey_3_blank, FALSE, FALSE,
9474 EL_EM_KEY_3, ACTION_COLLECTING, -1
9477 Ykey_4_blank, FALSE, FALSE,
9478 EL_EM_KEY_4, ACTION_COLLECTING, -1
9481 Ykey_5_blank, FALSE, FALSE,
9482 EL_EMC_KEY_5, ACTION_COLLECTING, -1
9485 Ykey_6_blank, FALSE, FALSE,
9486 EL_EMC_KEY_6, ACTION_COLLECTING, -1
9489 Ykey_7_blank, FALSE, FALSE,
9490 EL_EMC_KEY_7, ACTION_COLLECTING, -1
9493 Ykey_8_blank, FALSE, FALSE,
9494 EL_EMC_KEY_8, ACTION_COLLECTING, -1
9497 Ylenses_blank, FALSE, FALSE,
9498 EL_EMC_LENSES, ACTION_COLLECTING, -1
9501 Ymagnify_blank, FALSE, FALSE,
9502 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
9505 Ygrass_blank, FALSE, FALSE,
9506 EL_EMC_GRASS, ACTION_SNAPPING, -1
9509 Ydirt_blank, FALSE, FALSE,
9510 EL_SAND, ACTION_SNAPPING, -1
9519 static struct Mapping_EM_to_RND_player
9528 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
9532 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
9536 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
9540 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
9544 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
9548 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
9552 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
9556 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
9560 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
9564 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
9568 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
9572 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
9576 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
9580 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
9584 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
9588 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
9592 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
9596 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
9600 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
9604 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
9608 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
9612 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
9616 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
9620 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
9624 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
9628 EL_PLAYER_1, ACTION_DEFAULT, -1,
9632 EL_PLAYER_2, ACTION_DEFAULT, -1,
9636 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
9640 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
9644 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
9648 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
9652 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
9656 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
9660 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
9664 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
9668 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
9672 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
9676 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
9680 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
9684 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
9688 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
9692 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
9696 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
9700 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
9704 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
9708 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
9712 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
9716 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
9720 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
9724 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
9728 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
9732 EL_PLAYER_3, ACTION_DEFAULT, -1,
9736 EL_PLAYER_4, ACTION_DEFAULT, -1,
9745 int map_element_RND_to_EM_cave(int element_rnd)
9747 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
9748 static boolean mapping_initialized = FALSE;
9750 if (!mapping_initialized)
9754 // return "Xalpha_quest" for all undefined elements in mapping array
9755 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9756 mapping_RND_to_EM[i] = Xalpha_quest;
9758 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
9759 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
9760 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
9761 em_object_mapping_list[i].element_em;
9763 mapping_initialized = TRUE;
9766 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
9768 Warn("invalid RND level element %d", element_rnd);
9773 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
9776 int map_element_EM_to_RND_cave(int element_em_cave)
9778 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
9779 static boolean mapping_initialized = FALSE;
9781 if (!mapping_initialized)
9785 // return "EL_UNKNOWN" for all undefined elements in mapping array
9786 for (i = 0; i < GAME_TILE_MAX; i++)
9787 mapping_EM_to_RND[i] = EL_UNKNOWN;
9789 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
9790 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
9791 em_object_mapping_list[i].element_rnd;
9793 mapping_initialized = TRUE;
9796 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
9798 Warn("invalid EM cave element %d", element_em_cave);
9803 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
9806 int map_element_EM_to_RND_game(int element_em_game)
9808 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
9809 static boolean mapping_initialized = FALSE;
9811 if (!mapping_initialized)
9815 // return "EL_UNKNOWN" for all undefined elements in mapping array
9816 for (i = 0; i < GAME_TILE_MAX; i++)
9817 mapping_EM_to_RND[i] = EL_UNKNOWN;
9819 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
9820 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
9821 em_object_mapping_list[i].element_rnd;
9823 mapping_initialized = TRUE;
9826 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
9828 Warn("invalid EM game element %d", element_em_game);
9833 return mapping_EM_to_RND[element_em_game];
9836 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
9838 struct LevelInfo_EM *level_em = level->native_em_level;
9839 struct CAVE *cav = level_em->cav;
9842 for (i = 0; i < GAME_TILE_MAX; i++)
9843 cav->android_array[i] = Cblank;
9845 for (i = 0; i < level->num_android_clone_elements; i++)
9847 int element_rnd = level->android_clone_element[i];
9848 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
9850 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
9851 if (em_object_mapping_list[j].element_rnd == element_rnd)
9852 cav->android_array[em_object_mapping_list[j].element_em] =
9857 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
9859 struct LevelInfo_EM *level_em = level->native_em_level;
9860 struct CAVE *cav = level_em->cav;
9863 level->num_android_clone_elements = 0;
9865 for (i = 0; i < GAME_TILE_MAX; i++)
9867 int element_em_cave = cav->android_array[i];
9869 boolean element_found = FALSE;
9871 if (element_em_cave == Cblank)
9874 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
9876 for (j = 0; j < level->num_android_clone_elements; j++)
9877 if (level->android_clone_element[j] == element_rnd)
9878 element_found = TRUE;
9882 level->android_clone_element[level->num_android_clone_elements++] =
9885 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
9890 if (level->num_android_clone_elements == 0)
9892 level->num_android_clone_elements = 1;
9893 level->android_clone_element[0] = EL_EMPTY;
9897 int map_direction_RND_to_EM(int direction)
9899 return (direction == MV_UP ? 0 :
9900 direction == MV_RIGHT ? 1 :
9901 direction == MV_DOWN ? 2 :
9902 direction == MV_LEFT ? 3 :
9906 int map_direction_EM_to_RND(int direction)
9908 return (direction == 0 ? MV_UP :
9909 direction == 1 ? MV_RIGHT :
9910 direction == 2 ? MV_DOWN :
9911 direction == 3 ? MV_LEFT :
9915 int map_element_RND_to_SP(int element_rnd)
9917 int element_sp = 0x20; // map unknown elements to yellow "hardware"
9919 if (element_rnd >= EL_SP_START &&
9920 element_rnd <= EL_SP_END)
9921 element_sp = element_rnd - EL_SP_START;
9922 else if (element_rnd == EL_EMPTY_SPACE)
9924 else if (element_rnd == EL_INVISIBLE_WALL)
9930 int map_element_SP_to_RND(int element_sp)
9932 int element_rnd = EL_UNKNOWN;
9934 if (element_sp >= 0x00 &&
9936 element_rnd = EL_SP_START + element_sp;
9937 else if (element_sp == 0x28)
9938 element_rnd = EL_INVISIBLE_WALL;
9943 int map_action_SP_to_RND(int action_sp)
9947 case actActive: return ACTION_ACTIVE;
9948 case actImpact: return ACTION_IMPACT;
9949 case actExploding: return ACTION_EXPLODING;
9950 case actDigging: return ACTION_DIGGING;
9951 case actSnapping: return ACTION_SNAPPING;
9952 case actCollecting: return ACTION_COLLECTING;
9953 case actPassing: return ACTION_PASSING;
9954 case actPushing: return ACTION_PUSHING;
9955 case actDropping: return ACTION_DROPPING;
9957 default: return ACTION_DEFAULT;
9961 int map_element_RND_to_MM(int element_rnd)
9963 return (element_rnd >= EL_MM_START_1 &&
9964 element_rnd <= EL_MM_END_1 ?
9965 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
9967 element_rnd >= EL_MM_START_2 &&
9968 element_rnd <= EL_MM_END_2 ?
9969 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
9971 element_rnd >= EL_MM_START_3 &&
9972 element_rnd <= EL_MM_END_3 ?
9973 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
9975 element_rnd >= EL_CHAR_START &&
9976 element_rnd <= EL_CHAR_END ?
9977 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
9979 element_rnd >= EL_MM_RUNTIME_START &&
9980 element_rnd <= EL_MM_RUNTIME_END ?
9981 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
9983 EL_MM_EMPTY_NATIVE);
9986 int map_element_MM_to_RND(int element_mm)
9988 return (element_mm == EL_MM_EMPTY_NATIVE ||
9989 element_mm == EL_DF_EMPTY_NATIVE ?
9992 element_mm >= EL_MM_START_1_NATIVE &&
9993 element_mm <= EL_MM_END_1_NATIVE ?
9994 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
9996 element_mm >= EL_MM_START_2_NATIVE &&
9997 element_mm <= EL_MM_END_2_NATIVE ?
9998 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
10000 element_mm >= EL_MM_START_3_NATIVE &&
10001 element_mm <= EL_MM_END_3_NATIVE ?
10002 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
10004 element_mm >= EL_MM_CHAR_START_NATIVE &&
10005 element_mm <= EL_MM_CHAR_END_NATIVE ?
10006 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
10008 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
10009 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
10010 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
10015 int map_action_MM_to_RND(int action_mm)
10017 // all MM actions are defined to exactly match their RND counterparts
10021 int map_sound_MM_to_RND(int sound_mm)
10025 case SND_MM_GAME_LEVELTIME_CHARGING:
10026 return SND_GAME_LEVELTIME_CHARGING;
10028 case SND_MM_GAME_HEALTH_CHARGING:
10029 return SND_GAME_HEALTH_CHARGING;
10032 return SND_UNDEFINED;
10036 int map_mm_wall_element(int element)
10038 return (element >= EL_MM_STEEL_WALL_START &&
10039 element <= EL_MM_STEEL_WALL_END ?
10042 element >= EL_MM_WOODEN_WALL_START &&
10043 element <= EL_MM_WOODEN_WALL_END ?
10044 EL_MM_WOODEN_WALL :
10046 element >= EL_MM_ICE_WALL_START &&
10047 element <= EL_MM_ICE_WALL_END ?
10050 element >= EL_MM_AMOEBA_WALL_START &&
10051 element <= EL_MM_AMOEBA_WALL_END ?
10052 EL_MM_AMOEBA_WALL :
10054 element >= EL_DF_STEEL_WALL_START &&
10055 element <= EL_DF_STEEL_WALL_END ?
10058 element >= EL_DF_WOODEN_WALL_START &&
10059 element <= EL_DF_WOODEN_WALL_END ?
10060 EL_DF_WOODEN_WALL :
10065 int map_mm_wall_element_editor(int element)
10069 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
10070 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
10071 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
10072 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
10073 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
10074 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
10076 default: return element;
10080 int get_next_element(int element)
10084 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
10085 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
10086 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
10087 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
10088 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
10089 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
10090 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
10091 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
10092 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
10093 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
10094 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
10096 default: return element;
10100 int el2img_mm(int element_mm)
10102 return el2img(map_element_MM_to_RND(element_mm));
10105 int el_act2img_mm(int element_mm, int action)
10107 return el_act2img(map_element_MM_to_RND(element_mm), action);
10110 int el_act_dir2img(int element, int action, int direction)
10112 element = GFX_ELEMENT(element);
10113 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
10115 // direction_graphic[][] == graphic[] for undefined direction graphics
10116 return element_info[element].direction_graphic[action][direction];
10119 static int el_act_dir2crm(int element, int action, int direction)
10121 element = GFX_ELEMENT(element);
10122 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
10124 // direction_graphic[][] == graphic[] for undefined direction graphics
10125 return element_info[element].direction_crumbled[action][direction];
10128 int el_act2img(int element, int action)
10130 element = GFX_ELEMENT(element);
10132 return element_info[element].graphic[action];
10135 int el_act2crm(int element, int action)
10137 element = GFX_ELEMENT(element);
10139 return element_info[element].crumbled[action];
10142 int el_dir2img(int element, int direction)
10144 element = GFX_ELEMENT(element);
10146 return el_act_dir2img(element, ACTION_DEFAULT, direction);
10149 int el2baseimg(int element)
10151 return element_info[element].graphic[ACTION_DEFAULT];
10154 int el2img(int element)
10156 element = GFX_ELEMENT(element);
10158 return element_info[element].graphic[ACTION_DEFAULT];
10161 int el2edimg(int element)
10163 element = GFX_ELEMENT(element);
10165 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
10168 int el2edimg_with_frame(int element, int *graphic, int *frame)
10170 *graphic = el2edimg(element);
10173 if (*graphic == IMG_UNKNOWN)
10175 // no graphic defined -- if BD style, try to get runtime ("effect") element graphics
10176 // (normal BD style elements have graphics, but runtime ("effects") elements do not)
10177 int element_bd = map_element_RND_to_BD_cave(element);
10179 if (element_bd != O_UNKNOWN)
10181 struct GraphicInfo_BD *g_bd = &graphic_info_bd_object[element_bd][0];
10183 *graphic = g_bd->graphic;
10184 *frame = g_bd->frame;
10191 int el2preimg(int element)
10193 element = GFX_ELEMENT(element);
10195 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
10198 int el2panelimg(int element)
10200 element = GFX_ELEMENT(element);
10202 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
10205 int font2baseimg(int font_nr)
10207 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
10210 int getBeltNrFromBeltElement(int element)
10212 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
10213 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
10214 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
10217 int getBeltNrFromBeltActiveElement(int element)
10219 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
10220 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
10221 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
10224 int getBeltNrFromBeltSwitchElement(int element)
10226 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
10227 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
10228 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
10231 int getBeltDirNrFromBeltElement(int element)
10233 static int belt_base_element[4] =
10235 EL_CONVEYOR_BELT_1_LEFT,
10236 EL_CONVEYOR_BELT_2_LEFT,
10237 EL_CONVEYOR_BELT_3_LEFT,
10238 EL_CONVEYOR_BELT_4_LEFT
10241 int belt_nr = getBeltNrFromBeltElement(element);
10242 int belt_dir_nr = element - belt_base_element[belt_nr];
10244 return (belt_dir_nr % 3);
10247 int getBeltDirNrFromBeltSwitchElement(int element)
10249 static int belt_base_element[4] =
10251 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
10252 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
10253 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
10254 EL_CONVEYOR_BELT_4_SWITCH_LEFT
10257 int belt_nr = getBeltNrFromBeltSwitchElement(element);
10258 int belt_dir_nr = element - belt_base_element[belt_nr];
10260 return (belt_dir_nr % 3);
10263 int getBeltDirFromBeltElement(int element)
10265 static int belt_move_dir[3] =
10272 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
10274 return belt_move_dir[belt_dir_nr];
10277 int getBeltDirFromBeltSwitchElement(int element)
10279 static int belt_move_dir[3] =
10286 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
10288 return belt_move_dir[belt_dir_nr];
10291 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
10293 static int belt_base_element[4] =
10295 EL_CONVEYOR_BELT_1_LEFT,
10296 EL_CONVEYOR_BELT_2_LEFT,
10297 EL_CONVEYOR_BELT_3_LEFT,
10298 EL_CONVEYOR_BELT_4_LEFT
10301 return belt_base_element[belt_nr] + belt_dir_nr;
10304 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
10306 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
10308 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
10311 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
10313 static int belt_base_element[4] =
10315 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
10316 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
10317 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
10318 EL_CONVEYOR_BELT_4_SWITCH_LEFT
10321 return belt_base_element[belt_nr] + belt_dir_nr;
10324 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
10326 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
10328 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
10331 boolean swapTiles_EM(boolean is_pre_emc_cave)
10333 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
10336 boolean getTeamMode_EM(void)
10338 return game.team_mode || network_playing;
10341 boolean isActivePlayer_EM(int player_nr)
10343 return stored_player[player_nr].active;
10346 unsigned int InitRND(int seed)
10348 if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
10349 return InitEngineRandom_BD(seed);
10350 else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10351 return InitEngineRandom_EM(seed);
10352 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10353 return InitEngineRandom_SP(seed);
10354 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
10355 return InitEngineRandom_MM(seed);
10357 return InitEngineRandom_RND(seed);
10360 static struct Mapping_BD_to_RND_object bd_object_mapping[O_MAX_ALL];
10361 static struct Mapping_EM_to_RND_object em_object_mapping[GAME_TILE_MAX];
10362 static struct Mapping_EM_to_RND_player em_player_mapping[MAX_PLAYERS][PLY_MAX];
10364 static int get_effective_element_EM(int tile, int frame_em)
10366 int element = em_object_mapping[tile].element_rnd;
10367 int action = em_object_mapping[tile].action;
10368 boolean is_backside = em_object_mapping[tile].is_backside;
10369 boolean action_removing = (action == ACTION_DIGGING ||
10370 action == ACTION_SNAPPING ||
10371 action == ACTION_COLLECTING);
10379 return (frame_em > 5 ? EL_EMPTY : element);
10385 else // frame_em == 7
10396 case Ydiamond_stone:
10399 case Xdrip_stretch:
10400 case Xdrip_stretchB:
10415 case Ylenses_blank:
10416 case Ymagnify_blank:
10419 case Xsand_stonein_1:
10420 case Xsand_stonein_2:
10421 case Xsand_stonein_3:
10422 case Xsand_stonein_4:
10426 return (is_backside || action_removing ? EL_EMPTY : element);
10431 static boolean check_linear_animation_EM(int tile)
10435 case Xsand_stonesand_1:
10436 case Xsand_stonesand_quickout_1:
10437 case Xsand_sandstone_1:
10438 case Xsand_stonein_1:
10439 case Xsand_stoneout_1:
10467 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
10468 boolean has_crumbled_graphics,
10469 int crumbled, int sync_frame)
10471 // if element can be crumbled, but certain action graphics are just empty
10472 // space (like instantly snapping sand to empty space in 1 frame), do not
10473 // treat these empty space graphics as crumbled graphics in EMC engine
10474 if (crumbled == IMG_EMPTY_SPACE)
10475 has_crumbled_graphics = FALSE;
10477 if (has_crumbled_graphics)
10479 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
10480 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
10481 g_crumbled->anim_delay,
10482 g_crumbled->anim_mode,
10483 g_crumbled->anim_start_frame,
10486 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
10487 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
10489 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
10490 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
10492 g_em->has_crumbled_graphics = TRUE;
10496 g_em->crumbled_bitmap = NULL;
10497 g_em->crumbled_src_x = 0;
10498 g_em->crumbled_src_y = 0;
10499 g_em->crumbled_border_size = 0;
10500 g_em->crumbled_tile_size = 0;
10502 g_em->has_crumbled_graphics = FALSE;
10507 void ResetGfxAnimation_EM(int x, int y, int tile)
10509 GfxFrame[x][y] = 0;
10513 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
10514 int tile, int frame_em, int x, int y)
10516 int action = em_object_mapping[tile].action;
10517 int direction = em_object_mapping[tile].direction;
10518 int effective_element = get_effective_element_EM(tile, frame_em);
10519 int graphic = (direction == MV_NONE ?
10520 el_act2img(effective_element, action) :
10521 el_act_dir2img(effective_element, action, direction));
10522 struct GraphicInfo *g = &graphic_info[graphic];
10524 boolean action_removing = (action == ACTION_DIGGING ||
10525 action == ACTION_SNAPPING ||
10526 action == ACTION_COLLECTING);
10527 boolean action_moving = (action == ACTION_FALLING ||
10528 action == ACTION_MOVING ||
10529 action == ACTION_PUSHING ||
10530 action == ACTION_EATING ||
10531 action == ACTION_FILLING ||
10532 action == ACTION_EMPTYING);
10533 boolean action_falling = (action == ACTION_FALLING ||
10534 action == ACTION_FILLING ||
10535 action == ACTION_EMPTYING);
10537 // special case: graphic uses "2nd movement tile" and has defined
10538 // 7 frames for movement animation (or less) => use default graphic
10539 // for last (8th) frame which ends the movement animation
10540 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
10542 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
10543 graphic = (direction == MV_NONE ?
10544 el_act2img(effective_element, action) :
10545 el_act_dir2img(effective_element, action, direction));
10547 g = &graphic_info[graphic];
10550 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
10552 GfxFrame[x][y] = 0;
10554 else if (action_moving)
10556 boolean is_backside = em_object_mapping[tile].is_backside;
10560 int direction = em_object_mapping[tile].direction;
10561 int move_dir = (action_falling ? MV_DOWN : direction);
10566 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
10567 if (g->double_movement && frame_em == 0)
10568 GfxFrame[x][y] = 0;
10571 if (move_dir == MV_LEFT)
10572 GfxFrame[x - 1][y] = GfxFrame[x][y];
10573 else if (move_dir == MV_RIGHT)
10574 GfxFrame[x + 1][y] = GfxFrame[x][y];
10575 else if (move_dir == MV_UP)
10576 GfxFrame[x][y - 1] = GfxFrame[x][y];
10577 else if (move_dir == MV_DOWN)
10578 GfxFrame[x][y + 1] = GfxFrame[x][y];
10585 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
10586 if (tile == Xsand_stonesand_quickout_1 ||
10587 tile == Xsand_stonesand_quickout_2)
10591 if (graphic_info[graphic].anim_global_sync)
10592 sync_frame = FrameCounter;
10593 else if (graphic_info[graphic].anim_global_anim_sync)
10594 sync_frame = getGlobalAnimSyncFrame();
10595 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
10596 sync_frame = GfxFrame[x][y];
10598 sync_frame = 0; // playfield border (pseudo steel)
10600 SetRandomAnimationValue(x, y);
10602 int frame = getAnimationFrame(g->anim_frames,
10605 g->anim_start_frame,
10608 g_em->unique_identifier =
10609 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
10612 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
10613 int tile, int frame_em, int x, int y)
10615 int action = em_object_mapping[tile].action;
10616 int direction = em_object_mapping[tile].direction;
10617 boolean is_backside = em_object_mapping[tile].is_backside;
10618 int effective_element = get_effective_element_EM(tile, frame_em);
10619 int effective_action = action;
10620 int graphic = (direction == MV_NONE ?
10621 el_act2img(effective_element, effective_action) :
10622 el_act_dir2img(effective_element, effective_action,
10624 int crumbled = (direction == MV_NONE ?
10625 el_act2crm(effective_element, effective_action) :
10626 el_act_dir2crm(effective_element, effective_action,
10628 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
10629 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
10630 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
10631 struct GraphicInfo *g = &graphic_info[graphic];
10634 // special case: graphic uses "2nd movement tile" and has defined
10635 // 7 frames for movement animation (or less) => use default graphic
10636 // for last (8th) frame which ends the movement animation
10637 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
10639 effective_action = ACTION_DEFAULT;
10640 graphic = (direction == MV_NONE ?
10641 el_act2img(effective_element, effective_action) :
10642 el_act_dir2img(effective_element, effective_action,
10644 crumbled = (direction == MV_NONE ?
10645 el_act2crm(effective_element, effective_action) :
10646 el_act_dir2crm(effective_element, effective_action,
10649 g = &graphic_info[graphic];
10652 if (graphic_info[graphic].anim_global_sync)
10653 sync_frame = FrameCounter;
10654 else if (graphic_info[graphic].anim_global_anim_sync)
10655 sync_frame = getGlobalAnimSyncFrame();
10656 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
10657 sync_frame = GfxFrame[x][y];
10659 sync_frame = 0; // playfield border (pseudo steel)
10661 SetRandomAnimationValue(x, y);
10663 int frame = getAnimationFrame(g->anim_frames,
10666 g->anim_start_frame,
10669 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
10670 g->double_movement && is_backside);
10672 // (updating the "crumbled" graphic definitions is probably not really needed,
10673 // as animations for crumbled graphics can't be longer than one EMC cycle)
10674 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
10678 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
10679 int player_nr, int anim, int frame_em)
10681 int element = em_player_mapping[player_nr][anim].element_rnd;
10682 int action = em_player_mapping[player_nr][anim].action;
10683 int direction = em_player_mapping[player_nr][anim].direction;
10684 int graphic = (direction == MV_NONE ?
10685 el_act2img(element, action) :
10686 el_act_dir2img(element, action, direction));
10687 struct GraphicInfo *g = &graphic_info[graphic];
10690 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
10692 stored_player[player_nr].StepFrame = frame_em;
10694 sync_frame = stored_player[player_nr].Frame;
10696 int frame = getAnimationFrame(g->anim_frames,
10699 g->anim_start_frame,
10702 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
10703 &g_em->src_x, &g_em->src_y, FALSE);
10706 #define BD_GFX_RANGE(a, n, i) ((i) >= (a) && (i) < (a) + (n))
10707 #define BD_GFX_FRAME(b, i) (((i) - (b)) * 8)
10709 void InitGraphicInfo_BD(void)
10713 // always start with reliable default values
10714 for (i = 0; i < O_MAX_ALL; i++)
10716 bd_object_mapping[i].element_rnd = EL_UNKNOWN;
10717 bd_object_mapping[i].action = ACTION_DEFAULT;
10718 bd_object_mapping[i].direction = MV_NONE;
10721 for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
10723 int e = bd_object_mapping_list[i].element_bd;
10725 bd_object_mapping[e].element_rnd = bd_object_mapping_list[i].element_rnd;
10727 if (bd_object_mapping_list[i].action != -1)
10728 bd_object_mapping[e].action = bd_object_mapping_list[i].action;
10730 if (bd_object_mapping_list[i].direction != -1)
10731 bd_object_mapping[e].direction =
10732 MV_DIR_FROM_BIT(bd_object_mapping_list[i].direction);
10735 for (i = 0; i < O_MAX_ALL; i++)
10737 int element = bd_object_mapping[i].element_rnd;
10738 int action = bd_object_mapping[i].action;
10739 int direction = bd_object_mapping[i].direction;
10741 for (j = 0; j < 8; j++)
10743 int effective_element = element;
10744 int effective_action = action;
10745 int graphic = (el_act_dir2img(effective_element, effective_action,
10747 struct GraphicInfo *g = &graphic_info[graphic];
10748 struct GraphicInfo_BD *g_bd = &graphic_info_bd_object[i][j];
10749 Bitmap *src_bitmap;
10751 int sync_frame = (BD_GFX_RANGE(O_PRE_PL_1, 3, i) ? BD_GFX_FRAME(O_PRE_PL_1, i) :
10752 BD_GFX_RANGE(O_PRE_DIA_1, 5, i) ? BD_GFX_FRAME(O_PRE_DIA_1, i) :
10753 BD_GFX_RANGE(O_PRE_STONE_1, 4, i) ? BD_GFX_FRAME(O_PRE_STONE_1, i) :
10754 BD_GFX_RANGE(O_PRE_STEEL_1, 4, i) ? BD_GFX_FRAME(O_PRE_STEEL_1, i) :
10755 BD_GFX_RANGE(O_BOMB_TICK_1, 7, i) ? BD_GFX_FRAME(O_BOMB_TICK_1, i) :
10756 BD_GFX_RANGE(O_BOMB_EXPL_1, 4, i) ? BD_GFX_FRAME(O_BOMB_EXPL_1, i) :
10757 BD_GFX_RANGE(O_NUT_EXPL_1, 4, i) ? BD_GFX_FRAME(O_NUT_EXPL_1, i) :
10758 BD_GFX_RANGE(O_GHOST_EXPL_1, 4, i) ? BD_GFX_FRAME(O_GHOST_EXPL_1, i) :
10759 BD_GFX_RANGE(O_EXPLODE_1, 5, i) ? BD_GFX_FRAME(O_EXPLODE_1, i) :
10760 BD_GFX_RANGE(O_PRE_CLOCK_1, 4, i) ? BD_GFX_FRAME(O_PRE_CLOCK_1, i) :
10761 BD_GFX_RANGE(O_NITRO_EXPL_1, 4, i) ? BD_GFX_FRAME(O_NITRO_EXPL_1, i) :
10762 BD_GFX_RANGE(O_AMOEBA_2_EXPL_1, 4, i) ? BD_GFX_FRAME(O_AMOEBA_2_EXPL_1, i):
10763 i == O_INBOX_OPEN || i == O_OUTBOX_OPEN ? j :
10765 int frame = getAnimationFrame(g->anim_frames,
10768 g->anim_start_frame,
10771 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
10773 g_bd->bitmap = src_bitmap;
10774 g_bd->src_x = src_x;
10775 g_bd->src_y = src_y;
10776 g_bd->width = TILEX;
10777 g_bd->height = TILEY;
10779 g_bd->graphic = graphic;
10780 g_bd->frame = frame;
10784 // game graphics template for level-specific colors for native BD levels
10785 int graphic = IMG_BD_GAME_GRAPHICS_COLOR_TEMPLATE;
10786 struct GraphicInfo_BD *g_bd = &graphic_info_bd_color_template;
10787 Bitmap *src_bitmap;
10790 getGraphicSourceExt(graphic, 0, &src_bitmap, &src_x, &src_y, FALSE);
10792 g_bd->bitmap = src_bitmap;
10793 g_bd->src_x = src_x;
10794 g_bd->src_y = src_y;
10795 g_bd->width = TILEX;
10796 g_bd->height = TILEY;
10798 g_bd->graphic = graphic;
10802 void InitGraphicInfo_EM(void)
10806 // always start with reliable default values
10807 for (i = 0; i < GAME_TILE_MAX; i++)
10809 em_object_mapping[i].element_rnd = EL_UNKNOWN;
10810 em_object_mapping[i].is_backside = FALSE;
10811 em_object_mapping[i].action = ACTION_DEFAULT;
10812 em_object_mapping[i].direction = MV_NONE;
10815 // always start with reliable default values
10816 for (p = 0; p < MAX_PLAYERS; p++)
10818 for (i = 0; i < PLY_MAX; i++)
10820 em_player_mapping[p][i].element_rnd = EL_UNKNOWN;
10821 em_player_mapping[p][i].action = ACTION_DEFAULT;
10822 em_player_mapping[p][i].direction = MV_NONE;
10826 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
10828 int e = em_object_mapping_list[i].element_em;
10830 em_object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
10831 em_object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
10833 if (em_object_mapping_list[i].action != -1)
10834 em_object_mapping[e].action = em_object_mapping_list[i].action;
10836 if (em_object_mapping_list[i].direction != -1)
10837 em_object_mapping[e].direction =
10838 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
10841 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
10843 int a = em_player_mapping_list[i].action_em;
10844 int p = em_player_mapping_list[i].player_nr;
10846 em_player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
10848 if (em_player_mapping_list[i].action != -1)
10849 em_player_mapping[p][a].action = em_player_mapping_list[i].action;
10851 if (em_player_mapping_list[i].direction != -1)
10852 em_player_mapping[p][a].direction =
10853 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
10856 for (i = 0; i < GAME_TILE_MAX; i++)
10858 int element = em_object_mapping[i].element_rnd;
10859 int action = em_object_mapping[i].action;
10860 int direction = em_object_mapping[i].direction;
10861 boolean is_backside = em_object_mapping[i].is_backside;
10862 boolean action_exploding = ((action == ACTION_EXPLODING ||
10863 action == ACTION_SMASHED_BY_ROCK ||
10864 action == ACTION_SMASHED_BY_SPRING) &&
10865 element != EL_DIAMOND);
10866 boolean action_active = (action == ACTION_ACTIVE);
10867 boolean action_other = (action == ACTION_OTHER);
10869 for (j = 0; j < 8; j++)
10871 int effective_element = get_effective_element_EM(i, j);
10872 int effective_action = (j < 7 ? action :
10873 i == Xdrip_stretch ? action :
10874 i == Xdrip_stretchB ? action :
10875 i == Ydrip_1_s ? action :
10876 i == Ydrip_1_sB ? action :
10877 i == Yball_1 ? action :
10878 i == Xball_2 ? action :
10879 i == Yball_2 ? action :
10880 i == Yball_blank ? action :
10881 i == Ykey_1_blank ? action :
10882 i == Ykey_2_blank ? action :
10883 i == Ykey_3_blank ? action :
10884 i == Ykey_4_blank ? action :
10885 i == Ykey_5_blank ? action :
10886 i == Ykey_6_blank ? action :
10887 i == Ykey_7_blank ? action :
10888 i == Ykey_8_blank ? action :
10889 i == Ylenses_blank ? action :
10890 i == Ymagnify_blank ? action :
10891 i == Ygrass_blank ? action :
10892 i == Ydirt_blank ? action :
10893 i == Xsand_stonein_1 ? action :
10894 i == Xsand_stonein_2 ? action :
10895 i == Xsand_stonein_3 ? action :
10896 i == Xsand_stonein_4 ? action :
10897 i == Xsand_stoneout_1 ? action :
10898 i == Xsand_stoneout_2 ? action :
10899 i == Xboom_android ? ACTION_EXPLODING :
10900 action_exploding ? ACTION_EXPLODING :
10901 action_active ? action :
10902 action_other ? action :
10904 int graphic = (el_act_dir2img(effective_element, effective_action,
10906 int crumbled = (el_act_dir2crm(effective_element, effective_action,
10908 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
10909 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
10910 boolean has_action_graphics = (graphic != base_graphic);
10911 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
10912 struct GraphicInfo *g = &graphic_info[graphic];
10913 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
10914 Bitmap *src_bitmap;
10916 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
10917 boolean special_animation = (action != ACTION_DEFAULT &&
10918 g->anim_frames == 3 &&
10919 g->anim_delay == 2 &&
10920 g->anim_mode & ANIM_LINEAR);
10921 int sync_frame = (i == Xdrip_stretch ? 7 :
10922 i == Xdrip_stretchB ? 7 :
10923 i == Ydrip_2_s ? j + 8 :
10924 i == Ydrip_2_sB ? j + 8 :
10926 i == Xacid_2 ? 10 :
10927 i == Xacid_3 ? 20 :
10928 i == Xacid_4 ? 30 :
10929 i == Xacid_5 ? 40 :
10930 i == Xacid_6 ? 50 :
10931 i == Xacid_7 ? 60 :
10932 i == Xacid_8 ? 70 :
10933 i == Xfake_acid_1 ? 0 :
10934 i == Xfake_acid_2 ? 10 :
10935 i == Xfake_acid_3 ? 20 :
10936 i == Xfake_acid_4 ? 30 :
10937 i == Xfake_acid_5 ? 40 :
10938 i == Xfake_acid_6 ? 50 :
10939 i == Xfake_acid_7 ? 60 :
10940 i == Xfake_acid_8 ? 70 :
10941 i == Xfake_acid_1_player ? 0 :
10942 i == Xfake_acid_2_player ? 10 :
10943 i == Xfake_acid_3_player ? 20 :
10944 i == Xfake_acid_4_player ? 30 :
10945 i == Xfake_acid_5_player ? 40 :
10946 i == Xfake_acid_6_player ? 50 :
10947 i == Xfake_acid_7_player ? 60 :
10948 i == Xfake_acid_8_player ? 70 :
10950 i == Yball_2 ? j + 8 :
10951 i == Yball_blank ? j + 1 :
10952 i == Ykey_1_blank ? j + 1 :
10953 i == Ykey_2_blank ? j + 1 :
10954 i == Ykey_3_blank ? j + 1 :
10955 i == Ykey_4_blank ? j + 1 :
10956 i == Ykey_5_blank ? j + 1 :
10957 i == Ykey_6_blank ? j + 1 :
10958 i == Ykey_7_blank ? j + 1 :
10959 i == Ykey_8_blank ? j + 1 :
10960 i == Ylenses_blank ? j + 1 :
10961 i == Ymagnify_blank ? j + 1 :
10962 i == Ygrass_blank ? j + 1 :
10963 i == Ydirt_blank ? j + 1 :
10964 i == Xamoeba_1 ? 0 :
10965 i == Xamoeba_2 ? 1 :
10966 i == Xamoeba_3 ? 2 :
10967 i == Xamoeba_4 ? 3 :
10968 i == Xamoeba_5 ? 0 :
10969 i == Xamoeba_6 ? 1 :
10970 i == Xamoeba_7 ? 2 :
10971 i == Xamoeba_8 ? 3 :
10972 i == Xexit_2 ? j + 8 :
10973 i == Xexit_3 ? j + 16 :
10974 i == Xdynamite_1 ? 0 :
10975 i == Xdynamite_2 ? 8 :
10976 i == Xdynamite_3 ? 16 :
10977 i == Xdynamite_4 ? 24 :
10978 i == Xsand_stonein_1 ? j + 1 :
10979 i == Xsand_stonein_2 ? j + 9 :
10980 i == Xsand_stonein_3 ? j + 17 :
10981 i == Xsand_stonein_4 ? j + 25 :
10982 i == Xsand_stoneout_1 && j == 0 ? 0 :
10983 i == Xsand_stoneout_1 && j == 1 ? 0 :
10984 i == Xsand_stoneout_1 && j == 2 ? 1 :
10985 i == Xsand_stoneout_1 && j == 3 ? 2 :
10986 i == Xsand_stoneout_1 && j == 4 ? 2 :
10987 i == Xsand_stoneout_1 && j == 5 ? 3 :
10988 i == Xsand_stoneout_1 && j == 6 ? 4 :
10989 i == Xsand_stoneout_1 && j == 7 ? 4 :
10990 i == Xsand_stoneout_2 && j == 0 ? 5 :
10991 i == Xsand_stoneout_2 && j == 1 ? 6 :
10992 i == Xsand_stoneout_2 && j == 2 ? 7 :
10993 i == Xsand_stoneout_2 && j == 3 ? 8 :
10994 i == Xsand_stoneout_2 && j == 4 ? 9 :
10995 i == Xsand_stoneout_2 && j == 5 ? 11 :
10996 i == Xsand_stoneout_2 && j == 6 ? 13 :
10997 i == Xsand_stoneout_2 && j == 7 ? 15 :
10998 i == Xboom_bug && j == 1 ? 2 :
10999 i == Xboom_bug && j == 2 ? 2 :
11000 i == Xboom_bug && j == 3 ? 4 :
11001 i == Xboom_bug && j == 4 ? 4 :
11002 i == Xboom_bug && j == 5 ? 2 :
11003 i == Xboom_bug && j == 6 ? 2 :
11004 i == Xboom_bug && j == 7 ? 0 :
11005 i == Xboom_tank && j == 1 ? 2 :
11006 i == Xboom_tank && j == 2 ? 2 :
11007 i == Xboom_tank && j == 3 ? 4 :
11008 i == Xboom_tank && j == 4 ? 4 :
11009 i == Xboom_tank && j == 5 ? 2 :
11010 i == Xboom_tank && j == 6 ? 2 :
11011 i == Xboom_tank && j == 7 ? 0 :
11012 i == Xboom_android && j == 7 ? 6 :
11013 i == Xboom_1 && j == 1 ? 2 :
11014 i == Xboom_1 && j == 2 ? 2 :
11015 i == Xboom_1 && j == 3 ? 4 :
11016 i == Xboom_1 && j == 4 ? 4 :
11017 i == Xboom_1 && j == 5 ? 6 :
11018 i == Xboom_1 && j == 6 ? 6 :
11019 i == Xboom_1 && j == 7 ? 8 :
11020 i == Xboom_2 && j == 0 ? 8 :
11021 i == Xboom_2 && j == 1 ? 8 :
11022 i == Xboom_2 && j == 2 ? 10 :
11023 i == Xboom_2 && j == 3 ? 10 :
11024 i == Xboom_2 && j == 4 ? 10 :
11025 i == Xboom_2 && j == 5 ? 12 :
11026 i == Xboom_2 && j == 6 ? 12 :
11027 i == Xboom_2 && j == 7 ? 12 :
11028 special_animation && j == 4 ? 3 :
11029 effective_action != action ? 0 :
11031 int frame = getAnimationFrame(g->anim_frames,
11034 g->anim_start_frame,
11037 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
11038 g->double_movement && is_backside);
11040 g_em->bitmap = src_bitmap;
11041 g_em->src_x = src_x;
11042 g_em->src_y = src_y;
11043 g_em->src_offset_x = 0;
11044 g_em->src_offset_y = 0;
11045 g_em->dst_offset_x = 0;
11046 g_em->dst_offset_y = 0;
11047 g_em->width = TILEX;
11048 g_em->height = TILEY;
11050 g_em->preserve_background = FALSE;
11052 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
11055 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
11056 effective_action == ACTION_MOVING ||
11057 effective_action == ACTION_PUSHING ||
11058 effective_action == ACTION_EATING)) ||
11059 (!has_action_graphics && (effective_action == ACTION_FILLING ||
11060 effective_action == ACTION_EMPTYING)))
11063 (effective_action == ACTION_FALLING ||
11064 effective_action == ACTION_FILLING ||
11065 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
11066 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
11067 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
11068 int num_steps = (i == Ydrip_1_s ? 16 :
11069 i == Ydrip_1_sB ? 16 :
11070 i == Ydrip_2_s ? 16 :
11071 i == Ydrip_2_sB ? 16 :
11072 i == Xsand_stonein_1 ? 32 :
11073 i == Xsand_stonein_2 ? 32 :
11074 i == Xsand_stonein_3 ? 32 :
11075 i == Xsand_stonein_4 ? 32 :
11076 i == Xsand_stoneout_1 ? 16 :
11077 i == Xsand_stoneout_2 ? 16 : 8);
11078 int cx = ABS(dx) * (TILEX / num_steps);
11079 int cy = ABS(dy) * (TILEY / num_steps);
11080 int step_frame = (i == Ydrip_2_s ? j + 8 :
11081 i == Ydrip_2_sB ? j + 8 :
11082 i == Xsand_stonein_2 ? j + 8 :
11083 i == Xsand_stonein_3 ? j + 16 :
11084 i == Xsand_stonein_4 ? j + 24 :
11085 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
11086 int step = (is_backside ? step_frame : num_steps - step_frame);
11088 if (is_backside) // tile where movement starts
11090 if (dx < 0 || dy < 0)
11092 g_em->src_offset_x = cx * step;
11093 g_em->src_offset_y = cy * step;
11097 g_em->dst_offset_x = cx * step;
11098 g_em->dst_offset_y = cy * step;
11101 else // tile where movement ends
11103 if (dx < 0 || dy < 0)
11105 g_em->dst_offset_x = cx * step;
11106 g_em->dst_offset_y = cy * step;
11110 g_em->src_offset_x = cx * step;
11111 g_em->src_offset_y = cy * step;
11115 g_em->width = TILEX - cx * step;
11116 g_em->height = TILEY - cy * step;
11119 // create unique graphic identifier to decide if tile must be redrawn
11120 /* bit 31 - 16 (16 bit): EM style graphic
11121 bit 15 - 12 ( 4 bit): EM style frame
11122 bit 11 - 6 ( 6 bit): graphic width
11123 bit 5 - 0 ( 6 bit): graphic height */
11124 g_em->unique_identifier =
11125 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
11129 for (i = 0; i < GAME_TILE_MAX; i++)
11131 for (j = 0; j < 8; j++)
11133 int element = em_object_mapping[i].element_rnd;
11134 int action = em_object_mapping[i].action;
11135 int direction = em_object_mapping[i].direction;
11136 boolean is_backside = em_object_mapping[i].is_backside;
11137 int graphic_action = el_act_dir2img(element, action, direction);
11138 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
11140 if ((action == ACTION_SMASHED_BY_ROCK ||
11141 action == ACTION_SMASHED_BY_SPRING ||
11142 action == ACTION_EATING) &&
11143 graphic_action == graphic_default)
11145 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
11146 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
11147 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
11148 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
11151 // no separate animation for "smashed by rock" -- use rock instead
11152 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
11153 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
11155 g_em->bitmap = g_xx->bitmap;
11156 g_em->src_x = g_xx->src_x;
11157 g_em->src_y = g_xx->src_y;
11158 g_em->src_offset_x = g_xx->src_offset_x;
11159 g_em->src_offset_y = g_xx->src_offset_y;
11160 g_em->dst_offset_x = g_xx->dst_offset_x;
11161 g_em->dst_offset_y = g_xx->dst_offset_y;
11162 g_em->width = g_xx->width;
11163 g_em->height = g_xx->height;
11164 g_em->unique_identifier = g_xx->unique_identifier;
11167 g_em->preserve_background = TRUE;
11172 for (p = 0; p < MAX_PLAYERS; p++)
11174 for (i = 0; i < PLY_MAX; i++)
11176 int element = em_player_mapping[p][i].element_rnd;
11177 int action = em_player_mapping[p][i].action;
11178 int direction = em_player_mapping[p][i].direction;
11180 for (j = 0; j < 8; j++)
11182 int effective_element = element;
11183 int effective_action = action;
11184 int graphic = (direction == MV_NONE ?
11185 el_act2img(effective_element, effective_action) :
11186 el_act_dir2img(effective_element, effective_action,
11188 struct GraphicInfo *g = &graphic_info[graphic];
11189 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
11190 Bitmap *src_bitmap;
11192 int sync_frame = j;
11193 int frame = getAnimationFrame(g->anim_frames,
11196 g->anim_start_frame,
11199 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
11201 g_em->bitmap = src_bitmap;
11202 g_em->src_x = src_x;
11203 g_em->src_y = src_y;
11204 g_em->src_offset_x = 0;
11205 g_em->src_offset_y = 0;
11206 g_em->dst_offset_x = 0;
11207 g_em->dst_offset_y = 0;
11208 g_em->width = TILEX;
11209 g_em->height = TILEY;
11215 static void CheckSaveEngineSnapshot_EM(int frame,
11216 boolean any_player_moving,
11217 boolean any_player_snapping,
11218 boolean any_player_dropping)
11220 if (frame == 7 && !any_player_dropping)
11222 if (!local_player->was_waiting)
11224 if (!CheckSaveEngineSnapshotToList())
11227 local_player->was_waiting = TRUE;
11230 else if (any_player_moving || any_player_snapping || any_player_dropping)
11232 local_player->was_waiting = FALSE;
11236 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
11237 boolean murphy_is_dropping)
11239 if (murphy_is_waiting)
11241 if (!local_player->was_waiting)
11243 if (!CheckSaveEngineSnapshotToList())
11246 local_player->was_waiting = TRUE;
11251 local_player->was_waiting = FALSE;
11255 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
11256 boolean button_released)
11258 if (button_released)
11260 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
11261 CheckSaveEngineSnapshotToList();
11263 else if (element_clicked)
11265 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
11266 CheckSaveEngineSnapshotToList();
11268 game.snapshot.changed_action = TRUE;
11272 boolean CheckSingleStepMode_EM(int frame,
11273 boolean any_player_moving,
11274 boolean any_player_snapping,
11275 boolean any_player_dropping)
11277 if (tape.single_step && tape.recording && !tape.pausing)
11278 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
11279 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11281 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
11282 any_player_snapping, any_player_dropping);
11284 return tape.pausing;
11287 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
11288 boolean murphy_is_dropping)
11290 boolean murphy_starts_dropping = FALSE;
11293 for (i = 0; i < MAX_PLAYERS; i++)
11294 if (stored_player[i].force_dropping)
11295 murphy_starts_dropping = TRUE;
11297 if (tape.single_step && tape.recording && !tape.pausing)
11298 if (murphy_is_waiting && !murphy_starts_dropping)
11299 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11301 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
11304 void CheckSingleStepMode_MM(boolean element_clicked,
11305 boolean button_released)
11307 if (tape.single_step && tape.recording && !tape.pausing)
11308 if (button_released)
11309 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11311 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
11314 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
11315 int graphic, int sync_frame)
11317 int frame = getGraphicAnimationFrame(graphic, sync_frame);
11319 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
11322 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
11324 return (IS_NEXT_FRAME(sync_frame, graphic));
11327 int getGraphicInfo_Delay(int graphic)
11329 return graphic_info[graphic].anim_delay;
11332 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
11334 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
11337 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
11343 void PlayMenuSoundExt(int sound)
11345 if (sound == SND_UNDEFINED)
11348 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
11349 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
11352 if (IS_LOOP_SOUND(sound))
11353 PlaySoundLoop(sound);
11358 void PlayMenuSound(void)
11360 PlayMenuSoundExt(menu.sound[game_status]);
11363 void PlayMenuSoundStereo(int sound, int stereo_position)
11365 if (sound == SND_UNDEFINED)
11368 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
11369 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
11372 if (IS_LOOP_SOUND(sound))
11373 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
11375 PlaySoundStereo(sound, stereo_position);
11378 void PlayMenuSoundIfLoopExt(int sound)
11380 if (sound == SND_UNDEFINED)
11383 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
11384 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
11387 if (IS_LOOP_SOUND(sound))
11388 PlaySoundLoop(sound);
11391 void PlayMenuSoundIfLoop(void)
11393 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
11396 void PlayMenuMusicExt(int music)
11398 if (music == MUS_UNDEFINED)
11401 if (!setup.sound_music)
11404 if (IS_LOOP_MUSIC(music))
11405 PlayMusicLoop(music);
11410 void PlayMenuMusic(void)
11412 char *curr_music = getCurrentlyPlayingMusicFilename();
11413 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
11415 if (!strEqual(curr_music, next_music))
11416 PlayMenuMusicExt(menu.music[game_status]);
11419 void PlayMenuSoundsAndMusic(void)
11425 static void FadeMenuSounds(void)
11430 static void FadeMenuMusic(void)
11432 char *curr_music = getCurrentlyPlayingMusicFilename();
11433 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
11435 if (!strEqual(curr_music, next_music))
11439 void FadeMenuSoundsAndMusic(void)
11445 void PlaySoundActivating(void)
11448 PlaySound(SND_MENU_ITEM_ACTIVATING);
11452 void PlaySoundSelecting(void)
11455 PlaySound(SND_MENU_ITEM_SELECTING);
11459 void ToggleAudioSampleRateIfNeeded(void)
11461 int setup_audio_sample_rate = (setup.audio_sample_rate_44100 ? 44100 : 22050);
11463 // if setup and audio sample rate are already matching, nothing do do
11464 if ((setup_audio_sample_rate == audio.sample_rate) ||
11465 !audio.sound_available)
11469 // apparently changing the audio output sample rate does not work at runtime,
11470 // so currently the program has to be restarted to apply the new sample rate
11471 Request("Please restart the program to change audio sample rate!", REQ_CONFIRM);
11475 // set setup value according to successfully changed audio sample rate
11476 setup.audio_sample_rate_44100 = (audio.sample_rate == 44100);
11480 void ToggleFullscreenIfNeeded(void)
11482 // if setup and video fullscreen state are already matching, nothing do do
11483 if (setup.fullscreen == video.fullscreen_enabled ||
11484 !video.fullscreen_available)
11487 SDLSetWindowFullscreen(setup.fullscreen);
11489 // set setup value according to successfully changed fullscreen mode
11490 setup.fullscreen = video.fullscreen_enabled;
11493 void ChangeWindowScalingIfNeeded(void)
11495 // if setup and video window scaling are already matching, nothing do do
11496 if (setup.window_scaling_percent == video.window_scaling_percent ||
11497 video.fullscreen_enabled)
11500 SDLSetWindowScaling(setup.window_scaling_percent);
11502 // set setup value according to successfully changed window scaling
11503 setup.window_scaling_percent = video.window_scaling_percent;
11506 void ChangeVsyncModeIfNeeded(void)
11508 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
11509 int video_vsync_mode = video.vsync_mode;
11511 // if setup and video vsync mode are already matching, nothing do do
11512 if (setup_vsync_mode == video_vsync_mode)
11515 // if renderer is using OpenGL, vsync mode can directly be changed
11516 SDLSetScreenVsyncMode(setup.vsync_mode);
11518 // if vsync mode unchanged, try re-creating renderer to set vsync mode
11519 if (video.vsync_mode == video_vsync_mode)
11521 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
11523 // save backbuffer content which gets lost when re-creating screen
11524 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
11526 // force re-creating screen and renderer to set new vsync mode
11527 video.fullscreen_enabled = !setup.fullscreen;
11529 // when creating new renderer, destroy textures linked to old renderer
11530 FreeAllImageTextures(); // needs old renderer to free the textures
11532 // re-create screen and renderer (including change of vsync mode)
11533 ChangeVideoModeIfNeeded(setup.fullscreen);
11535 // set setup value according to successfully changed fullscreen mode
11536 setup.fullscreen = video.fullscreen_enabled;
11538 // restore backbuffer content from temporary backbuffer backup bitmap
11539 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
11540 FreeBitmap(tmp_backbuffer);
11542 // update visible window/screen
11543 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
11545 // when changing vsync mode, re-create textures for new renderer
11546 InitImageTextures();
11549 // set setup value according to successfully changed vsync mode
11550 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
11553 static void JoinRectangles(int *x, int *y, int *width, int *height,
11554 int x2, int y2, int width2, int height2)
11556 // do not join with "off-screen" rectangle
11557 if (x2 == -1 || y2 == -1)
11562 *width = MAX(*width, width2);
11563 *height = MAX(*height, height2);
11566 void SetAnimStatus(int anim_status_new)
11568 if (anim_status_new == GAME_MODE_MAIN)
11569 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
11570 else if (anim_status_new == GAME_MODE_NAMES)
11571 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
11572 else if (anim_status_new == GAME_MODE_SCORES)
11573 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
11575 global.anim_status_next = anim_status_new;
11577 // directly set screen modes that are entered without fading
11578 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
11579 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
11580 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
11581 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
11582 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
11583 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
11584 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
11585 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
11586 global.anim_status = global.anim_status_next;
11589 void SetGameStatus(int game_status_new)
11591 if (game_status_new != game_status)
11592 game_status_last_screen = game_status;
11594 game_status = game_status_new;
11596 SetAnimStatus(game_status_new);
11599 void SetFontStatus(int game_status_new)
11601 static int last_game_status = -1;
11603 if (game_status_new != -1)
11605 // set game status for font use after storing last game status
11606 last_game_status = game_status;
11607 game_status = game_status_new;
11611 // reset game status after font use from last stored game status
11612 game_status = last_game_status;
11616 void ResetFontStatus(void)
11621 void SetLevelSetInfo(char *identifier, int level_nr)
11623 setString(&levelset.identifier, identifier);
11625 levelset.level_nr = level_nr;
11628 boolean CheckIfAllViewportsHaveChanged(void)
11630 // if game status has not changed, viewports have not changed either
11631 if (game_status == game_status_last)
11634 // check if all viewports have changed with current game status
11636 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
11637 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
11638 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
11639 int new_real_sx = vp_playfield->x;
11640 int new_real_sy = vp_playfield->y;
11641 int new_full_sxsize = vp_playfield->width;
11642 int new_full_sysize = vp_playfield->height;
11643 int new_dx = vp_door_1->x;
11644 int new_dy = vp_door_1->y;
11645 int new_dxsize = vp_door_1->width;
11646 int new_dysize = vp_door_1->height;
11647 int new_vx = vp_door_2->x;
11648 int new_vy = vp_door_2->y;
11649 int new_vxsize = vp_door_2->width;
11650 int new_vysize = vp_door_2->height;
11652 boolean playfield_viewport_has_changed =
11653 (new_real_sx != REAL_SX ||
11654 new_real_sy != REAL_SY ||
11655 new_full_sxsize != FULL_SXSIZE ||
11656 new_full_sysize != FULL_SYSIZE);
11658 boolean door_1_viewport_has_changed =
11661 new_dxsize != DXSIZE ||
11662 new_dysize != DYSIZE);
11664 boolean door_2_viewport_has_changed =
11667 new_vxsize != VXSIZE ||
11668 new_vysize != VYSIZE ||
11669 game_status_last == GAME_MODE_EDITOR);
11671 return (playfield_viewport_has_changed &&
11672 door_1_viewport_has_changed &&
11673 door_2_viewport_has_changed);
11676 boolean CheckFadeAll(void)
11678 return (CheckIfGlobalBorderHasChanged() ||
11679 CheckIfAllViewportsHaveChanged());
11682 void ChangeViewportPropertiesIfNeeded(void)
11684 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
11685 FALSE : setup.small_game_graphics);
11686 int gfx_game_mode = getGlobalGameStatus(game_status);
11687 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
11689 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
11690 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
11691 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
11692 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
11693 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
11694 int new_win_xsize = vp_window->width;
11695 int new_win_ysize = vp_window->height;
11696 int border_left = vp_playfield->border_left;
11697 int border_right = vp_playfield->border_right;
11698 int border_top = vp_playfield->border_top;
11699 int border_bottom = vp_playfield->border_bottom;
11700 int new_sx = vp_playfield->x + border_left;
11701 int new_sy = vp_playfield->y + border_top;
11702 int new_sxsize = vp_playfield->width - border_left - border_right;
11703 int new_sysize = vp_playfield->height - border_top - border_bottom;
11704 int new_real_sx = vp_playfield->x;
11705 int new_real_sy = vp_playfield->y;
11706 int new_full_sxsize = vp_playfield->width;
11707 int new_full_sysize = vp_playfield->height;
11708 int new_dx = vp_door_1->x;
11709 int new_dy = vp_door_1->y;
11710 int new_dxsize = vp_door_1->width;
11711 int new_dysize = vp_door_1->height;
11712 int new_vx = vp_door_2->x;
11713 int new_vy = vp_door_2->y;
11714 int new_vxsize = vp_door_2->width;
11715 int new_vysize = vp_door_2->height;
11716 int new_ex = vp_door_3->x;
11717 int new_ey = vp_door_3->y;
11718 int new_exsize = vp_door_3->width;
11719 int new_eysize = vp_door_3->height;
11720 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
11721 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
11722 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
11723 int new_scr_fieldx = new_sxsize / tilesize;
11724 int new_scr_fieldy = new_sysize / tilesize;
11725 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
11726 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
11727 boolean init_gfx_buffers = FALSE;
11728 boolean init_video_buffer = FALSE;
11729 boolean init_gadgets_and_anims = FALSE;
11730 boolean init_bd_graphics = FALSE;
11731 boolean init_em_graphics = FALSE;
11733 if (new_win_xsize != WIN_XSIZE ||
11734 new_win_ysize != WIN_YSIZE)
11736 WIN_XSIZE = new_win_xsize;
11737 WIN_YSIZE = new_win_ysize;
11739 init_video_buffer = TRUE;
11740 init_gfx_buffers = TRUE;
11741 init_gadgets_and_anims = TRUE;
11743 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
11746 if (new_scr_fieldx != SCR_FIELDX ||
11747 new_scr_fieldy != SCR_FIELDY)
11749 // this always toggles between MAIN and GAME when using small tile size
11751 SCR_FIELDX = new_scr_fieldx;
11752 SCR_FIELDY = new_scr_fieldy;
11754 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
11757 if (new_sx != SX ||
11765 new_sxsize != SXSIZE ||
11766 new_sysize != SYSIZE ||
11767 new_dxsize != DXSIZE ||
11768 new_dysize != DYSIZE ||
11769 new_vxsize != VXSIZE ||
11770 new_vysize != VYSIZE ||
11771 new_exsize != EXSIZE ||
11772 new_eysize != EYSIZE ||
11773 new_real_sx != REAL_SX ||
11774 new_real_sy != REAL_SY ||
11775 new_full_sxsize != FULL_SXSIZE ||
11776 new_full_sysize != FULL_SYSIZE ||
11777 new_tilesize_var != TILESIZE_VAR
11780 // ------------------------------------------------------------------------
11781 // determine next fading area for changed viewport definitions
11782 // ------------------------------------------------------------------------
11784 // start with current playfield area (default fading area)
11787 FADE_SXSIZE = FULL_SXSIZE;
11788 FADE_SYSIZE = FULL_SYSIZE;
11790 // add new playfield area if position or size has changed
11791 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
11792 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
11794 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11795 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
11798 // add current and new door 1 area if position or size has changed
11799 if (new_dx != DX || new_dy != DY ||
11800 new_dxsize != DXSIZE || new_dysize != DYSIZE)
11802 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11803 DX, DY, DXSIZE, DYSIZE);
11804 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11805 new_dx, new_dy, new_dxsize, new_dysize);
11808 // add current and new door 2 area if position or size has changed
11809 if (new_vx != VX || new_vy != VY ||
11810 new_vxsize != VXSIZE || new_vysize != VYSIZE)
11812 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11813 VX, VY, VXSIZE, VYSIZE);
11814 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11815 new_vx, new_vy, new_vxsize, new_vysize);
11818 // ------------------------------------------------------------------------
11819 // handle changed tile size
11820 // ------------------------------------------------------------------------
11822 if (new_tilesize_var != TILESIZE_VAR)
11824 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
11826 // changing tile size invalidates scroll values of engine snapshots
11827 FreeEngineSnapshotSingle();
11829 // changing tile size requires update of graphic mapping for BD/EM engine
11830 init_bd_graphics = TRUE;
11831 init_em_graphics = TRUE;
11842 SXSIZE = new_sxsize;
11843 SYSIZE = new_sysize;
11844 DXSIZE = new_dxsize;
11845 DYSIZE = new_dysize;
11846 VXSIZE = new_vxsize;
11847 VYSIZE = new_vysize;
11848 EXSIZE = new_exsize;
11849 EYSIZE = new_eysize;
11850 REAL_SX = new_real_sx;
11851 REAL_SY = new_real_sy;
11852 FULL_SXSIZE = new_full_sxsize;
11853 FULL_SYSIZE = new_full_sysize;
11854 TILESIZE_VAR = new_tilesize_var;
11856 init_gfx_buffers = TRUE;
11857 init_gadgets_and_anims = TRUE;
11859 // Debug("tools:viewport", "viewports: init_gfx_buffers");
11860 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
11863 if (init_gfx_buffers)
11865 // Debug("tools:viewport", "init_gfx_buffers");
11867 SCR_FIELDX = new_scr_fieldx_buffers;
11868 SCR_FIELDY = new_scr_fieldy_buffers;
11872 SCR_FIELDX = new_scr_fieldx;
11873 SCR_FIELDY = new_scr_fieldy;
11875 SetDrawDeactivationMask(REDRAW_NONE);
11876 SetDrawBackgroundMask(REDRAW_FIELD);
11879 if (init_video_buffer)
11881 // Debug("tools:viewport", "init_video_buffer");
11883 FreeAllImageTextures(); // needs old renderer to free the textures
11885 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
11886 InitImageTextures();
11889 if (init_gadgets_and_anims)
11891 // Debug("tools:viewport", "init_gadgets_and_anims");
11894 InitGlobalAnimations();
11897 if (init_bd_graphics)
11899 InitGraphicInfo_BD();
11902 if (init_em_graphics)
11904 InitGraphicInfo_EM();
11908 void OpenURL(char *url)
11910 #if SDL_VERSION_ATLEAST(2,0,14)
11913 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
11914 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
11915 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
11919 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
11921 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
11924 char *getCurrentLevelsetName(void)
11926 return leveldir_current->name;
11930 // ============================================================================
11932 // ============================================================================
11934 #if defined(PLATFORM_WINDOWS)
11935 /* FILETIME of Jan 1 1970 00:00:00. */
11936 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
11939 * timezone information is stored outside the kernel so tzp isn't used anymore.
11941 * Note: this function is not for Win32 high precision timing purpose. See
11944 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
11946 FILETIME file_time;
11947 SYSTEMTIME system_time;
11948 ULARGE_INTEGER ularge;
11950 GetSystemTime(&system_time);
11951 SystemTimeToFileTime(&system_time, &file_time);
11952 ularge.LowPart = file_time.dwLowDateTime;
11953 ularge.HighPart = file_time.dwHighDateTime;
11955 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
11956 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
11962 static char *test_init_uuid_random_function_simple(void)
11964 static char seed_text[100];
11965 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
11967 sprintf(seed_text, "%d", seed);
11972 static char *test_init_uuid_random_function_better(void)
11974 static char seed_text[100];
11975 struct timeval current_time;
11977 gettimeofday(¤t_time, NULL);
11979 prng_seed_bytes(¤t_time, sizeof(current_time));
11981 sprintf(seed_text, "%ld.%ld",
11982 (long)current_time.tv_sec,
11983 (long)current_time.tv_usec);
11988 #if defined(PLATFORM_WINDOWS)
11989 static char *test_init_uuid_random_function_better_windows(void)
11991 static char seed_text[100];
11992 struct timeval current_time;
11994 gettimeofday_windows(¤t_time, NULL);
11996 prng_seed_bytes(¤t_time, sizeof(current_time));
11998 sprintf(seed_text, "%ld.%ld",
11999 (long)current_time.tv_sec,
12000 (long)current_time.tv_usec);
12006 static unsigned int test_uuid_random_function_simple(int max)
12008 return GetSimpleRandom(max);
12011 static unsigned int test_uuid_random_function_better(int max)
12013 return (max > 0 ? prng_get_uint() % max : 0);
12016 #if defined(PLATFORM_WINDOWS)
12017 #define NUM_UUID_TESTS 3
12019 #define NUM_UUID_TESTS 2
12022 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
12024 HashTable *hash_seeds =
12025 create_hashtable(get_hash_from_string, hash_key_strings_are_equal, free, NULL);
12026 HashTable *hash_uuids =
12027 create_hashtable(get_hash_from_string, hash_key_strings_are_equal, free, NULL);
12028 static char message[100];
12031 char *random_name = (nr == 0 ? "simple" : "better");
12032 char *random_type = (always_seed ? "always" : "only once");
12033 char *(*init_random_function)(void) =
12035 test_init_uuid_random_function_simple :
12036 test_init_uuid_random_function_better);
12037 unsigned int (*random_function)(int) =
12039 test_uuid_random_function_simple :
12040 test_uuid_random_function_better);
12043 #if defined(PLATFORM_WINDOWS)
12046 random_name = "windows";
12047 init_random_function = test_init_uuid_random_function_better_windows;
12053 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
12054 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
12056 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
12057 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
12058 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
12060 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
12064 // always initialize random number generator at least once
12065 init_random_function();
12067 unsigned int time_start = SDL_GetTicks();
12069 for (i = 0; i < num_uuids; i++)
12073 char *seed = getStringCopy(init_random_function());
12075 hashtable_remove(hash_seeds, seed);
12076 hashtable_insert(hash_seeds, seed, "1");
12079 char *uuid = getStringCopy(getUUIDExt(random_function));
12081 hashtable_remove(hash_uuids, uuid);
12082 hashtable_insert(hash_uuids, uuid, "1");
12085 int num_unique_seeds = hashtable_count(hash_seeds);
12086 int num_unique_uuids = hashtable_count(hash_uuids);
12088 unsigned int time_needed = SDL_GetTicks() - time_start;
12090 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
12092 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
12095 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
12097 if (nr == NUM_UUID_TESTS - 1 && always_seed)
12098 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
12100 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
12102 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
12104 Request(message, REQ_CONFIRM);
12106 hashtable_destroy(hash_seeds);
12107 hashtable_destroy(hash_uuids);
12110 void TestGeneratingUUIDs(void)
12112 int num_uuids = 1000000;
12115 for (i = 0; i < NUM_UUID_TESTS; i++)
12116 for (j = 0; j < 2; j++)
12117 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
12119 CloseAllAndExit(0);