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);
2821 int graphic = el2edimg(element);
2824 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2826 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2830 void DrawSizedElement(int x, int y, int element, int tilesize)
2832 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2835 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2837 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2840 void DrawMiniElement(int x, int y, int element)
2844 graphic = el2edimg(element);
2845 DrawMiniGraphic(x, y, graphic);
2848 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2851 int x = sx + scroll_x, y = sy + scroll_y;
2853 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2854 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2855 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2856 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2858 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2861 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2863 int x = sx + scroll_x, y = sy + scroll_y;
2865 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2866 DrawMiniElement(sx, sy, EL_EMPTY);
2867 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2868 DrawMiniElement(sx, sy, Tile[x][y]);
2870 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2873 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2874 int x, int y, int xsize, int ysize,
2875 int tile_width, int tile_height)
2879 int dst_x = startx + x * tile_width;
2880 int dst_y = starty + y * tile_height;
2881 int width = graphic_info[graphic].width;
2882 int height = graphic_info[graphic].height;
2883 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2884 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2885 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2886 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2887 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2888 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2889 boolean draw_masked = graphic_info[graphic].draw_masked;
2891 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2893 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2895 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2899 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2900 inner_sx + (x - 1) * tile_width % inner_width);
2901 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2902 inner_sy + (y - 1) * tile_height % inner_height);
2905 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2908 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2912 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2913 int x, int y, int xsize, int ysize,
2916 int font_width = getFontWidth(font_nr);
2917 int font_height = getFontHeight(font_nr);
2919 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2920 font_width, font_height);
2923 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2925 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2926 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2927 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2928 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2929 boolean no_delay = (tape.warp_forward);
2930 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2931 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2932 DelayCounter anim_delay = { anim_delay_value };
2933 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2934 int font_width = getFontWidth(font_nr);
2935 int font_height = getFontHeight(font_nr);
2936 int max_xsize = level.envelope[envelope_nr].xsize;
2937 int max_ysize = level.envelope[envelope_nr].ysize;
2938 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2939 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2940 int xend = max_xsize;
2941 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2942 int xstep = (xstart < xend ? 1 : 0);
2943 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2945 int end = MAX(xend - xstart, yend - ystart);
2948 for (i = start; i <= end; i++)
2950 int last_frame = end; // last frame of this "for" loop
2951 int x = xstart + i * xstep;
2952 int y = ystart + i * ystep;
2953 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2954 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2955 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2956 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2959 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2961 BlitScreenToBitmap(backbuffer);
2963 SetDrawtoField(DRAW_TO_BACKBUFFER);
2965 for (yy = 0; yy < ysize; yy++)
2966 for (xx = 0; xx < xsize; xx++)
2967 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2969 DrawTextArea(sx + font_width, sy + font_height,
2970 level.envelope[envelope_nr].text, font_nr, max_xsize,
2971 xsize - 2, ysize - 2, 0, mask_mode,
2972 level.envelope[envelope_nr].autowrap,
2973 level.envelope[envelope_nr].centered, FALSE);
2975 redraw_mask |= REDRAW_FIELD;
2978 SkipUntilDelayReached(&anim_delay, &i, last_frame);
2981 ClearAutoRepeatKeyEvents();
2984 void ShowEnvelope(int envelope_nr)
2986 int element = EL_ENVELOPE_1 + envelope_nr;
2987 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2988 int sound_opening = element_info[element].sound[ACTION_OPENING];
2989 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2990 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2991 boolean no_delay = (tape.warp_forward);
2992 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2993 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2994 int anim_mode = graphic_info[graphic].anim_mode;
2995 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2996 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2997 boolean overlay_enabled = GetOverlayEnabled();
2999 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3001 SetOverlayEnabled(FALSE);
3004 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3006 if (anim_mode == ANIM_DEFAULT)
3007 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
3009 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
3012 Delay_WithScreenUpdates(wait_delay_value);
3014 WaitForEventToContinue();
3017 SetOverlayEnabled(overlay_enabled);
3019 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3021 if (anim_mode != ANIM_NONE)
3022 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
3024 if (anim_mode == ANIM_DEFAULT)
3025 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
3027 game.envelope_active = FALSE;
3029 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3031 redraw_mask |= REDRAW_FIELD;
3035 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
3036 int xsize, int ysize)
3038 if (!global.use_envelope_request)
3041 if (request.bitmap == NULL ||
3042 xsize > request.xsize ||
3043 ysize > request.ysize)
3045 if (request.bitmap != NULL)
3046 FreeBitmap(request.bitmap);
3048 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
3050 SDL_Surface *surface = request.bitmap->surface;
3052 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
3053 Fail("SDLGetNativeSurface() failed");
3056 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3058 // create masked surface for request bitmap, if needed
3059 if (graphic_info[IMG_BACKGROUND_REQUEST].draw_masked)
3061 SDL_Surface *surface = request.bitmap->surface;
3062 SDL_Surface *surface_masked = request.bitmap->surface_masked;
3064 SDLBlitSurface(surface, surface_masked, 0, 0, xsize, ysize, 0, 0);
3065 SDL_SetColorKey(surface_masked, SET_TRANSPARENT_PIXEL,
3066 SDL_MapRGB(surface_masked->format, 0x00, 0x00, 0x00));
3069 SDLFreeBitmapTextures(request.bitmap);
3070 SDLCreateBitmapTextures(request.bitmap);
3072 ResetBitmapAlpha(request.bitmap);
3074 // set envelope request run-time values
3077 request.xsize = xsize;
3078 request.ysize = ysize;
3081 void DrawEnvelopeRequestToScreen(int drawing_target)
3083 if (global.use_envelope_request &&
3084 game.request_active &&
3085 drawing_target == DRAW_TO_SCREEN)
3087 struct GraphicInfo *g = &graphic_info[IMG_BACKGROUND_REQUEST];
3089 SetBitmapAlphaNextBlit(request.bitmap, g->alpha);
3092 BlitToScreenMasked(request.bitmap, 0, 0, request.xsize, request.ysize,
3093 request.sx, request.sy);
3095 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3096 request.sx, request.sy);
3100 static void setRequestBasePosition(int *x, int *y)
3102 int sx_base, sy_base;
3104 if (request.x != -1)
3105 sx_base = request.x;
3106 else if (request.align == ALIGN_LEFT)
3108 else if (request.align == ALIGN_RIGHT)
3109 sx_base = SX + SXSIZE;
3111 sx_base = SX + SXSIZE / 2;
3113 if (request.y != -1)
3114 sy_base = request.y;
3115 else if (request.valign == VALIGN_TOP)
3117 else if (request.valign == VALIGN_BOTTOM)
3118 sy_base = SY + SYSIZE;
3120 sy_base = SY + SYSIZE / 2;
3126 static void setRequestPositionExt(int *x, int *y, int width, int height,
3127 boolean add_border_size)
3129 int border_size = request.border_size;
3130 int sx_base, sy_base;
3133 setRequestBasePosition(&sx_base, &sy_base);
3135 if (request.align == ALIGN_LEFT)
3137 else if (request.align == ALIGN_RIGHT)
3138 sx = sx_base - width;
3140 sx = sx_base - width / 2;
3142 if (request.valign == VALIGN_TOP)
3144 else if (request.valign == VALIGN_BOTTOM)
3145 sy = sy_base - height;
3147 sy = sy_base - height / 2;
3149 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3150 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3152 if (add_border_size)
3162 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3164 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3167 static void DrawEnvelopeRequestText(int sx, int sy, char *text)
3169 char *text_final = text;
3170 char *text_door_style = NULL;
3171 int graphic = IMG_BACKGROUND_REQUEST;
3172 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3173 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3174 int font_nr = FONT_REQUEST;
3175 int font_width = getFontWidth(font_nr);
3176 int font_height = getFontHeight(font_nr);
3177 int border_size = request.border_size;
3178 int line_spacing = request.line_spacing;
3179 int line_height = font_height + line_spacing;
3180 int max_text_width = request.width - 2 * border_size;
3181 int max_text_height = request.height - 2 * border_size;
3182 int line_length = max_text_width / font_width;
3183 int max_lines = max_text_height / line_height;
3184 int text_width = line_length * font_width;
3185 int sx_offset = border_size;
3186 int sy_offset = border_size;
3188 // force DOOR font inside door area
3189 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3191 if (request.centered)
3192 sx_offset = (request.width - text_width) / 2;
3194 if (request.wrap_single_words && !request.autowrap)
3196 char *src_text_ptr, *dst_text_ptr;
3198 if (maxWordLengthInRequestString(text) > line_length)
3200 font_nr = FONT_REQUEST_NARROW;
3201 font_width = getFontWidth(font_nr);
3202 line_length = max_text_width / font_width;
3205 text_door_style = checked_malloc(2 * strlen(text) + 1);
3207 src_text_ptr = text;
3208 dst_text_ptr = text_door_style;
3210 while (*src_text_ptr)
3212 if (*src_text_ptr == ' ' ||
3213 *src_text_ptr == '?' ||
3214 *src_text_ptr == '!')
3215 *dst_text_ptr++ = '\n';
3217 if (*src_text_ptr != ' ')
3218 *dst_text_ptr++ = *src_text_ptr;
3223 *dst_text_ptr = '\0';
3225 text_final = text_door_style;
3228 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3229 line_length, -1, max_lines, line_spacing, mask_mode,
3230 request.autowrap, request.centered, FALSE);
3232 if (text_door_style)
3233 free(text_door_style);
3238 static void DrawEnvelopeRequest(char *text, unsigned int req_state)
3240 DrawBuffer *drawto_last = drawto;
3241 int graphic = IMG_BACKGROUND_REQUEST;
3242 int width = request.width;
3243 int height = request.height;
3244 int tile_size = MAX(request.step_offset, 1);
3245 int x_steps = width / tile_size;
3246 int y_steps = height / tile_size;
3250 setRequestPosition(&sx, &sy, FALSE);
3252 // draw complete envelope request to temporary bitmap
3253 drawto = bitmap_db_store_1;
3255 ClearRectangle(drawto, sx, sy, width, height);
3257 for (y = 0; y < y_steps; y++)
3258 for (x = 0; x < x_steps; x++)
3259 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3260 x, y, x_steps, y_steps,
3261 tile_size, tile_size);
3263 // write text for request
3264 DrawEnvelopeRequestText(sx, sy, text);
3266 MapToolButtons(req_state);
3268 // restore pointer to drawing buffer
3269 drawto = drawto_last;
3271 // prepare complete envelope request from temporary bitmap
3272 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy, width, height);
3275 static void AnimateEnvelopeRequest(int anim_mode, int action)
3277 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
3278 int delay_value_normal = request.step_delay;
3279 int delay_value_fast = delay_value_normal / 2;
3280 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3281 boolean no_delay = (tape.warp_forward);
3282 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3283 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value) / 2);
3284 DelayCounter anim_delay = { anim_delay_value };
3286 int tile_size = MAX(request.step_offset, 1);
3287 int max_xsize = request.width / tile_size;
3288 int max_ysize = request.height / tile_size;
3289 int max_xsize_inner = max_xsize - 2;
3290 int max_ysize_inner = max_ysize - 2;
3292 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3293 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3294 int xend = max_xsize_inner;
3295 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3296 int xstep = (xstart < xend ? 1 : 0);
3297 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3299 int end = MAX(xend - xstart, yend - ystart);
3302 if (setup.quick_doors)
3309 for (i = start; i <= end; i++)
3311 int last_frame = end; // last frame of this "for" loop
3312 int x = xstart + i * xstep;
3313 int y = ystart + i * ystep;
3314 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3315 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3316 int xsize_size_left = (xsize - 1) * tile_size;
3317 int ysize_size_top = (ysize - 1) * tile_size;
3318 int max_xsize_pos = (max_xsize - 1) * tile_size;
3319 int max_ysize_pos = (max_ysize - 1) * tile_size;
3320 int width = xsize * tile_size;
3321 int height = ysize * tile_size;
3327 HandleGameActions();
3329 setRequestPosition(&src_x, &src_y, FALSE);
3330 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3332 for (yy = 0; yy < 2; yy++)
3334 for (xx = 0; xx < 2; xx++)
3336 int src_xx = src_x + xx * max_xsize_pos;
3337 int src_yy = src_y + yy * max_ysize_pos;
3338 int dst_xx = dst_x + xx * xsize_size_left;
3339 int dst_yy = dst_y + yy * ysize_size_top;
3340 int xx_size = (xx ? tile_size : xsize_size_left);
3341 int yy_size = (yy ? tile_size : ysize_size_top);
3343 // draw partial (animated) envelope request to temporary bitmap
3344 BlitBitmap(bitmap_db_store_1, bitmap_db_store_2,
3345 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3349 // prepare partial (animated) envelope request from temporary bitmap
3350 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, dst_x, dst_y,
3353 redraw_mask |= REDRAW_FIELD;
3357 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3360 ClearAutoRepeatKeyEvents();
3363 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3365 int graphic = IMG_BACKGROUND_REQUEST;
3366 int sound_opening = SND_REQUEST_OPENING;
3367 int sound_closing = SND_REQUEST_CLOSING;
3368 int anim_mode_1 = request.anim_mode; // (higher priority)
3369 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3370 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3371 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3372 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3374 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3376 if (action == ACTION_OPENING)
3378 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3380 if (anim_mode == ANIM_DEFAULT)
3381 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3383 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3387 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3389 if (anim_mode != ANIM_NONE)
3390 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3392 if (anim_mode == ANIM_DEFAULT)
3393 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3396 game.envelope_active = FALSE;
3399 static Bitmap *GetPreviewTileBitmap(Bitmap *bitmap)
3401 if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
3402 return GetPreviewTileBitmap_BD(bitmap);
3407 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3409 if (IS_MM_WALL(element))
3411 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3417 int graphic = el2preimg(element);
3419 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3421 // for BD style levels, maybe use bitmap with level-specific colors
3422 src_bitmap = GetPreviewTileBitmap(src_bitmap);
3424 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3429 void DrawLevel(int draw_background_mask)
3433 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3434 SetDrawBackgroundMask(draw_background_mask);
3438 for (x = BX1; x <= BX2; x++)
3439 for (y = BY1; y <= BY2; y++)
3440 DrawScreenField(x, y);
3442 redraw_mask |= REDRAW_FIELD;
3445 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3450 for (x = 0; x < size_x; x++)
3451 for (y = 0; y < size_y; y++)
3452 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3454 redraw_mask |= REDRAW_FIELD;
3457 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3461 for (x = 0; x < size_x; x++)
3462 for (y = 0; y < size_y; y++)
3463 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3465 redraw_mask |= REDRAW_FIELD;
3468 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3470 boolean show_level_border = (BorderElement != EL_EMPTY);
3471 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3472 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3473 int tile_size = preview.tile_size;
3474 int preview_width = preview.xsize * tile_size;
3475 int preview_height = preview.ysize * tile_size;
3476 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3477 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3478 int real_preview_width = real_preview_xsize * tile_size;
3479 int real_preview_height = real_preview_ysize * tile_size;
3480 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3481 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3484 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3487 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3489 dst_x += (preview_width - real_preview_width) / 2;
3490 dst_y += (preview_height - real_preview_height) / 2;
3492 for (x = 0; x < real_preview_xsize; x++)
3494 for (y = 0; y < real_preview_ysize; y++)
3496 int lx = from_x + x + (show_level_border ? -1 : 0);
3497 int ly = from_y + y + (show_level_border ? -1 : 0);
3498 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3499 getBorderElement(lx, ly));
3501 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3502 element, tile_size);
3506 redraw_mask |= REDRAW_FIELD;
3509 #define MICROLABEL_EMPTY 0
3510 #define MICROLABEL_LEVEL_NAME 1
3511 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3512 #define MICROLABEL_LEVEL_AUTHOR 3
3513 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3514 #define MICROLABEL_IMPORTED_FROM 5
3515 #define MICROLABEL_IMPORTED_BY_HEAD 6
3516 #define MICROLABEL_IMPORTED_BY 7
3518 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3520 int max_text_width = SXSIZE;
3521 int font_width = getFontWidth(font_nr);
3523 if (pos->align == ALIGN_CENTER)
3524 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3525 else if (pos->align == ALIGN_RIGHT)
3526 max_text_width = pos->x;
3528 max_text_width = SXSIZE - pos->x;
3530 return max_text_width / font_width;
3533 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3535 char label_text[MAX_OUTPUT_LINESIZE + 1];
3536 int max_len_label_text;
3537 int font_nr = pos->font;
3540 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3543 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3544 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3545 mode == MICROLABEL_IMPORTED_BY_HEAD)
3546 font_nr = pos->font_alt;
3548 max_len_label_text = getMaxTextLength(pos, font_nr);
3550 if (pos->size != -1)
3551 max_len_label_text = pos->size;
3553 for (i = 0; i < max_len_label_text; i++)
3554 label_text[i] = ' ';
3555 label_text[max_len_label_text] = '\0';
3557 if (strlen(label_text) > 0)
3558 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3561 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3562 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3563 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3564 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3565 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3566 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3567 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3568 max_len_label_text);
3569 label_text[max_len_label_text] = '\0';
3571 if (strlen(label_text) > 0)
3572 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3574 redraw_mask |= REDRAW_FIELD;
3577 static void DrawPreviewLevelLabel(int mode)
3579 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3582 static void DrawPreviewLevelInfo(int mode)
3584 if (mode == MICROLABEL_LEVEL_NAME)
3585 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3586 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3587 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3590 static void DrawPreviewLevelExt(boolean restart)
3592 static DelayCounter scroll_delay = { 0 };
3593 static DelayCounter label_delay = { 0 };
3594 static int from_x, from_y, scroll_direction;
3595 static int label_state, label_counter;
3596 boolean show_level_border = (BorderElement != EL_EMPTY);
3597 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3598 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3600 scroll_delay.value = preview.step_delay;
3601 label_delay.value = MICROLEVEL_LABEL_DELAY;
3608 if (preview.anim_mode == ANIM_CENTERED)
3610 if (level_xsize > preview.xsize)
3611 from_x = (level_xsize - preview.xsize) / 2;
3612 if (level_ysize > preview.ysize)
3613 from_y = (level_ysize - preview.ysize) / 2;
3616 from_x += preview.xoffset;
3617 from_y += preview.yoffset;
3619 scroll_direction = MV_RIGHT;
3623 DrawPreviewLevelPlayfield(from_x, from_y);
3624 DrawPreviewLevelLabel(label_state);
3626 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3627 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3629 // initialize delay counters
3630 ResetDelayCounter(&scroll_delay);
3631 ResetDelayCounter(&label_delay);
3633 if (leveldir_current->name)
3635 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3636 char label_text[MAX_OUTPUT_LINESIZE + 1];
3637 int font_nr = pos->font;
3638 int max_len_label_text = getMaxTextLength(pos, font_nr);
3640 if (pos->size != -1)
3641 max_len_label_text = pos->size;
3643 strncpy(label_text, leveldir_current->name, max_len_label_text);
3644 label_text[max_len_label_text] = '\0';
3646 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3647 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3653 // scroll preview level, if needed
3654 if (preview.anim_mode != ANIM_NONE &&
3655 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3656 DelayReached(&scroll_delay))
3658 switch (scroll_direction)
3663 from_x -= preview.step_offset;
3664 from_x = (from_x < 0 ? 0 : from_x);
3667 scroll_direction = MV_UP;
3671 if (from_x < level_xsize - preview.xsize)
3673 from_x += preview.step_offset;
3674 from_x = (from_x > level_xsize - preview.xsize ?
3675 level_xsize - preview.xsize : from_x);
3678 scroll_direction = MV_DOWN;
3684 from_y -= preview.step_offset;
3685 from_y = (from_y < 0 ? 0 : from_y);
3688 scroll_direction = MV_RIGHT;
3692 if (from_y < level_ysize - preview.ysize)
3694 from_y += preview.step_offset;
3695 from_y = (from_y > level_ysize - preview.ysize ?
3696 level_ysize - preview.ysize : from_y);
3699 scroll_direction = MV_LEFT;
3706 DrawPreviewLevelPlayfield(from_x, from_y);
3709 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3710 // redraw micro level label, if needed
3711 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3712 !strEqual(level.author, ANONYMOUS_NAME) &&
3713 !strEqual(level.author, leveldir_current->name) &&
3714 DelayReached(&label_delay))
3716 int max_label_counter = 23;
3718 if (leveldir_current->imported_from != NULL &&
3719 strlen(leveldir_current->imported_from) > 0)
3720 max_label_counter += 14;
3721 if (leveldir_current->imported_by != NULL &&
3722 strlen(leveldir_current->imported_by) > 0)
3723 max_label_counter += 14;
3725 label_counter = (label_counter + 1) % max_label_counter;
3726 label_state = (label_counter >= 0 && label_counter <= 7 ?
3727 MICROLABEL_LEVEL_NAME :
3728 label_counter >= 9 && label_counter <= 12 ?
3729 MICROLABEL_LEVEL_AUTHOR_HEAD :
3730 label_counter >= 14 && label_counter <= 21 ?
3731 MICROLABEL_LEVEL_AUTHOR :
3732 label_counter >= 23 && label_counter <= 26 ?
3733 MICROLABEL_IMPORTED_FROM_HEAD :
3734 label_counter >= 28 && label_counter <= 35 ?
3735 MICROLABEL_IMPORTED_FROM :
3736 label_counter >= 37 && label_counter <= 40 ?
3737 MICROLABEL_IMPORTED_BY_HEAD :
3738 label_counter >= 42 && label_counter <= 49 ?
3739 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3741 if (leveldir_current->imported_from == NULL &&
3742 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3743 label_state == MICROLABEL_IMPORTED_FROM))
3744 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3745 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3747 DrawPreviewLevelLabel(label_state);
3751 void DrawPreviewPlayers(void)
3753 if (game_status != GAME_MODE_MAIN)
3756 // do not draw preview players if level preview redefined, but players aren't
3757 if (preview.redefined && !menu.main.preview_players.redefined)
3760 boolean player_found[MAX_PLAYERS];
3761 int num_players = 0;
3764 for (i = 0; i < MAX_PLAYERS; i++)
3765 player_found[i] = FALSE;
3767 // check which players can be found in the level (simple approach)
3768 for (x = 0; x < lev_fieldx; x++)
3770 for (y = 0; y < lev_fieldy; y++)
3772 int element = level.field[x][y];
3774 if (IS_PLAYER_ELEMENT(element))
3776 int player_nr = GET_PLAYER_NR(element);
3778 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3780 if (!player_found[player_nr])
3783 player_found[player_nr] = TRUE;
3788 struct TextPosInfo *pos = &menu.main.preview_players;
3789 int tile_size = pos->tile_size;
3790 int border_size = pos->border_size;
3791 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3792 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3793 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3794 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3795 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3796 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3797 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3798 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3799 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3800 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3801 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3802 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3804 // clear area in which the players will be drawn
3805 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3806 max_players_width, max_players_height);
3808 if (!network.enabled && !setup.team_mode)
3811 // only draw players if level is suited for team mode
3812 if (num_players < 2)
3815 // draw all players that were found in the level
3816 for (i = 0; i < MAX_PLAYERS; i++)
3818 if (player_found[i])
3820 int graphic = el2img(EL_PLAYER_1 + i);
3822 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3824 xpos += player_xoffset;
3825 ypos += player_yoffset;
3830 static void PreparePreviewTileBitmap(void)
3832 // check if special preview bitmap with level-specific colors should be created
3833 if (level.game_engine_type != GAME_ENGINE_TYPE_BD)
3836 // use original sized bitmap (else reduced color palette is lost by downscaling)
3837 int original_tilesize = MAX(MINI_TILESIZE, preview.tile_size);
3838 int scale_down_factor = original_tilesize / preview.tile_size;
3841 int element = EL_BD_ROCK;
3842 int graphic = el2preimg(element);
3844 // create special preview bitmap and scale it down to preview tile size
3845 getSizedGraphicSource(graphic, 0, original_tilesize, &src_bitmap, &src_x, &src_y);
3846 PreparePreviewTileBitmap_BD(src_bitmap, scale_down_factor);
3848 // force using special preview bitmap to replace original preview bitmap
3849 getSizedGraphicSource(graphic, 0, preview.tile_size, &src_bitmap, &src_x, &src_y);
3850 SetPreviewTileBitmapReference_BD(src_bitmap);
3853 void DrawPreviewLevelInitial(void)
3855 PreparePreviewTileBitmap(); // only needed for native BD style levels
3857 DrawPreviewLevelExt(TRUE);
3858 DrawPreviewPlayers();
3861 void DrawPreviewLevelAnimation(void)
3863 DrawPreviewLevelExt(FALSE);
3866 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3867 int border_size, int font_nr)
3869 int graphic = el2img(EL_PLAYER_1 + player_nr);
3870 int font_height = getFontHeight(font_nr);
3871 int player_height = MAX(tile_size, font_height);
3872 int xoffset_text = tile_size + border_size;
3873 int yoffset_text = (player_height - font_height) / 2;
3874 int yoffset_graphic = (player_height - tile_size) / 2;
3875 char *player_name = getNetworkPlayerName(player_nr + 1);
3877 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3879 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3882 static void DrawNetworkPlayersExt(boolean force)
3884 if (game_status != GAME_MODE_MAIN)
3887 if (!network.connected && !force)
3890 // do not draw network players if level preview redefined, but players aren't
3891 if (preview.redefined && !menu.main.network_players.redefined)
3894 int num_players = 0;
3897 for (i = 0; i < MAX_PLAYERS; i++)
3898 if (stored_player[i].connected_network)
3901 struct TextPosInfo *pos = &menu.main.network_players;
3902 int tile_size = pos->tile_size;
3903 int border_size = pos->border_size;
3904 int xoffset_text = tile_size + border_size;
3905 int font_nr = pos->font;
3906 int font_width = getFontWidth(font_nr);
3907 int font_height = getFontHeight(font_nr);
3908 int player_height = MAX(tile_size, font_height);
3909 int player_yoffset = player_height + border_size;
3910 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3911 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3912 int all_players_height = num_players * player_yoffset - border_size;
3913 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3914 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3915 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3917 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3918 max_players_width, max_players_height);
3920 // first draw local network player ...
3921 for (i = 0; i < MAX_PLAYERS; i++)
3923 if (stored_player[i].connected_network &&
3924 stored_player[i].connected_locally)
3926 char *player_name = getNetworkPlayerName(i + 1);
3927 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3928 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3930 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3932 ypos += player_yoffset;
3936 // ... then draw all other network players
3937 for (i = 0; i < MAX_PLAYERS; i++)
3939 if (stored_player[i].connected_network &&
3940 !stored_player[i].connected_locally)
3942 char *player_name = getNetworkPlayerName(i + 1);
3943 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3944 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3946 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3948 ypos += player_yoffset;
3953 void DrawNetworkPlayers(void)
3955 DrawNetworkPlayersExt(FALSE);
3958 void ClearNetworkPlayers(void)
3960 DrawNetworkPlayersExt(TRUE);
3963 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3964 int graphic, int lx, int ly,
3967 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3969 if (mask_mode == USE_MASKING)
3970 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3972 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3975 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3976 int graphic, int sync_frame, int mask_mode)
3978 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3980 if (mask_mode == USE_MASKING)
3981 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3983 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3986 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3987 int graphic, int sync_frame, int tilesize,
3990 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3992 if (mask_mode == USE_MASKING)
3993 DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
3995 DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
3998 static void DrawGraphicAnimation(int x, int y, int graphic)
4000 int lx = LEVELX(x), ly = LEVELY(y);
4001 int mask_mode = NO_MASKING;
4003 if (!IN_SCR_FIELD(x, y))
4006 if (game.use_masked_elements)
4008 if (Tile[lx][ly] != EL_EMPTY)
4010 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
4012 mask_mode = USE_MASKING;
4016 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
4017 graphic, lx, ly, mask_mode);
4019 MarkTileDirty(x, y);
4022 void DrawFixedGraphicAnimation(int x, int y, int graphic)
4024 int lx = LEVELX(x), ly = LEVELY(y);
4025 int mask_mode = NO_MASKING;
4027 if (!IN_SCR_FIELD(x, y))
4030 if (game.use_masked_elements)
4032 if (Tile[lx][ly] != EL_EMPTY)
4034 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
4036 mask_mode = USE_MASKING;
4040 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
4041 graphic, lx, ly, mask_mode);
4043 MarkTileDirty(x, y);
4046 void DrawLevelGraphicAnimation(int x, int y, int graphic)
4048 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4051 void DrawLevelElementAnimation(int x, int y, int element)
4053 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4055 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4058 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
4060 int sx = SCREENX(x), sy = SCREENY(y);
4062 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4065 if (Tile[x][y] == EL_EMPTY)
4066 graphic = el2img(GfxElementEmpty[x][y]);
4068 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4071 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4074 DrawGraphicAnimation(sx, sy, graphic);
4077 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4078 DrawLevelFieldCrumbled(x, y);
4080 if (GFX_CRUMBLED(Tile[x][y]))
4081 DrawLevelFieldCrumbled(x, y);
4085 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4087 int sx = SCREENX(x), sy = SCREENY(y);
4090 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4093 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4095 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4098 DrawGraphicAnimation(sx, sy, graphic);
4100 if (GFX_CRUMBLED(element))
4101 DrawLevelFieldCrumbled(x, y);
4104 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4106 if (player->use_murphy)
4108 // this works only because currently only one player can be "murphy" ...
4109 static int last_horizontal_dir = MV_LEFT;
4110 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4112 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4113 last_horizontal_dir = move_dir;
4115 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4117 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4119 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4125 return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4128 static boolean equalGraphics(int graphic1, int graphic2)
4130 struct GraphicInfo *g1 = &graphic_info[graphic1];
4131 struct GraphicInfo *g2 = &graphic_info[graphic2];
4133 return (g1->bitmap == g2->bitmap &&
4134 g1->src_x == g2->src_x &&
4135 g1->src_y == g2->src_y &&
4136 g1->anim_frames == g2->anim_frames &&
4137 g1->anim_delay == g2->anim_delay &&
4138 g1->anim_mode == g2->anim_mode);
4141 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4145 DRAW_PLAYER_STAGE_INIT = 0,
4146 DRAW_PLAYER_STAGE_LAST_FIELD,
4147 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4148 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4149 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4150 DRAW_PLAYER_STAGE_PLAYER,
4152 DRAW_PLAYER_STAGE_PLAYER,
4153 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4155 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4156 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4158 NUM_DRAW_PLAYER_STAGES
4161 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4163 static int static_last_player_graphic[MAX_PLAYERS];
4164 static int static_last_player_frame[MAX_PLAYERS];
4165 static boolean static_player_is_opaque[MAX_PLAYERS];
4166 static boolean draw_player[MAX_PLAYERS];
4167 int pnr = player->index_nr;
4169 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4171 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4172 static_last_player_frame[pnr] = player->Frame;
4173 static_player_is_opaque[pnr] = FALSE;
4175 draw_player[pnr] = TRUE;
4178 if (!draw_player[pnr])
4182 if (!IN_LEV_FIELD(player->jx, player->jy))
4184 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4185 Debug("draw:DrawPlayerExt", "This should never happen!");
4187 draw_player[pnr] = FALSE;
4193 int last_player_graphic = static_last_player_graphic[pnr];
4194 int last_player_frame = static_last_player_frame[pnr];
4195 boolean player_is_opaque = static_player_is_opaque[pnr];
4197 int jx = player->jx;
4198 int jy = player->jy;
4199 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4200 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4201 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4202 int last_jx = (player->is_moving ? jx - dx : jx);
4203 int last_jy = (player->is_moving ? jy - dy : jy);
4204 int next_jx = jx + dx;
4205 int next_jy = jy + dy;
4206 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4207 int sx = SCREENX(jx);
4208 int sy = SCREENY(jy);
4209 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4210 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4211 int element = Tile[jx][jy];
4212 int last_element = Tile[last_jx][last_jy];
4213 int action = (player->is_pushing ? ACTION_PUSHING :
4214 player->is_digging ? ACTION_DIGGING :
4215 player->is_collecting ? ACTION_COLLECTING :
4216 player->is_moving ? ACTION_MOVING :
4217 player->is_snapping ? ACTION_SNAPPING :
4218 player->is_dropping ? ACTION_DROPPING :
4219 player->is_waiting ? player->action_waiting :
4222 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4224 // ------------------------------------------------------------------------
4225 // initialize drawing the player
4226 // ------------------------------------------------------------------------
4228 draw_player[pnr] = FALSE;
4230 // GfxElement[][] is set to the element the player is digging or collecting;
4231 // remove also for off-screen player if the player is not moving anymore
4232 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4233 GfxElement[jx][jy] = EL_UNDEFINED;
4235 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4238 if (element == EL_EXPLOSION)
4241 InitPlayerGfxAnimation(player, action, move_dir);
4243 draw_player[pnr] = TRUE;
4245 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4247 // ------------------------------------------------------------------------
4248 // draw things in the field the player is leaving, if needed
4249 // ------------------------------------------------------------------------
4251 if (!IN_SCR_FIELD(sx, sy))
4252 draw_player[pnr] = FALSE;
4254 if (!player->is_moving)
4257 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4259 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4261 if (last_element == EL_DYNAMITE_ACTIVE ||
4262 last_element == EL_EM_DYNAMITE_ACTIVE ||
4263 last_element == EL_SP_DISK_RED_ACTIVE)
4264 DrawDynamite(last_jx, last_jy);
4266 DrawLevelFieldThruMask(last_jx, last_jy);
4268 else if (last_element == EL_DYNAMITE_ACTIVE ||
4269 last_element == EL_EM_DYNAMITE_ACTIVE ||
4270 last_element == EL_SP_DISK_RED_ACTIVE)
4271 DrawDynamite(last_jx, last_jy);
4273 DrawLevelField(last_jx, last_jy);
4275 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4277 // ------------------------------------------------------------------------
4278 // draw things behind the player, if needed
4279 // ------------------------------------------------------------------------
4283 DrawLevelElement(jx, jy, Back[jx][jy]);
4288 if (IS_ACTIVE_BOMB(element))
4290 DrawLevelElement(jx, jy, EL_EMPTY);
4295 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4297 int old_element = GfxElement[jx][jy];
4298 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4299 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4301 if (GFX_CRUMBLED(old_element))
4302 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4304 DrawScreenGraphic(sx, sy, old_graphic, frame);
4306 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4307 static_player_is_opaque[pnr] = TRUE;
4311 GfxElement[jx][jy] = EL_UNDEFINED;
4313 // make sure that pushed elements are drawn with correct frame rate
4314 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4316 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4317 GfxFrame[jx][jy] = player->StepFrame;
4319 DrawLevelField(jx, jy);
4322 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4324 // ------------------------------------------------------------------------
4325 // draw things the player is pushing, if needed
4326 // ------------------------------------------------------------------------
4328 if (!player->is_pushing || !player->is_moving)
4331 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4334 int gfx_frame = GfxFrame[jx][jy];
4336 if (!IS_MOVING(jx, jy)) // push movement already finished
4338 element = Tile[next_jx][next_jy];
4339 gfx_frame = GfxFrame[next_jx][next_jy];
4342 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4343 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4344 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4346 // draw background element under pushed element (like the Sokoban field)
4347 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4349 // this allows transparent pushing animation over non-black background
4352 DrawLevelElement(jx, jy, Back[jx][jy]);
4354 DrawLevelElement(jx, jy, EL_EMPTY);
4357 if (Back[next_jx][next_jy])
4358 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4360 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4362 int px = SCREENX(jx), py = SCREENY(jy);
4363 int pxx = (TILEX - ABS(sxx)) * dx;
4364 int pyy = (TILEY - ABS(syy)) * dy;
4367 // do not draw (EM style) pushing animation when pushing is finished
4368 // (two-tile animations usually do not contain start and end frame)
4369 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4370 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4372 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4374 // masked drawing is needed for EMC style (double) movement graphics
4375 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4376 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4379 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4381 // ------------------------------------------------------------------------
4382 // draw player himself
4383 // ------------------------------------------------------------------------
4385 int graphic = getPlayerGraphic(player, move_dir);
4387 // in the case of changed player action or direction, prevent the current
4388 // animation frame from being restarted for identical animations
4389 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4390 player->Frame = last_player_frame;
4392 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4394 if (player_is_opaque)
4395 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4397 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4399 if (SHIELD_ON(player))
4401 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4402 IMG_SHIELD_NORMAL_ACTIVE);
4403 frame = getGraphicAnimationFrame(graphic, -1);
4405 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4408 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4410 // ------------------------------------------------------------------------
4411 // draw things in front of player (active dynamite or dynabombs)
4412 // ------------------------------------------------------------------------
4414 if (IS_ACTIVE_BOMB(element))
4416 int graphic = el2img(element);
4417 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4419 if (game.emulation == EMU_SUPAPLEX)
4420 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4422 DrawGraphicThruMask(sx, sy, graphic, frame);
4425 if (player_is_moving && last_element == EL_EXPLOSION)
4427 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4428 GfxElement[last_jx][last_jy] : EL_EMPTY);
4429 int graphic = el_act2img(element, ACTION_EXPLODING);
4430 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4431 int phase = ExplodePhase[last_jx][last_jy] - 1;
4432 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4435 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4438 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4440 // ------------------------------------------------------------------------
4441 // draw elements the player is just walking/passing through/under
4442 // ------------------------------------------------------------------------
4444 if (player_is_moving)
4446 // handle the field the player is leaving ...
4447 if (IS_ACCESSIBLE_INSIDE(last_element))
4448 DrawLevelField(last_jx, last_jy);
4449 else if (IS_ACCESSIBLE_UNDER(last_element))
4450 DrawLevelFieldThruMask(last_jx, last_jy);
4453 // do not redraw accessible elements if the player is just pushing them
4454 if (!player_is_moving || !player->is_pushing)
4456 // ... and the field the player is entering
4457 if (IS_ACCESSIBLE_INSIDE(element))
4458 DrawLevelField(jx, jy);
4459 else if (IS_ACCESSIBLE_UNDER(element))
4460 DrawLevelFieldThruMask(jx, jy);
4463 MarkTileDirty(sx, sy);
4467 void DrawPlayer(struct PlayerInfo *player)
4471 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4472 DrawPlayerExt(player, i);
4475 void DrawAllPlayers(void)
4479 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4480 for (j = 0; j < MAX_PLAYERS; j++)
4481 if (stored_player[j].active)
4482 DrawPlayerExt(&stored_player[j], i);
4485 void DrawPlayerField(int x, int y)
4487 if (!IS_PLAYER(x, y))
4490 DrawPlayer(PLAYERINFO(x, y));
4493 // ----------------------------------------------------------------------------
4495 void WaitForEventToContinue(void)
4497 boolean first_wait = TRUE;
4498 boolean still_wait = TRUE;
4500 if (program.headless)
4503 // simulate releasing mouse button over last gadget, if still pressed
4505 HandleGadgets(-1, -1, 0);
4507 button_status = MB_RELEASED;
4510 ClearPlayerAction();
4516 if (NextValidEvent(&event))
4520 case EVENT_BUTTONPRESS:
4521 case EVENT_FINGERPRESS:
4525 case EVENT_BUTTONRELEASE:
4526 case EVENT_FINGERRELEASE:
4527 still_wait = first_wait;
4530 case EVENT_KEYPRESS:
4531 case SDL_CONTROLLERBUTTONDOWN:
4532 case SDL_JOYBUTTONDOWN:
4537 HandleOtherEvents(&event);
4541 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4546 if (!PendingEvent())
4551 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4553 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4554 int draw_buffer_last = GetDrawtoField();
4555 int width = request.width;
4556 int height = request.height;
4560 setRequestPosition(&sx, &sy, FALSE);
4562 button_status = MB_RELEASED;
4564 request_gadget_id = -1;
4571 SetDrawtoField(draw_buffer_game);
4573 HandleGameActions();
4575 SetDrawtoField(DRAW_TO_BACKBUFFER);
4582 while (NextValidEvent(&event))
4586 case EVENT_BUTTONPRESS:
4587 case EVENT_BUTTONRELEASE:
4588 case EVENT_MOTIONNOTIFY:
4590 DrawBuffer *drawto_last = drawto;
4593 if (event.type == EVENT_MOTIONNOTIFY)
4598 motion_status = TRUE;
4599 mx = ((MotionEvent *) &event)->x;
4600 my = ((MotionEvent *) &event)->y;
4604 motion_status = FALSE;
4605 mx = ((ButtonEvent *) &event)->x;
4606 my = ((ButtonEvent *) &event)->y;
4607 if (event.type == EVENT_BUTTONPRESS)
4608 button_status = ((ButtonEvent *) &event)->button;
4610 button_status = MB_RELEASED;
4613 if (global.use_envelope_request)
4615 // draw changed button states to temporary bitmap
4616 drawto = bitmap_db_store_1;
4619 // this sets 'request_gadget_id'
4620 HandleGadgets(mx, my, button_status);
4622 if (global.use_envelope_request)
4624 // restore pointer to drawing buffer
4625 drawto = drawto_last;
4627 // prepare complete envelope request from temporary bitmap
4628 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy,
4632 switch (request_gadget_id)
4634 case TOOL_CTRL_ID_YES:
4635 case TOOL_CTRL_ID_TOUCH_YES:
4638 case TOOL_CTRL_ID_NO:
4639 case TOOL_CTRL_ID_TOUCH_NO:
4642 case TOOL_CTRL_ID_CONFIRM:
4643 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4644 result = TRUE | FALSE;
4647 case TOOL_CTRL_ID_PLAYER_1:
4650 case TOOL_CTRL_ID_PLAYER_2:
4653 case TOOL_CTRL_ID_PLAYER_3:
4656 case TOOL_CTRL_ID_PLAYER_4:
4664 // only needed to handle clickable pointer animations here
4665 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4670 case SDL_WINDOWEVENT:
4671 HandleWindowEvent((WindowEvent *) &event);
4674 case SDL_APP_WILLENTERBACKGROUND:
4675 case SDL_APP_DIDENTERBACKGROUND:
4676 case SDL_APP_WILLENTERFOREGROUND:
4677 case SDL_APP_DIDENTERFOREGROUND:
4678 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4681 case EVENT_KEYPRESS:
4683 Key key = GetEventKey((KeyEvent *)&event);
4688 if (req_state & REQ_CONFIRM)
4697 #if defined(KSYM_Rewind)
4698 case KSYM_Rewind: // for Amazon Fire TV remote
4707 #if defined(KSYM_FastForward)
4708 case KSYM_FastForward: // for Amazon Fire TV remote
4714 HandleKeysDebug(key, KEY_PRESSED);
4718 if (req_state & REQ_PLAYER)
4720 int old_player_nr = setup.network_player_nr;
4723 result = old_player_nr + 1;
4728 result = old_player_nr + 1;
4759 case EVENT_FINGERRELEASE:
4760 case EVENT_KEYRELEASE:
4761 ClearPlayerAction();
4764 case SDL_CONTROLLERBUTTONDOWN:
4765 switch (event.cbutton.button)
4767 case SDL_CONTROLLER_BUTTON_A:
4768 case SDL_CONTROLLER_BUTTON_X:
4769 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4770 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4774 case SDL_CONTROLLER_BUTTON_B:
4775 case SDL_CONTROLLER_BUTTON_Y:
4776 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4777 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4778 case SDL_CONTROLLER_BUTTON_BACK:
4783 if (req_state & REQ_PLAYER)
4785 int old_player_nr = setup.network_player_nr;
4788 result = old_player_nr + 1;
4790 switch (event.cbutton.button)
4792 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4793 case SDL_CONTROLLER_BUTTON_Y:
4797 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4798 case SDL_CONTROLLER_BUTTON_B:
4802 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4803 case SDL_CONTROLLER_BUTTON_A:
4807 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4808 case SDL_CONTROLLER_BUTTON_X:
4819 case SDL_CONTROLLERBUTTONUP:
4820 HandleJoystickEvent(&event);
4821 ClearPlayerAction();
4825 HandleOtherEvents(&event);
4830 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4832 int joy = AnyJoystick();
4834 if (joy & JOY_BUTTON_1)
4836 else if (joy & JOY_BUTTON_2)
4839 else if (AnyJoystick())
4841 int joy = AnyJoystick();
4843 if (req_state & REQ_PLAYER)
4847 else if (joy & JOY_RIGHT)
4849 else if (joy & JOY_DOWN)
4851 else if (joy & JOY_LEFT)
4859 SetDrawtoField(draw_buffer_last);
4864 static void DoRequestBefore(void)
4866 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4868 // when showing request dialog after game ended, deactivate game panel
4870 game.panel.active = FALSE;
4872 if (game_status == GAME_MODE_PLAYING)
4873 BlitScreenToBitmap(backbuffer);
4875 // disable deactivated drawing when quick-loading level tape recording
4876 if (tape.playing && tape.deactivate_display)
4877 TapeDeactivateDisplayOff(TRUE);
4879 SetMouseCursor(CURSOR_DEFAULT);
4881 // pause network game while waiting for request to answer
4882 if (network.enabled &&
4883 game_status == GAME_MODE_PLAYING &&
4884 !game.all_players_gone)
4885 SendToServer_PausePlaying();
4887 // simulate releasing mouse button over last gadget, if still pressed
4889 HandleGadgets(-1, -1, 0);
4894 static void DoRequestAfter(void)
4898 if (game_status == GAME_MODE_PLAYING)
4900 SetPanelBackground();
4901 SetDrawBackgroundMask(REDRAW_DOOR_1);
4905 SetDrawBackgroundMask(REDRAW_FIELD);
4908 // continue network game after request
4909 if (network.enabled &&
4910 game_status == GAME_MODE_PLAYING &&
4911 !game.all_players_gone)
4912 SendToServer_ContinuePlaying();
4914 // restore deactivated drawing when quick-loading level tape recording
4915 if (tape.playing && tape.deactivate_display)
4916 TapeDeactivateDisplayOn();
4919 static void setRequestDoorTextProperties(char *text,
4924 int *set_max_line_length)
4926 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
4927 struct TextPosInfo *pos = &request.button.confirm;
4928 int button_ypos = pos->y;
4929 int font_nr = FONT_TEXT_2;
4930 int font_width = getFontWidth(font_nr);
4931 int font_height = getFontHeight(font_nr);
4932 int line_height = font_height + line_spacing;
4933 int max_text_width = vp_door_1->width;
4934 int max_text_height = button_ypos - 2 * text_spacing;
4935 int max_line_length = max_text_width / font_width;
4936 int max_lines = max_text_height / line_height;
4938 if (maxWordLengthInRequestString(text) > max_line_length)
4940 font_nr = FONT_TEXT_1;
4941 font_width = getFontWidth(font_nr);
4942 max_line_length = max_text_width / font_width;
4945 *set_font_nr = font_nr;
4946 *set_max_lines = max_lines;
4947 *set_max_line_length = max_line_length;
4950 static void DrawRequestDoorText(char *text)
4952 char *text_ptr = text;
4953 int text_spacing = 8;
4954 int line_spacing = 2;
4955 int max_request_lines;
4956 int max_request_line_len;
4960 // force DOOR font inside door area
4961 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4963 setRequestDoorTextProperties(text, text_spacing, line_spacing, &font_nr,
4964 &max_request_lines, &max_request_line_len);
4966 for (text_ptr = text, ty = 0; ty < max_request_lines; ty++)
4968 char text_line[max_request_line_len + 1];
4974 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4976 tc = *(text_ptr + tx);
4977 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4981 if ((tc == '?' || tc == '!') && tl == 0)
4991 strncpy(text_line, text_ptr, tl);
4994 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4995 DY + text_spacing + ty * (getFontHeight(font_nr) + line_spacing),
4996 text_line, font_nr);
4998 text_ptr += tl + (tc == ' ' ? 1 : 0);
5004 static int RequestDoor(char *text, unsigned int req_state)
5006 unsigned int old_door_state = GetDoorState();
5007 int draw_buffer_last = GetDrawtoField();
5010 if (old_door_state & DOOR_OPEN_1)
5012 CloseDoor(DOOR_CLOSE_1);
5014 // save old door content
5015 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5016 0, 0, DXSIZE, DYSIZE, DXSIZE, 0);
5019 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5020 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5022 // clear door drawing field
5023 DrawBackground(DX, DY, DXSIZE, DYSIZE);
5025 // write text for request
5026 DrawRequestDoorText(text);
5028 MapToolButtons(req_state);
5030 // copy request gadgets to door backbuffer
5031 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
5033 OpenDoor(DOOR_OPEN_1);
5035 // ---------- handle request buttons ----------
5036 result = RequestHandleEvents(req_state, draw_buffer_last);
5040 if (!(req_state & REQ_STAY_OPEN))
5042 CloseDoor(DOOR_CLOSE_1);
5044 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
5045 (req_state & REQ_REOPEN))
5046 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
5052 static int RequestEnvelope(char *text, unsigned int req_state)
5054 int draw_buffer_last = GetDrawtoField();
5057 DrawEnvelopeRequest(text, req_state);
5058 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5060 // ---------- handle request buttons ----------
5061 result = RequestHandleEvents(req_state, draw_buffer_last);
5065 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5070 int Request(char *text, unsigned int req_state)
5072 boolean overlay_enabled = GetOverlayEnabled();
5075 game.request_active = TRUE;
5077 SetOverlayEnabled(FALSE);
5081 if (global.use_envelope_request)
5082 result = RequestEnvelope(text, req_state);
5084 result = RequestDoor(text, req_state);
5088 SetOverlayEnabled(overlay_enabled);
5090 game.request_active = FALSE;
5095 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5097 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5098 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5101 if (dpo1->sort_priority != dpo2->sort_priority)
5102 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5104 compare_result = dpo1->nr - dpo2->nr;
5106 return compare_result;
5109 void InitGraphicCompatibilityInfo_Doors(void)
5115 struct DoorInfo *door;
5119 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5120 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5122 { -1, -1, -1, NULL }
5124 struct Rect door_rect_list[] =
5126 { DX, DY, DXSIZE, DYSIZE },
5127 { VX, VY, VXSIZE, VYSIZE }
5131 for (i = 0; doors[i].door_token != -1; i++)
5133 int door_token = doors[i].door_token;
5134 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5135 int part_1 = doors[i].part_1;
5136 int part_8 = doors[i].part_8;
5137 int part_2 = part_1 + 1;
5138 int part_3 = part_1 + 2;
5139 struct DoorInfo *door = doors[i].door;
5140 struct Rect *door_rect = &door_rect_list[door_index];
5141 boolean door_gfx_redefined = FALSE;
5143 // check if any door part graphic definitions have been redefined
5145 for (j = 0; door_part_controls[j].door_token != -1; j++)
5147 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5148 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5150 if (dpc->door_token == door_token && fi->redefined)
5151 door_gfx_redefined = TRUE;
5154 // check for old-style door graphic/animation modifications
5156 if (!door_gfx_redefined)
5158 if (door->anim_mode & ANIM_STATIC_PANEL)
5160 door->panel.step_xoffset = 0;
5161 door->panel.step_yoffset = 0;
5164 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5166 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5167 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5168 int num_door_steps, num_panel_steps;
5170 // remove door part graphics other than the two default wings
5172 for (j = 0; door_part_controls[j].door_token != -1; j++)
5174 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5175 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5177 if (dpc->graphic >= part_3 &&
5178 dpc->graphic <= part_8)
5182 // set graphics and screen positions of the default wings
5184 g_part_1->width = door_rect->width;
5185 g_part_1->height = door_rect->height;
5186 g_part_2->width = door_rect->width;
5187 g_part_2->height = door_rect->height;
5188 g_part_2->src_x = door_rect->width;
5189 g_part_2->src_y = g_part_1->src_y;
5191 door->part_2.x = door->part_1.x;
5192 door->part_2.y = door->part_1.y;
5194 if (door->width != -1)
5196 g_part_1->width = door->width;
5197 g_part_2->width = door->width;
5199 // special treatment for graphics and screen position of right wing
5200 g_part_2->src_x += door_rect->width - door->width;
5201 door->part_2.x += door_rect->width - door->width;
5204 if (door->height != -1)
5206 g_part_1->height = door->height;
5207 g_part_2->height = door->height;
5209 // special treatment for graphics and screen position of bottom wing
5210 g_part_2->src_y += door_rect->height - door->height;
5211 door->part_2.y += door_rect->height - door->height;
5214 // set animation delays for the default wings and panels
5216 door->part_1.step_delay = door->step_delay;
5217 door->part_2.step_delay = door->step_delay;
5218 door->panel.step_delay = door->step_delay;
5220 // set animation draw order for the default wings
5222 door->part_1.sort_priority = 2; // draw left wing over ...
5223 door->part_2.sort_priority = 1; // ... right wing
5225 // set animation draw offset for the default wings
5227 if (door->anim_mode & ANIM_HORIZONTAL)
5229 door->part_1.step_xoffset = door->step_offset;
5230 door->part_1.step_yoffset = 0;
5231 door->part_2.step_xoffset = door->step_offset * -1;
5232 door->part_2.step_yoffset = 0;
5234 num_door_steps = g_part_1->width / door->step_offset;
5236 else // ANIM_VERTICAL
5238 door->part_1.step_xoffset = 0;
5239 door->part_1.step_yoffset = door->step_offset;
5240 door->part_2.step_xoffset = 0;
5241 door->part_2.step_yoffset = door->step_offset * -1;
5243 num_door_steps = g_part_1->height / door->step_offset;
5246 // set animation draw offset for the default panels
5248 if (door->step_offset > 1)
5250 num_panel_steps = 2 * door_rect->height / door->step_offset;
5251 door->panel.start_step = num_panel_steps - num_door_steps;
5252 door->panel.start_step_closing = door->panel.start_step;
5256 num_panel_steps = door_rect->height / door->step_offset;
5257 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5258 door->panel.start_step_closing = door->panel.start_step;
5259 door->panel.step_delay *= 2;
5266 void InitDoors(void)
5270 for (i = 0; door_part_controls[i].door_token != -1; i++)
5272 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5273 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5275 // initialize "start_step_opening" and "start_step_closing", if needed
5276 if (dpc->pos->start_step_opening == 0 &&
5277 dpc->pos->start_step_closing == 0)
5279 // dpc->pos->start_step_opening = dpc->pos->start_step;
5280 dpc->pos->start_step_closing = dpc->pos->start_step;
5283 // fill structure for door part draw order (sorted below)
5285 dpo->sort_priority = dpc->pos->sort_priority;
5288 // sort door part controls according to sort_priority and graphic number
5289 qsort(door_part_order, MAX_DOOR_PARTS,
5290 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5293 unsigned int OpenDoor(unsigned int door_state)
5295 if (door_state & DOOR_COPY_BACK)
5297 if (door_state & DOOR_OPEN_1)
5298 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5299 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5301 if (door_state & DOOR_OPEN_2)
5302 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5303 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5305 door_state &= ~DOOR_COPY_BACK;
5308 return MoveDoor(door_state);
5311 unsigned int CloseDoor(unsigned int door_state)
5313 unsigned int old_door_state = GetDoorState();
5315 if (!(door_state & DOOR_NO_COPY_BACK))
5317 if (old_door_state & DOOR_OPEN_1)
5318 BlitBitmap(backbuffer, bitmap_db_door_1,
5319 DX, DY, DXSIZE, DYSIZE, 0, 0);
5321 if (old_door_state & DOOR_OPEN_2)
5322 BlitBitmap(backbuffer, bitmap_db_door_2,
5323 VX, VY, VXSIZE, VYSIZE, 0, 0);
5325 door_state &= ~DOOR_NO_COPY_BACK;
5328 return MoveDoor(door_state);
5331 unsigned int GetDoorState(void)
5333 return MoveDoor(DOOR_GET_STATE);
5336 unsigned int SetDoorState(unsigned int door_state)
5338 return MoveDoor(door_state | DOOR_SET_STATE);
5341 static int euclid(int a, int b)
5343 return (b ? euclid(b, a % b) : a);
5346 unsigned int MoveDoor(unsigned int door_state)
5348 struct Rect door_rect_list[] =
5350 { DX, DY, DXSIZE, DYSIZE },
5351 { VX, VY, VXSIZE, VYSIZE }
5353 static int door1 = DOOR_CLOSE_1;
5354 static int door2 = DOOR_CLOSE_2;
5355 DelayCounter door_delay = { 0 };
5358 if (door_state == DOOR_GET_STATE)
5359 return (door1 | door2);
5361 if (door_state & DOOR_SET_STATE)
5363 if (door_state & DOOR_ACTION_1)
5364 door1 = door_state & DOOR_ACTION_1;
5365 if (door_state & DOOR_ACTION_2)
5366 door2 = door_state & DOOR_ACTION_2;
5368 return (door1 | door2);
5371 if (!(door_state & DOOR_FORCE_REDRAW))
5373 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5374 door_state &= ~DOOR_OPEN_1;
5375 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5376 door_state &= ~DOOR_CLOSE_1;
5377 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5378 door_state &= ~DOOR_OPEN_2;
5379 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5380 door_state &= ~DOOR_CLOSE_2;
5383 if (global.autoplay_leveldir)
5385 door_state |= DOOR_NO_DELAY;
5386 door_state &= ~DOOR_CLOSE_ALL;
5389 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5390 door_state |= DOOR_NO_DELAY;
5392 if (door_state & DOOR_ACTION)
5394 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
5395 boolean door_panel_drawn[NUM_DOORS];
5396 boolean panel_has_doors[NUM_DOORS];
5397 boolean door_part_skip[MAX_DOOR_PARTS];
5398 boolean door_part_done[MAX_DOOR_PARTS];
5399 boolean door_part_done_all;
5400 int num_steps[MAX_DOOR_PARTS];
5401 int max_move_delay = 0; // delay for complete animations of all doors
5402 int max_step_delay = 0; // delay (ms) between two animation frames
5403 int num_move_steps = 0; // number of animation steps for all doors
5404 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5405 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5409 for (i = 0; i < NUM_DOORS; i++)
5410 panel_has_doors[i] = FALSE;
5412 for (i = 0; i < MAX_DOOR_PARTS; i++)
5414 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5415 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5416 int door_token = dpc->door_token;
5418 door_part_done[i] = FALSE;
5419 door_part_skip[i] = (!(door_state & door_token) ||
5423 for (i = 0; i < MAX_DOOR_PARTS; i++)
5425 int nr = door_part_order[i].nr;
5426 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5427 struct DoorPartPosInfo *pos = dpc->pos;
5428 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5429 int door_token = dpc->door_token;
5430 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5431 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5432 int step_xoffset = ABS(pos->step_xoffset);
5433 int step_yoffset = ABS(pos->step_yoffset);
5434 int step_delay = pos->step_delay;
5435 int current_door_state = door_state & door_token;
5436 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5437 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5438 boolean part_opening = (is_panel ? door_closing : door_opening);
5439 int start_step = (part_opening ? pos->start_step_opening :
5440 pos->start_step_closing);
5441 float move_xsize = (step_xoffset ? g->width : 0);
5442 float move_ysize = (step_yoffset ? g->height : 0);
5443 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5444 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5445 int move_steps = (move_xsteps && move_ysteps ?
5446 MIN(move_xsteps, move_ysteps) :
5447 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5448 int move_delay = move_steps * step_delay;
5450 if (door_part_skip[nr])
5453 max_move_delay = MAX(max_move_delay, move_delay);
5454 max_step_delay = (max_step_delay == 0 ? step_delay :
5455 euclid(max_step_delay, step_delay));
5456 num_steps[nr] = move_steps;
5460 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5462 panel_has_doors[door_index] = TRUE;
5466 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5468 num_move_steps = max_move_delay / max_step_delay;
5469 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5471 door_delay.value = max_step_delay;
5473 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5475 start = num_move_steps - 1;
5479 // opening door sound has priority over simultaneously closing door
5480 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5482 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5484 if (door_state & DOOR_OPEN_1)
5485 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5486 if (door_state & DOOR_OPEN_2)
5487 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5489 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5491 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5493 if (door_state & DOOR_CLOSE_1)
5494 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5495 if (door_state & DOOR_CLOSE_2)
5496 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5500 for (k = start; k < num_move_steps; k++)
5502 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5504 door_part_done_all = TRUE;
5506 for (i = 0; i < NUM_DOORS; i++)
5507 door_panel_drawn[i] = FALSE;
5509 for (i = 0; i < MAX_DOOR_PARTS; i++)
5511 int nr = door_part_order[i].nr;
5512 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5513 struct DoorPartPosInfo *pos = dpc->pos;
5514 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5515 int door_token = dpc->door_token;
5516 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5517 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5518 boolean is_panel_and_door_has_closed = FALSE;
5519 struct Rect *door_rect = &door_rect_list[door_index];
5520 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5522 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5523 int current_door_state = door_state & door_token;
5524 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5525 boolean door_closing = !door_opening;
5526 boolean part_opening = (is_panel ? door_closing : door_opening);
5527 boolean part_closing = !part_opening;
5528 int start_step = (part_opening ? pos->start_step_opening :
5529 pos->start_step_closing);
5530 int step_delay = pos->step_delay;
5531 int step_factor = step_delay / max_step_delay;
5532 int k1 = (step_factor ? k / step_factor + 1 : k);
5533 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5534 int kk = MAX(0, k2);
5537 int src_x, src_y, src_xx, src_yy;
5538 int dst_x, dst_y, dst_xx, dst_yy;
5541 if (door_part_skip[nr])
5544 if (!(door_state & door_token))
5552 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5553 int kk_door = MAX(0, k2_door);
5554 int sync_frame = kk_door * door_delay.value;
5555 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5557 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5558 &g_src_x, &g_src_y);
5563 if (!door_panel_drawn[door_index])
5565 ClearRectangle(drawto, door_rect->x, door_rect->y,
5566 door_rect->width, door_rect->height);
5568 door_panel_drawn[door_index] = TRUE;
5571 // draw opening or closing door parts
5573 if (pos->step_xoffset < 0) // door part on right side
5576 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5579 if (dst_xx + width > door_rect->width)
5580 width = door_rect->width - dst_xx;
5582 else // door part on left side
5585 dst_xx = pos->x - kk * pos->step_xoffset;
5589 src_xx = ABS(dst_xx);
5593 width = g->width - src_xx;
5595 if (width > door_rect->width)
5596 width = door_rect->width;
5598 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5601 if (pos->step_yoffset < 0) // door part on bottom side
5604 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5607 if (dst_yy + height > door_rect->height)
5608 height = door_rect->height - dst_yy;
5610 else // door part on top side
5613 dst_yy = pos->y - kk * pos->step_yoffset;
5617 src_yy = ABS(dst_yy);
5621 height = g->height - src_yy;
5624 src_x = g_src_x + src_xx;
5625 src_y = g_src_y + src_yy;
5627 dst_x = door_rect->x + dst_xx;
5628 dst_y = door_rect->y + dst_yy;
5630 is_panel_and_door_has_closed =
5633 panel_has_doors[door_index] &&
5634 k >= num_move_steps_doors_only - 1);
5636 if (width >= 0 && width <= g->width &&
5637 height >= 0 && height <= g->height &&
5638 !is_panel_and_door_has_closed)
5640 if (is_panel || !pos->draw_masked)
5641 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5644 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5648 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5650 if ((part_opening && (width < 0 || height < 0)) ||
5651 (part_closing && (width >= g->width && height >= g->height)))
5652 door_part_done[nr] = TRUE;
5654 // continue door part animations, but not panel after door has closed
5655 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5656 door_part_done_all = FALSE;
5659 if (!(door_state & DOOR_NO_DELAY))
5662 HandleGameActions();
5666 SkipUntilDelayReached(&door_delay, &k, last_frame);
5668 // prevent OS (Windows) from complaining about program not responding
5672 if (door_part_done_all)
5676 if (!(door_state & DOOR_NO_DELAY))
5678 // wait for specified door action post delay
5679 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5680 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5681 else if (door_state & DOOR_ACTION_1)
5682 door_delay.value = door_1.post_delay;
5683 else if (door_state & DOOR_ACTION_2)
5684 door_delay.value = door_2.post_delay;
5686 while (!DelayReached(&door_delay))
5689 HandleGameActions();
5696 if (door_state & DOOR_ACTION_1)
5697 door1 = door_state & DOOR_ACTION_1;
5698 if (door_state & DOOR_ACTION_2)
5699 door2 = door_state & DOOR_ACTION_2;
5701 // draw masked border over door area
5702 DrawMaskedBorder(REDRAW_DOOR_1);
5703 DrawMaskedBorder(REDRAW_DOOR_2);
5705 ClearAutoRepeatKeyEvents();
5707 return (door1 | door2);
5710 static boolean useSpecialEditorDoor(void)
5712 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5713 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5715 // do not draw special editor door if editor border defined or redefined
5716 if (graphic_info[graphic].bitmap != NULL || redefined)
5719 // do not draw special editor door if global border defined to be empty
5720 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5723 // do not draw special editor door if viewport definitions do not match
5727 EY + EYSIZE != VY + VYSIZE)
5733 void DrawSpecialEditorDoor(void)
5735 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5736 int top_border_width = gfx1->width;
5737 int top_border_height = gfx1->height;
5738 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5739 int ex = EX - outer_border;
5740 int ey = EY - outer_border;
5741 int vy = VY - outer_border;
5742 int exsize = EXSIZE + 2 * outer_border;
5744 if (!useSpecialEditorDoor())
5747 // draw bigger level editor toolbox window
5748 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5749 top_border_width, top_border_height, ex, ey - top_border_height);
5750 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5751 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5753 redraw_mask |= REDRAW_ALL;
5756 void UndrawSpecialEditorDoor(void)
5758 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5759 int top_border_width = gfx1->width;
5760 int top_border_height = gfx1->height;
5761 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5762 int ex = EX - outer_border;
5763 int ey = EY - outer_border;
5764 int ey_top = ey - top_border_height;
5765 int exsize = EXSIZE + 2 * outer_border;
5766 int eysize = EYSIZE + 2 * outer_border;
5768 if (!useSpecialEditorDoor())
5771 // draw normal tape recorder window
5772 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5774 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5775 ex, ey_top, top_border_width, top_border_height,
5777 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5778 ex, ey, exsize, eysize, ex, ey);
5782 // if screen background is set to "[NONE]", clear editor toolbox window
5783 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5784 ClearRectangle(drawto, ex, ey, exsize, eysize);
5787 redraw_mask |= REDRAW_ALL;
5791 // ---------- new tool button stuff -------------------------------------------
5796 struct TextPosInfo *pos;
5798 boolean is_touch_button;
5800 } toolbutton_info[NUM_TOOL_BUTTONS] =
5803 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5804 TOOL_CTRL_ID_YES, FALSE, "yes"
5807 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5808 TOOL_CTRL_ID_NO, FALSE, "no"
5811 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5812 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5815 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5816 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5819 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5820 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5823 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5824 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5827 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5828 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5831 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5832 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5835 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5836 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5839 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5840 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5844 void CreateToolButtons(void)
5848 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5850 int graphic = toolbutton_info[i].graphic;
5851 struct GraphicInfo *gfx = &graphic_info[graphic];
5852 struct TextPosInfo *pos = toolbutton_info[i].pos;
5853 struct GadgetInfo *gi;
5854 Bitmap *deco_bitmap = None;
5855 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5856 unsigned int event_mask = GD_EVENT_RELEASED;
5857 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5858 int base_x = (is_touch_button ? 0 : DX);
5859 int base_y = (is_touch_button ? 0 : DY);
5860 int gd_x = gfx->src_x;
5861 int gd_y = gfx->src_y;
5862 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5863 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5868 // do not use touch buttons if overlay touch buttons are disabled
5869 if (is_touch_button && !setup.touch.overlay_buttons)
5872 if (global.use_envelope_request && !is_touch_button)
5874 setRequestPosition(&base_x, &base_y, TRUE);
5876 // check if request buttons are outside of envelope and fix, if needed
5877 if (x < 0 || x + gfx->width > request.width ||
5878 y < 0 || y + gfx->height > request.height)
5880 if (id == TOOL_CTRL_ID_YES)
5883 y = request.height - 2 * request.border_size - gfx->height;
5885 else if (id == TOOL_CTRL_ID_NO)
5887 x = request.width - 2 * request.border_size - gfx->width;
5888 y = request.height - 2 * request.border_size - gfx->height;
5890 else if (id == TOOL_CTRL_ID_CONFIRM)
5892 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5893 y = request.height - 2 * request.border_size - gfx->height;
5895 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5897 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5899 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5900 y = request.height - 2 * request.border_size - gfx->height * 2;
5902 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5903 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5908 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5911 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5913 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5914 pos->size, &deco_bitmap, &deco_x, &deco_y);
5915 deco_xpos = (gfx->width - pos->size) / 2;
5916 deco_ypos = (gfx->height - pos->size) / 2;
5919 gi = CreateGadget(GDI_CUSTOM_ID, id,
5920 GDI_IMAGE_ID, graphic,
5921 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5924 GDI_WIDTH, gfx->width,
5925 GDI_HEIGHT, gfx->height,
5926 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5927 GDI_STATE, GD_BUTTON_UNPRESSED,
5928 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5929 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5930 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5931 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5932 GDI_DECORATION_SIZE, pos->size, pos->size,
5933 GDI_DECORATION_SHIFTING, 1, 1,
5934 GDI_DIRECT_DRAW, FALSE,
5935 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5936 GDI_EVENT_MASK, event_mask,
5937 GDI_CALLBACK_ACTION, HandleToolButtons,
5941 Fail("cannot create gadget");
5943 tool_gadget[id] = gi;
5947 void FreeToolButtons(void)
5951 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5952 FreeGadget(tool_gadget[i]);
5955 static void MapToolButtons(unsigned int req_state)
5957 if (req_state & REQ_ASK)
5959 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
5960 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
5961 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
5962 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
5964 else if (req_state & REQ_CONFIRM)
5966 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
5967 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
5969 else if (req_state & REQ_PLAYER)
5971 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
5972 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
5973 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
5974 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
5978 static void UnmapToolButtons(void)
5982 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5983 UnmapGadget(tool_gadget[i]);
5986 static void HandleToolButtons(struct GadgetInfo *gi)
5988 request_gadget_id = gi->custom_id;
5991 static struct Mapping_BD_to_RND_object
5994 boolean is_rnd_to_bd_mapping; // unique mapping BD <-> RND
6000 bd_object_mapping_list[] =
6002 // additional RND style elements mapped to BD style elements must be listed first
6018 EL_STEELWALL, -1, -1
6022 EL_BD_DIAMOND, -1, -1
6042 EL_EXIT_CLOSED, -1, -1
6045 // BD style elements with their corresponding RND style elements
6056 O_DIRT_SLOPED_UP_RIGHT, TRUE,
6057 EL_BD_SAND_SLOPED_UP_RIGHT, -1, -1
6060 O_DIRT_SLOPED_UP_LEFT, TRUE,
6061 EL_BD_SAND_SLOPED_UP_LEFT, -1, -1
6064 O_DIRT_SLOPED_DOWN_LEFT, TRUE,
6065 EL_BD_SAND_SLOPED_DOWN_LEFT, -1, -1
6068 O_DIRT_SLOPED_DOWN_RIGHT, TRUE,
6069 EL_BD_SAND_SLOPED_DOWN_RIGHT, -1, -1
6073 EL_BD_SAND_BALL, -1, -1
6076 O_DIRT_BALL_F, FALSE,
6077 EL_BD_SAND_BALL, ACTION_FALLING, -1
6081 EL_BD_SAND_LOOSE, -1, -1
6084 O_DIRT_LOOSE_F, FALSE,
6085 EL_BD_SAND_LOOSE, ACTION_FALLING, -1
6089 EL_BD_SAND_2, -1, -1
6096 O_BRICK_SLOPED_UP_RIGHT, TRUE,
6097 EL_BD_WALL_SLOPED_UP_RIGHT, -1, -1
6100 O_BRICK_SLOPED_UP_LEFT, TRUE,
6101 EL_BD_WALL_SLOPED_UP_LEFT, -1, -1
6104 O_BRICK_SLOPED_DOWN_LEFT, TRUE,
6105 EL_BD_WALL_SLOPED_DOWN_LEFT, -1, -1
6108 O_BRICK_SLOPED_DOWN_RIGHT, TRUE,
6109 EL_BD_WALL_SLOPED_DOWN_RIGHT, -1, -1
6112 O_BRICK_NON_SLOPED, TRUE,
6113 EL_BD_WALL_NON_SLOPED, -1, -1
6117 EL_BD_MAGIC_WALL, ACTION_ACTIVE, -1
6121 EL_BD_EXIT_CLOSED, -1, -1
6125 EL_BD_EXIT_OPEN, -1, -1
6128 O_PRE_INVIS_OUTBOX, TRUE,
6129 EL_BD_INVISIBLE_EXIT_CLOSED, -1, -1
6132 O_INVIS_OUTBOX, TRUE,
6133 EL_BD_INVISIBLE_EXIT_OPEN, -1, -1
6137 EL_BD_STEELWALL, -1, -1
6140 O_STEEL_SLOPED_UP_RIGHT, TRUE,
6141 EL_BD_STEELWALL_SLOPED_UP_RIGHT, -1, -1
6144 O_STEEL_SLOPED_UP_LEFT, TRUE,
6145 EL_BD_STEELWALL_SLOPED_UP_LEFT, -1, -1
6148 O_STEEL_SLOPED_DOWN_LEFT, TRUE,
6149 EL_BD_STEELWALL_SLOPED_DOWN_LEFT, -1, -1
6152 O_STEEL_SLOPED_DOWN_RIGHT, TRUE,
6153 EL_BD_STEELWALL_SLOPED_DOWN_RIGHT, -1, -1
6156 O_STEEL_EXPLODABLE, TRUE,
6157 EL_BD_STEELWALL_EXPLODABLE, -1, -1
6160 O_STEEL_EATABLE, TRUE,
6161 EL_BD_STEELWALL_DIGGABLE, -1, -1
6164 O_BRICK_EATABLE, TRUE,
6165 EL_BD_WALL_DIGGABLE, -1, -1
6173 EL_BD_ROCK, ACTION_FALLING, -1
6176 O_FLYING_STONE, TRUE,
6177 EL_BD_FLYING_ROCK, -1, -1
6180 O_FLYING_STONE_F, FALSE,
6181 EL_BD_FLYING_ROCK, ACTION_FALLING, -1
6185 EL_BD_MEGA_ROCK, -1, -1
6188 O_MEGA_STONE_F, FALSE,
6189 EL_BD_MEGA_ROCK, ACTION_FALLING, -1
6193 EL_BD_DIAMOND, -1, -1
6197 EL_BD_DIAMOND, ACTION_FALLING, -1
6200 O_FLYING_DIAMOND, TRUE,
6201 EL_BD_FLYING_DIAMOND, -1, -1
6204 O_FLYING_DIAMOND_F, FALSE,
6205 EL_BD_FLYING_DIAMOND, ACTION_FALLING, -1
6213 EL_BD_NUT, ACTION_FALLING, -1
6216 O_BLADDER_SPENDER, TRUE,
6217 EL_BD_BLADDER_SPENDER, -1, -1
6224 O_H_EXPANDING_WALL, TRUE,
6225 EL_BD_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6228 O_V_EXPANDING_WALL, TRUE,
6229 EL_BD_EXPANDABLE_WALL_VERTICAL, -1, -1
6232 O_EXPANDING_WALL, TRUE,
6233 EL_BD_EXPANDABLE_WALL_ANY, -1, -1
6236 O_H_EXPANDING_STEEL_WALL, TRUE,
6237 EL_BD_EXPANDABLE_STEELWALL_HORIZONTAL, -1, -1
6240 O_V_EXPANDING_STEEL_WALL, TRUE,
6241 EL_BD_EXPANDABLE_STEELWALL_VERTICAL, -1, -1
6244 O_EXPANDING_STEEL_WALL, TRUE,
6245 EL_BD_EXPANDABLE_STEELWALL_ANY, -1, -1
6248 O_EXPANDING_WALL_SWITCH, TRUE,
6249 EL_BD_EXPANDABLE_WALL_SWITCH_HORIZONTAL, -1, -1
6252 O_CREATURE_SWITCH, TRUE,
6253 EL_BD_CREATURE_SWITCH, -1, -1
6256 O_BITER_SWITCH, TRUE,
6257 EL_BD_BITER_SWITCH_1, -1, -1
6260 O_REPLICATOR_SWITCH, TRUE,
6261 EL_BD_REPLICATOR_SWITCH, -1, -1
6264 O_CONVEYOR_SWITCH, TRUE,
6265 EL_BD_CONVEYOR_SWITCH, -1, -1
6268 O_CONVEYOR_DIR_SWITCH, TRUE,
6269 EL_BD_CONVEYOR_DIR_SWITCH_RIGHT, -1, -1
6276 O_FALLING_WALL, TRUE,
6277 EL_BD_FALLING_WALL, -1, -1
6280 O_FALLING_WALL_F, FALSE,
6281 EL_BD_FALLING_WALL, ACTION_FALLING, -1
6288 O_TIME_PENALTY, TRUE,
6289 EL_BD_TIME_PENALTY, -1, -1
6293 EL_BD_GRAVESTONE, -1, -1
6296 O_STONE_GLUED, TRUE,
6297 EL_BD_ROCK_GLUED, -1, -1
6300 O_DIAMOND_GLUED, TRUE,
6301 EL_BD_DIAMOND_GLUED, -1, -1
6304 O_DIAMOND_KEY, TRUE,
6305 EL_BD_DIAMOND_KEY, -1, -1
6308 O_TRAPPED_DIAMOND, TRUE,
6309 EL_BD_TRAPPED_DIAMOND, -1, -1
6317 EL_BD_SAND_GLUED, -1, -1
6333 EL_BD_GATE_1, -1, -1
6337 EL_BD_GATE_2, -1, -1
6341 EL_BD_GATE_3, -1, -1
6348 O_GRAVITY_SWITCH, TRUE,
6349 EL_BD_GRAVITY_SWITCH, -1, -1
6352 O_PNEUMATIC_HAMMER, TRUE,
6353 EL_BD_PNEUMATIC_HAMMER, -1, -1
6357 EL_BD_TELEPORTER, -1, -1
6361 EL_BD_SKELETON, -1, -1
6433 EL_BD_COW_LEFT, -1, -1
6437 EL_BD_COW_UP, -1, -1
6441 EL_BD_COW_RIGHT, -1, -1
6445 EL_BD_COW_DOWN, -1, -1
6448 O_COW_ENCLOSED_1, FALSE,
6449 EL_BD_COW_DOWN, -1, -1
6452 O_COW_ENCLOSED_2, FALSE,
6453 EL_BD_COW_DOWN, -1, -1
6456 O_COW_ENCLOSED_3, FALSE,
6457 EL_BD_COW_DOWN, -1, -1
6460 O_COW_ENCLOSED_4, FALSE,
6461 EL_BD_COW_DOWN, -1, -1
6464 O_COW_ENCLOSED_5, FALSE,
6465 EL_BD_COW_DOWN, -1, -1
6468 O_COW_ENCLOSED_6, FALSE,
6469 EL_BD_COW_DOWN, -1, -1
6472 O_COW_ENCLOSED_7, FALSE,
6473 EL_BD_COW_DOWN, -1, -1
6476 O_WALLED_DIAMOND, TRUE,
6477 EL_BD_WALL_DIAMOND, -1, -1
6480 O_WALLED_KEY_1, TRUE,
6481 EL_BD_WALL_KEY_1, -1, -1
6484 O_WALLED_KEY_2, TRUE,
6485 EL_BD_WALL_KEY_2, -1, -1
6488 O_WALLED_KEY_3, TRUE,
6489 EL_BD_WALL_KEY_3, -1, -1
6493 EL_BD_AMOEBA, -1, -1
6497 EL_BD_AMOEBA_2, -1, -1
6501 EL_BD_REPLICATOR, -1, -1
6504 O_CONVEYOR_LEFT, TRUE,
6505 EL_BD_CONVEYOR_LEFT, -1, -1
6508 O_CONVEYOR_RIGHT, TRUE,
6509 EL_BD_CONVEYOR_RIGHT, -1, -1
6521 EL_BD_VOODOO_DOLL, -1, -1
6529 EL_BD_BLADDER, -1, -1
6533 EL_BD_BLADDER, -1, -1
6537 EL_BD_BLADDER, -1, -1
6541 EL_BD_BLADDER, -1, -1
6545 EL_BD_BLADDER, -1, -1
6549 EL_BD_BLADDER, -1, -1
6553 EL_BD_BLADDER, -1, -1
6557 EL_BD_BLADDER, -1, -1
6561 EL_BD_BLADDER, -1, -1
6564 O_WAITING_STONE, TRUE,
6565 EL_BD_WAITING_ROCK, -1, -1
6568 O_CHASING_STONE, TRUE,
6569 EL_BD_CHASING_ROCK, -1, -1
6577 EL_BD_FIREFLY_LEFT, -1, -1
6581 EL_BD_FIREFLY_UP, -1, -1
6585 EL_BD_FIREFLY_RIGHT, -1, -1
6589 EL_BD_FIREFLY_DOWN, -1, -1
6592 O_ALT_FIREFLY_1, TRUE,
6593 EL_BD_FIREFLY_2_LEFT, -1, -1
6596 O_ALT_FIREFLY_2, TRUE,
6597 EL_BD_FIREFLY_2_UP, -1, -1
6600 O_ALT_FIREFLY_3, TRUE,
6601 EL_BD_FIREFLY_2_RIGHT, -1, -1
6604 O_ALT_FIREFLY_4, TRUE,
6605 EL_BD_FIREFLY_2_DOWN, -1, -1
6609 EL_BD_BUTTERFLY_LEFT, -1, -1
6613 EL_BD_BUTTERFLY_UP, -1, -1
6617 EL_BD_BUTTERFLY_RIGHT, -1, -1
6621 EL_BD_BUTTERFLY_DOWN, -1, -1
6624 O_ALT_BUTTER_1, TRUE,
6625 EL_BD_BUTTERFLY_2_LEFT, -1, -1
6628 O_ALT_BUTTER_2, TRUE,
6629 EL_BD_BUTTERFLY_2_UP, -1, -1
6632 O_ALT_BUTTER_3, TRUE,
6633 EL_BD_BUTTERFLY_2_RIGHT, -1, -1
6636 O_ALT_BUTTER_4, TRUE,
6637 EL_BD_BUTTERFLY_2_DOWN, -1, -1
6641 EL_BD_STONEFLY_LEFT, -1, -1
6645 EL_BD_STONEFLY_UP, -1, -1
6649 EL_BD_STONEFLY_RIGHT, -1, -1
6653 EL_BD_STONEFLY_DOWN, -1, -1
6657 EL_BD_BITER_UP, -1, -1
6661 EL_BD_BITER_RIGHT, -1, -1
6665 EL_BD_BITER_DOWN, -1, -1
6669 EL_BD_BITER_LEFT, -1, -1
6672 O_DRAGONFLY_1, TRUE,
6673 EL_BD_DRAGONFLY_LEFT, -1, -1
6676 O_DRAGONFLY_2, TRUE,
6677 EL_BD_DRAGONFLY_UP, -1, -1
6680 O_DRAGONFLY_3, TRUE,
6681 EL_BD_DRAGONFLY_RIGHT, -1, -1
6684 O_DRAGONFLY_4, TRUE,
6685 EL_BD_DRAGONFLY_DOWN, -1, -1
6689 EL_BD_PLAYER, ACTION_GROWING, -1
6693 EL_BD_PLAYER, ACTION_GROWING, -1
6697 EL_BD_PLAYER, ACTION_GROWING, -1
6701 EL_BD_PLAYER, -1, -1
6704 O_PLAYER_BOMB, TRUE,
6705 EL_BD_PLAYER_WITH_BOMB, -1, -1
6708 O_PLAYER_GLUED, TRUE,
6709 EL_BD_PLAYER_GLUED, -1, -1
6712 O_PLAYER_STIRRING, TRUE,
6713 EL_BD_PLAYER_STIRRING, -1, -1
6720 O_BOMB_TICK_1, FALSE,
6721 EL_BD_BOMB, ACTION_ACTIVE, -1
6724 O_BOMB_TICK_2, FALSE,
6725 EL_BD_BOMB, ACTION_ACTIVE, -1
6728 O_BOMB_TICK_3, FALSE,
6729 EL_BD_BOMB, ACTION_ACTIVE, -1
6732 O_BOMB_TICK_4, FALSE,
6733 EL_BD_BOMB, ACTION_ACTIVE, -1
6736 O_BOMB_TICK_5, FALSE,
6737 EL_BD_BOMB, ACTION_ACTIVE, -1
6740 O_BOMB_TICK_6, FALSE,
6741 EL_BD_BOMB, ACTION_ACTIVE, -1
6744 O_BOMB_TICK_7, FALSE,
6745 EL_BD_BOMB, ACTION_ACTIVE, -1
6749 EL_BD_NITRO_PACK, -1, -1
6752 O_NITRO_PACK_F, FALSE,
6753 EL_BD_NITRO_PACK, ACTION_FALLING, -1
6756 O_NITRO_PACK_EXPLODE, FALSE,
6757 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
6760 O_PRE_CLOCK_1, FALSE,
6761 EL_BD_CLOCK, ACTION_GROWING, -1
6764 O_PRE_CLOCK_2, FALSE,
6765 EL_BD_CLOCK, ACTION_GROWING, -1
6768 O_PRE_CLOCK_3, FALSE,
6769 EL_BD_CLOCK, ACTION_GROWING, -1
6772 O_PRE_CLOCK_4, FALSE,
6773 EL_BD_CLOCK, ACTION_GROWING, -1
6777 EL_BD_DIAMOND, ACTION_GROWING, -1
6781 EL_BD_DIAMOND, ACTION_GROWING, -1
6785 EL_BD_DIAMOND, ACTION_GROWING, -1
6789 EL_BD_DIAMOND, ACTION_GROWING, -1
6793 EL_BD_DIAMOND, ACTION_GROWING, -1
6797 EL_DEFAULT, ACTION_EXPLODING, -1
6801 EL_DEFAULT, ACTION_EXPLODING, -1
6805 EL_DEFAULT, ACTION_EXPLODING, -1
6809 EL_DEFAULT, ACTION_EXPLODING, -1
6813 EL_DEFAULT, ACTION_EXPLODING, -1
6816 O_PRE_STONE_1, FALSE,
6817 EL_BD_ROCK, ACTION_GROWING, -1
6820 O_PRE_STONE_2, FALSE,
6821 EL_BD_ROCK, ACTION_GROWING, -1
6824 O_PRE_STONE_3, FALSE,
6825 EL_BD_ROCK, ACTION_GROWING, -1
6828 O_PRE_STONE_4, FALSE,
6829 EL_BD_ROCK, ACTION_GROWING, -1
6832 O_PRE_STEEL_1, FALSE,
6833 EL_BD_STEELWALL, ACTION_GROWING, -1
6836 O_PRE_STEEL_2, FALSE,
6837 EL_BD_STEELWALL, ACTION_GROWING, -1
6840 O_PRE_STEEL_3, FALSE,
6841 EL_BD_STEELWALL, ACTION_GROWING, -1
6844 O_PRE_STEEL_4, FALSE,
6845 EL_BD_STEELWALL, ACTION_GROWING, -1
6848 O_GHOST_EXPL_1, FALSE,
6849 EL_BD_GHOST, ACTION_EXPLODING, -1
6852 O_GHOST_EXPL_2, FALSE,
6853 EL_BD_GHOST, ACTION_EXPLODING, -1
6856 O_GHOST_EXPL_3, FALSE,
6857 EL_BD_GHOST, ACTION_EXPLODING, -1
6860 O_GHOST_EXPL_4, FALSE,
6861 EL_BD_GHOST, ACTION_EXPLODING, -1
6864 O_BOMB_EXPL_1, FALSE,
6865 EL_BD_BOMB, ACTION_EXPLODING, -1
6868 O_BOMB_EXPL_2, FALSE,
6869 EL_BD_BOMB, ACTION_EXPLODING, -1
6872 O_BOMB_EXPL_3, FALSE,
6873 EL_BD_BOMB, ACTION_EXPLODING, -1
6876 O_BOMB_EXPL_4, FALSE,
6877 EL_BD_BOMB, ACTION_EXPLODING, -1
6880 O_NITRO_EXPL_1, FALSE,
6881 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
6884 O_NITRO_EXPL_2, FALSE,
6885 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
6888 O_NITRO_EXPL_3, FALSE,
6889 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
6892 O_NITRO_EXPL_4, FALSE,
6893 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
6896 O_AMOEBA_2_EXPL_1, FALSE,
6897 EL_BD_AMOEBA_2, ACTION_EXPLODING, -1
6900 O_AMOEBA_2_EXPL_2, FALSE,
6901 EL_BD_AMOEBA_2, ACTION_EXPLODING, -1
6904 O_AMOEBA_2_EXPL_3, FALSE,
6905 EL_BD_AMOEBA_2, ACTION_EXPLODING, -1
6908 O_AMOEBA_2_EXPL_4, FALSE,
6909 EL_BD_AMOEBA_2, ACTION_EXPLODING, -1
6912 O_NUT_EXPL_1, FALSE,
6913 EL_BD_NUT, ACTION_BREAKING, -1
6916 O_NUT_EXPL_2, FALSE,
6917 EL_BD_NUT, ACTION_BREAKING, -1
6920 O_NUT_EXPL_3, FALSE,
6921 EL_BD_NUT, ACTION_BREAKING, -1
6924 O_NUT_EXPL_4, FALSE,
6925 EL_BD_NUT, ACTION_BREAKING, -1
6928 O_PLAYER_PNEUMATIC_LEFT, FALSE,
6929 EL_BD_PLAYER, ACTION_HITTING, MV_BIT_LEFT
6932 O_PLAYER_PNEUMATIC_RIGHT, FALSE,
6933 EL_BD_PLAYER, ACTION_HITTING, MV_BIT_RIGHT
6936 O_PNEUMATIC_ACTIVE_LEFT, TRUE,
6937 EL_BD_PNEUMATIC_HAMMER, ACTION_HITTING, MV_BIT_LEFT
6940 O_PNEUMATIC_ACTIVE_RIGHT, TRUE,
6941 EL_BD_PNEUMATIC_HAMMER, ACTION_HITTING, MV_BIT_RIGHT
6944 // helper (runtime) elements
6947 O_FAKE_BONUS, FALSE,
6948 EL_BD_FAKE_BONUS, -1, -1
6951 O_INBOX_CLOSED, FALSE,
6955 O_INBOX_OPEN, FALSE,
6956 EL_BD_INBOX, ACTION_OPENING, -1
6959 O_OUTBOX_CLOSED, FALSE,
6960 EL_BD_EXIT_CLOSED, -1, -1
6963 O_OUTBOX_OPEN, FALSE,
6964 EL_BD_EXIT_OPEN, -1, -1
6968 EL_BD_COVERED, -1, -1
6971 O_PLAYER_LEFT, FALSE,
6972 EL_BD_PLAYER, ACTION_MOVING, MV_BIT_LEFT
6975 O_PLAYER_RIGHT, FALSE,
6976 EL_BD_PLAYER, ACTION_MOVING, MV_BIT_RIGHT
6979 O_PLAYER_BLINK, FALSE,
6980 EL_BD_PLAYER, ACTION_BORING_1, -1
6983 O_PLAYER_TAP, FALSE,
6984 EL_BD_PLAYER, ACTION_BORING_2, -1
6987 O_PLAYER_TAP_BLINK, FALSE,
6988 EL_BD_PLAYER, ACTION_BORING_3, -1
6991 O_CREATURE_SWITCH_ON, FALSE,
6992 EL_BD_CREATURE_SWITCH_ACTIVE, -1, -1
6995 O_EXPANDING_WALL_SWITCH_HORIZ, FALSE,
6996 EL_BD_EXPANDABLE_WALL_SWITCH_HORIZONTAL, -1, -1
6999 O_EXPANDING_WALL_SWITCH_VERT, FALSE,
7000 EL_BD_EXPANDABLE_WALL_SWITCH_VERTICAL, -1, -1
7003 O_GRAVITY_SWITCH_ACTIVE, FALSE,
7004 EL_BD_GRAVITY_SWITCH_ACTIVE, -1, -1
7007 O_REPLICATOR_SWITCH_OFF, FALSE,
7008 EL_BD_REPLICATOR_SWITCH, -1, -1
7011 O_REPLICATOR_SWITCH_ON, FALSE,
7012 EL_BD_REPLICATOR_SWITCH_ACTIVE, -1, -1
7015 O_CONVEYOR_DIR_NORMAL, FALSE,
7016 EL_BD_CONVEYOR_DIR_SWITCH_RIGHT, -1, -1
7019 O_CONVEYOR_DIR_CHANGED, FALSE,
7020 EL_BD_CONVEYOR_DIR_SWITCH_LEFT, -1, -1
7023 O_CONVEYOR_SWITCH_OFF, FALSE,
7024 EL_BD_CONVEYOR_SWITCH, -1, -1
7027 O_CONVEYOR_SWITCH_ON, FALSE,
7028 EL_BD_CONVEYOR_SWITCH_ACTIVE, -1, -1
7031 O_MAGIC_WALL_ACTIVE, FALSE,
7032 EL_BD_MAGIC_WALL_ACTIVE, -1, -1
7035 O_REPLICATOR_ACTIVE, FALSE,
7036 EL_BD_REPLICATOR_ACTIVE, -1, -1
7039 O_CONVEYOR_LEFT_ACTIVE, FALSE,
7040 EL_BD_CONVEYOR_LEFT_ACTIVE, -1, -1
7043 O_CONVEYOR_RIGHT_ACTIVE, FALSE,
7044 EL_BD_CONVEYOR_RIGHT_ACTIVE, -1, -1
7047 O_BITER_SWITCH_1, FALSE,
7048 EL_BD_BITER_SWITCH_1, -1, -1
7051 O_BITER_SWITCH_2, FALSE,
7052 EL_BD_BITER_SWITCH_2, -1, -1
7055 O_BITER_SWITCH_3, FALSE,
7056 EL_BD_BITER_SWITCH_3, -1, -1
7059 O_BITER_SWITCH_4, FALSE,
7060 EL_BD_BITER_SWITCH_4, -1, -1
7069 int map_element_RND_to_BD(int element_rnd)
7071 static unsigned short mapping_RND_to_BD[NUM_FILE_ELEMENTS];
7072 static boolean mapping_initialized = FALSE;
7074 if (!mapping_initialized)
7078 // return "O_UNKNOWN" for all undefined elements in mapping array
7079 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7080 mapping_RND_to_BD[i] = O_UNKNOWN;
7082 for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
7083 if (bd_object_mapping_list[i].is_rnd_to_bd_mapping)
7084 mapping_RND_to_BD[bd_object_mapping_list[i].element_rnd] =
7085 bd_object_mapping_list[i].element_bd;
7087 mapping_initialized = TRUE;
7090 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7092 Warn("invalid RND element %d", element_rnd);
7097 return mapping_RND_to_BD[element_rnd];
7100 int map_element_BD_to_RND(int element_bd)
7102 static unsigned short mapping_BD_to_RND[O_MAX_ALL];
7103 static boolean mapping_initialized = FALSE;
7105 if (!mapping_initialized)
7109 // return "EL_UNKNOWN" for all undefined elements in mapping array
7110 for (i = 0; i < O_MAX_ALL; i++)
7111 mapping_BD_to_RND[i] = EL_UNKNOWN;
7113 for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
7114 mapping_BD_to_RND[bd_object_mapping_list[i].element_bd] =
7115 bd_object_mapping_list[i].element_rnd;
7117 mapping_initialized = TRUE;
7120 if (element_bd < 0 || element_bd >= O_MAX_ALL)
7122 Warn("invalid BD element %d", element_bd);
7127 return mapping_BD_to_RND[element_bd];
7130 static struct Mapping_EM_to_RND_object
7133 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
7134 boolean is_backside; // backside of moving element
7140 em_object_mapping_list[GAME_TILE_MAX + 1] =
7143 Zborder, FALSE, FALSE,
7147 Zplayer, FALSE, FALSE,
7156 Ztank, FALSE, FALSE,
7160 Zeater, FALSE, FALSE,
7164 Zdynamite, FALSE, FALSE,
7168 Zboom, FALSE, FALSE,
7173 Xchain, FALSE, FALSE,
7174 EL_DEFAULT, ACTION_EXPLODING, -1
7177 Xboom_bug, FALSE, FALSE,
7178 EL_BUG, ACTION_EXPLODING, -1
7181 Xboom_tank, FALSE, FALSE,
7182 EL_SPACESHIP, ACTION_EXPLODING, -1
7185 Xboom_android, FALSE, FALSE,
7186 EL_EMC_ANDROID, ACTION_OTHER, -1
7189 Xboom_1, FALSE, FALSE,
7190 EL_DEFAULT, ACTION_EXPLODING, -1
7193 Xboom_2, FALSE, FALSE,
7194 EL_DEFAULT, ACTION_EXPLODING, -1
7198 Xblank, TRUE, FALSE,
7203 Xsplash_e, FALSE, FALSE,
7204 EL_ACID_SPLASH_RIGHT, -1, -1
7207 Xsplash_w, FALSE, FALSE,
7208 EL_ACID_SPLASH_LEFT, -1, -1
7212 Xplant, TRUE, FALSE,
7213 EL_EMC_PLANT, -1, -1
7216 Yplant, FALSE, FALSE,
7217 EL_EMC_PLANT, -1, -1
7221 Xacid_1, TRUE, FALSE,
7225 Xacid_2, FALSE, FALSE,
7229 Xacid_3, FALSE, FALSE,
7233 Xacid_4, FALSE, FALSE,
7237 Xacid_5, FALSE, FALSE,
7241 Xacid_6, FALSE, FALSE,
7245 Xacid_7, FALSE, FALSE,
7249 Xacid_8, FALSE, FALSE,
7254 Xfake_acid_1, TRUE, FALSE,
7255 EL_EMC_FAKE_ACID, -1, -1
7258 Xfake_acid_2, FALSE, FALSE,
7259 EL_EMC_FAKE_ACID, -1, -1
7262 Xfake_acid_3, FALSE, FALSE,
7263 EL_EMC_FAKE_ACID, -1, -1
7266 Xfake_acid_4, FALSE, FALSE,
7267 EL_EMC_FAKE_ACID, -1, -1
7270 Xfake_acid_5, FALSE, FALSE,
7271 EL_EMC_FAKE_ACID, -1, -1
7274 Xfake_acid_6, FALSE, FALSE,
7275 EL_EMC_FAKE_ACID, -1, -1
7278 Xfake_acid_7, FALSE, FALSE,
7279 EL_EMC_FAKE_ACID, -1, -1
7282 Xfake_acid_8, FALSE, FALSE,
7283 EL_EMC_FAKE_ACID, -1, -1
7287 Xfake_acid_1_player, FALSE, FALSE,
7288 EL_EMC_FAKE_ACID, -1, -1
7291 Xfake_acid_2_player, FALSE, FALSE,
7292 EL_EMC_FAKE_ACID, -1, -1
7295 Xfake_acid_3_player, FALSE, FALSE,
7296 EL_EMC_FAKE_ACID, -1, -1
7299 Xfake_acid_4_player, FALSE, FALSE,
7300 EL_EMC_FAKE_ACID, -1, -1
7303 Xfake_acid_5_player, FALSE, FALSE,
7304 EL_EMC_FAKE_ACID, -1, -1
7307 Xfake_acid_6_player, FALSE, FALSE,
7308 EL_EMC_FAKE_ACID, -1, -1
7311 Xfake_acid_7_player, FALSE, FALSE,
7312 EL_EMC_FAKE_ACID, -1, -1
7315 Xfake_acid_8_player, FALSE, FALSE,
7316 EL_EMC_FAKE_ACID, -1, -1
7320 Xgrass, TRUE, FALSE,
7321 EL_EMC_GRASS, -1, -1
7324 Ygrass_nB, FALSE, FALSE,
7325 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
7328 Ygrass_eB, FALSE, FALSE,
7329 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
7332 Ygrass_sB, FALSE, FALSE,
7333 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
7336 Ygrass_wB, FALSE, FALSE,
7337 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
7345 Ydirt_nB, FALSE, FALSE,
7346 EL_SAND, ACTION_DIGGING, MV_BIT_UP
7349 Ydirt_eB, FALSE, FALSE,
7350 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
7353 Ydirt_sB, FALSE, FALSE,
7354 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
7357 Ydirt_wB, FALSE, FALSE,
7358 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
7362 Xandroid, TRUE, FALSE,
7363 EL_EMC_ANDROID, ACTION_ACTIVE, -1
7366 Xandroid_1_n, FALSE, FALSE,
7367 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
7370 Xandroid_2_n, FALSE, FALSE,
7371 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
7374 Xandroid_1_e, FALSE, FALSE,
7375 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
7378 Xandroid_2_e, FALSE, FALSE,
7379 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
7382 Xandroid_1_w, FALSE, FALSE,
7383 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
7386 Xandroid_2_w, FALSE, FALSE,
7387 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
7390 Xandroid_1_s, FALSE, FALSE,
7391 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
7394 Xandroid_2_s, FALSE, FALSE,
7395 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
7398 Yandroid_n, FALSE, FALSE,
7399 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
7402 Yandroid_nB, FALSE, TRUE,
7403 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
7406 Yandroid_ne, FALSE, FALSE,
7407 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
7410 Yandroid_neB, FALSE, TRUE,
7411 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
7414 Yandroid_e, FALSE, FALSE,
7415 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
7418 Yandroid_eB, FALSE, TRUE,
7419 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
7422 Yandroid_se, FALSE, FALSE,
7423 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
7426 Yandroid_seB, FALSE, TRUE,
7427 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
7430 Yandroid_s, FALSE, FALSE,
7431 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
7434 Yandroid_sB, FALSE, TRUE,
7435 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
7438 Yandroid_sw, FALSE, FALSE,
7439 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
7442 Yandroid_swB, FALSE, TRUE,
7443 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
7446 Yandroid_w, FALSE, FALSE,
7447 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
7450 Yandroid_wB, FALSE, TRUE,
7451 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
7454 Yandroid_nw, FALSE, FALSE,
7455 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
7458 Yandroid_nwB, FALSE, TRUE,
7459 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
7463 Xeater_n, TRUE, FALSE,
7464 EL_YAMYAM_UP, -1, -1
7467 Xeater_e, TRUE, FALSE,
7468 EL_YAMYAM_RIGHT, -1, -1
7471 Xeater_w, TRUE, FALSE,
7472 EL_YAMYAM_LEFT, -1, -1
7475 Xeater_s, TRUE, FALSE,
7476 EL_YAMYAM_DOWN, -1, -1
7479 Yeater_n, FALSE, FALSE,
7480 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
7483 Yeater_nB, FALSE, TRUE,
7484 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
7487 Yeater_e, FALSE, FALSE,
7488 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
7491 Yeater_eB, FALSE, TRUE,
7492 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
7495 Yeater_s, FALSE, FALSE,
7496 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
7499 Yeater_sB, FALSE, TRUE,
7500 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
7503 Yeater_w, FALSE, FALSE,
7504 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
7507 Yeater_wB, FALSE, TRUE,
7508 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
7511 Yeater_stone, FALSE, FALSE,
7512 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
7515 Yeater_spring, FALSE, FALSE,
7516 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
7520 Xalien, TRUE, FALSE,
7524 Xalien_pause, FALSE, FALSE,
7528 Yalien_n, FALSE, FALSE,
7529 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
7532 Yalien_nB, FALSE, TRUE,
7533 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
7536 Yalien_e, FALSE, FALSE,
7537 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
7540 Yalien_eB, FALSE, TRUE,
7541 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
7544 Yalien_s, FALSE, FALSE,
7545 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
7548 Yalien_sB, FALSE, TRUE,
7549 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
7552 Yalien_w, FALSE, FALSE,
7553 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
7556 Yalien_wB, FALSE, TRUE,
7557 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
7560 Yalien_stone, FALSE, FALSE,
7561 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
7564 Yalien_spring, FALSE, FALSE,
7565 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
7569 Xbug_1_n, TRUE, FALSE,
7573 Xbug_1_e, TRUE, FALSE,
7574 EL_BUG_RIGHT, -1, -1
7577 Xbug_1_s, TRUE, FALSE,
7581 Xbug_1_w, TRUE, FALSE,
7585 Xbug_2_n, FALSE, FALSE,
7589 Xbug_2_e, FALSE, FALSE,
7590 EL_BUG_RIGHT, -1, -1
7593 Xbug_2_s, FALSE, FALSE,
7597 Xbug_2_w, FALSE, FALSE,
7601 Ybug_n, FALSE, FALSE,
7602 EL_BUG, ACTION_MOVING, MV_BIT_UP
7605 Ybug_nB, FALSE, TRUE,
7606 EL_BUG, ACTION_MOVING, MV_BIT_UP
7609 Ybug_e, FALSE, FALSE,
7610 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
7613 Ybug_eB, FALSE, TRUE,
7614 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
7617 Ybug_s, FALSE, FALSE,
7618 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
7621 Ybug_sB, FALSE, TRUE,
7622 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
7625 Ybug_w, FALSE, FALSE,
7626 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
7629 Ybug_wB, FALSE, TRUE,
7630 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
7633 Ybug_w_n, FALSE, FALSE,
7634 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
7637 Ybug_n_e, FALSE, FALSE,
7638 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
7641 Ybug_e_s, FALSE, FALSE,
7642 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
7645 Ybug_s_w, FALSE, FALSE,
7646 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
7649 Ybug_e_n, FALSE, FALSE,
7650 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
7653 Ybug_s_e, FALSE, FALSE,
7654 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
7657 Ybug_w_s, FALSE, FALSE,
7658 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
7661 Ybug_n_w, FALSE, FALSE,
7662 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
7665 Ybug_stone, FALSE, FALSE,
7666 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
7669 Ybug_spring, FALSE, FALSE,
7670 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
7674 Xtank_1_n, TRUE, FALSE,
7675 EL_SPACESHIP_UP, -1, -1
7678 Xtank_1_e, TRUE, FALSE,
7679 EL_SPACESHIP_RIGHT, -1, -1
7682 Xtank_1_s, TRUE, FALSE,
7683 EL_SPACESHIP_DOWN, -1, -1
7686 Xtank_1_w, TRUE, FALSE,
7687 EL_SPACESHIP_LEFT, -1, -1
7690 Xtank_2_n, FALSE, FALSE,
7691 EL_SPACESHIP_UP, -1, -1
7694 Xtank_2_e, FALSE, FALSE,
7695 EL_SPACESHIP_RIGHT, -1, -1
7698 Xtank_2_s, FALSE, FALSE,
7699 EL_SPACESHIP_DOWN, -1, -1
7702 Xtank_2_w, FALSE, FALSE,
7703 EL_SPACESHIP_LEFT, -1, -1
7706 Ytank_n, FALSE, FALSE,
7707 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
7710 Ytank_nB, FALSE, TRUE,
7711 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
7714 Ytank_e, FALSE, FALSE,
7715 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
7718 Ytank_eB, FALSE, TRUE,
7719 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
7722 Ytank_s, FALSE, FALSE,
7723 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
7726 Ytank_sB, FALSE, TRUE,
7727 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
7730 Ytank_w, FALSE, FALSE,
7731 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
7734 Ytank_wB, FALSE, TRUE,
7735 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
7738 Ytank_w_n, FALSE, FALSE,
7739 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
7742 Ytank_n_e, FALSE, FALSE,
7743 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
7746 Ytank_e_s, FALSE, FALSE,
7747 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
7750 Ytank_s_w, FALSE, FALSE,
7751 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
7754 Ytank_e_n, FALSE, FALSE,
7755 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
7758 Ytank_s_e, FALSE, FALSE,
7759 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
7762 Ytank_w_s, FALSE, FALSE,
7763 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
7766 Ytank_n_w, FALSE, FALSE,
7767 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
7770 Ytank_stone, FALSE, FALSE,
7771 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
7774 Ytank_spring, FALSE, FALSE,
7775 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
7779 Xemerald, TRUE, FALSE,
7783 Xemerald_pause, FALSE, FALSE,
7787 Xemerald_fall, FALSE, FALSE,
7791 Xemerald_shine, FALSE, FALSE,
7792 EL_EMERALD, ACTION_TWINKLING, -1
7795 Yemerald_s, FALSE, FALSE,
7796 EL_EMERALD, ACTION_FALLING, -1
7799 Yemerald_sB, FALSE, TRUE,
7800 EL_EMERALD, ACTION_FALLING, -1
7803 Yemerald_e, FALSE, FALSE,
7804 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
7807 Yemerald_eB, FALSE, TRUE,
7808 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
7811 Yemerald_w, FALSE, FALSE,
7812 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
7815 Yemerald_wB, FALSE, TRUE,
7816 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
7819 Yemerald_blank, FALSE, FALSE,
7820 EL_EMERALD, ACTION_COLLECTING, -1
7824 Xdiamond, TRUE, FALSE,
7828 Xdiamond_pause, FALSE, FALSE,
7832 Xdiamond_fall, FALSE, FALSE,
7836 Xdiamond_shine, FALSE, FALSE,
7837 EL_DIAMOND, ACTION_TWINKLING, -1
7840 Ydiamond_s, FALSE, FALSE,
7841 EL_DIAMOND, ACTION_FALLING, -1
7844 Ydiamond_sB, FALSE, TRUE,
7845 EL_DIAMOND, ACTION_FALLING, -1
7848 Ydiamond_e, FALSE, FALSE,
7849 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
7852 Ydiamond_eB, FALSE, TRUE,
7853 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
7856 Ydiamond_w, FALSE, FALSE,
7857 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
7860 Ydiamond_wB, FALSE, TRUE,
7861 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
7864 Ydiamond_blank, FALSE, FALSE,
7865 EL_DIAMOND, ACTION_COLLECTING, -1
7868 Ydiamond_stone, FALSE, FALSE,
7869 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
7873 Xstone, TRUE, FALSE,
7877 Xstone_pause, FALSE, FALSE,
7881 Xstone_fall, FALSE, FALSE,
7885 Ystone_s, FALSE, FALSE,
7886 EL_ROCK, ACTION_FALLING, -1
7889 Ystone_sB, FALSE, TRUE,
7890 EL_ROCK, ACTION_FALLING, -1
7893 Ystone_e, FALSE, FALSE,
7894 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
7897 Ystone_eB, FALSE, TRUE,
7898 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
7901 Ystone_w, FALSE, FALSE,
7902 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
7905 Ystone_wB, FALSE, TRUE,
7906 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
7914 Xbomb_pause, FALSE, FALSE,
7918 Xbomb_fall, FALSE, FALSE,
7922 Ybomb_s, FALSE, FALSE,
7923 EL_BOMB, ACTION_FALLING, -1
7926 Ybomb_sB, FALSE, TRUE,
7927 EL_BOMB, ACTION_FALLING, -1
7930 Ybomb_e, FALSE, FALSE,
7931 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
7934 Ybomb_eB, FALSE, TRUE,
7935 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
7938 Ybomb_w, FALSE, FALSE,
7939 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
7942 Ybomb_wB, FALSE, TRUE,
7943 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
7946 Ybomb_blank, FALSE, FALSE,
7947 EL_BOMB, ACTION_ACTIVATING, -1
7955 Xnut_pause, FALSE, FALSE,
7959 Xnut_fall, FALSE, FALSE,
7963 Ynut_s, FALSE, FALSE,
7964 EL_NUT, ACTION_FALLING, -1
7967 Ynut_sB, FALSE, TRUE,
7968 EL_NUT, ACTION_FALLING, -1
7971 Ynut_e, FALSE, FALSE,
7972 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
7975 Ynut_eB, FALSE, TRUE,
7976 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
7979 Ynut_w, FALSE, FALSE,
7980 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
7983 Ynut_wB, FALSE, TRUE,
7984 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
7987 Ynut_stone, FALSE, FALSE,
7988 EL_NUT, ACTION_BREAKING, -1
7992 Xspring, TRUE, FALSE,
7996 Xspring_pause, FALSE, FALSE,
8000 Xspring_e, TRUE, FALSE,
8001 EL_SPRING_RIGHT, -1, -1
8004 Xspring_w, TRUE, FALSE,
8005 EL_SPRING_LEFT, -1, -1
8008 Xspring_fall, FALSE, FALSE,
8012 Yspring_s, FALSE, FALSE,
8013 EL_SPRING, ACTION_FALLING, -1
8016 Yspring_sB, FALSE, TRUE,
8017 EL_SPRING, ACTION_FALLING, -1
8020 Yspring_e, FALSE, FALSE,
8021 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
8024 Yspring_eB, FALSE, TRUE,
8025 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
8028 Yspring_w, FALSE, FALSE,
8029 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
8032 Yspring_wB, FALSE, TRUE,
8033 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
8036 Yspring_alien_e, FALSE, FALSE,
8037 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
8040 Yspring_alien_eB, FALSE, TRUE,
8041 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
8044 Yspring_alien_w, FALSE, FALSE,
8045 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
8048 Yspring_alien_wB, FALSE, TRUE,
8049 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
8053 Xpush_emerald_e, FALSE, FALSE,
8054 EL_EMERALD, -1, MV_BIT_RIGHT
8057 Xpush_emerald_w, FALSE, FALSE,
8058 EL_EMERALD, -1, MV_BIT_LEFT
8061 Xpush_diamond_e, FALSE, FALSE,
8062 EL_DIAMOND, -1, MV_BIT_RIGHT
8065 Xpush_diamond_w, FALSE, FALSE,
8066 EL_DIAMOND, -1, MV_BIT_LEFT
8069 Xpush_stone_e, FALSE, FALSE,
8070 EL_ROCK, -1, MV_BIT_RIGHT
8073 Xpush_stone_w, FALSE, FALSE,
8074 EL_ROCK, -1, MV_BIT_LEFT
8077 Xpush_bomb_e, FALSE, FALSE,
8078 EL_BOMB, -1, MV_BIT_RIGHT
8081 Xpush_bomb_w, FALSE, FALSE,
8082 EL_BOMB, -1, MV_BIT_LEFT
8085 Xpush_nut_e, FALSE, FALSE,
8086 EL_NUT, -1, MV_BIT_RIGHT
8089 Xpush_nut_w, FALSE, FALSE,
8090 EL_NUT, -1, MV_BIT_LEFT
8093 Xpush_spring_e, FALSE, FALSE,
8094 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
8097 Xpush_spring_w, FALSE, FALSE,
8098 EL_SPRING_LEFT, -1, MV_BIT_LEFT
8102 Xdynamite, TRUE, FALSE,
8103 EL_EM_DYNAMITE, -1, -1
8106 Ydynamite_blank, FALSE, FALSE,
8107 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
8110 Xdynamite_1, TRUE, FALSE,
8111 EL_EM_DYNAMITE_ACTIVE, -1, -1
8114 Xdynamite_2, FALSE, FALSE,
8115 EL_EM_DYNAMITE_ACTIVE, -1, -1
8118 Xdynamite_3, FALSE, FALSE,
8119 EL_EM_DYNAMITE_ACTIVE, -1, -1
8122 Xdynamite_4, FALSE, FALSE,
8123 EL_EM_DYNAMITE_ACTIVE, -1, -1
8127 Xkey_1, TRUE, FALSE,
8131 Xkey_2, TRUE, FALSE,
8135 Xkey_3, TRUE, FALSE,
8139 Xkey_4, TRUE, FALSE,
8143 Xkey_5, TRUE, FALSE,
8144 EL_EMC_KEY_5, -1, -1
8147 Xkey_6, TRUE, FALSE,
8148 EL_EMC_KEY_6, -1, -1
8151 Xkey_7, TRUE, FALSE,
8152 EL_EMC_KEY_7, -1, -1
8155 Xkey_8, TRUE, FALSE,
8156 EL_EMC_KEY_8, -1, -1
8160 Xdoor_1, TRUE, FALSE,
8161 EL_EM_GATE_1, -1, -1
8164 Xdoor_2, TRUE, FALSE,
8165 EL_EM_GATE_2, -1, -1
8168 Xdoor_3, TRUE, FALSE,
8169 EL_EM_GATE_3, -1, -1
8172 Xdoor_4, TRUE, FALSE,
8173 EL_EM_GATE_4, -1, -1
8176 Xdoor_5, TRUE, FALSE,
8177 EL_EMC_GATE_5, -1, -1
8180 Xdoor_6, TRUE, FALSE,
8181 EL_EMC_GATE_6, -1, -1
8184 Xdoor_7, TRUE, FALSE,
8185 EL_EMC_GATE_7, -1, -1
8188 Xdoor_8, TRUE, FALSE,
8189 EL_EMC_GATE_8, -1, -1
8193 Xfake_door_1, TRUE, FALSE,
8194 EL_EM_GATE_1_GRAY, -1, -1
8197 Xfake_door_2, TRUE, FALSE,
8198 EL_EM_GATE_2_GRAY, -1, -1
8201 Xfake_door_3, TRUE, FALSE,
8202 EL_EM_GATE_3_GRAY, -1, -1
8205 Xfake_door_4, TRUE, FALSE,
8206 EL_EM_GATE_4_GRAY, -1, -1
8209 Xfake_door_5, TRUE, FALSE,
8210 EL_EMC_GATE_5_GRAY, -1, -1
8213 Xfake_door_6, TRUE, FALSE,
8214 EL_EMC_GATE_6_GRAY, -1, -1
8217 Xfake_door_7, TRUE, FALSE,
8218 EL_EMC_GATE_7_GRAY, -1, -1
8221 Xfake_door_8, TRUE, FALSE,
8222 EL_EMC_GATE_8_GRAY, -1, -1
8226 Xballoon, TRUE, FALSE,
8230 Yballoon_n, FALSE, FALSE,
8231 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
8234 Yballoon_nB, FALSE, TRUE,
8235 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
8238 Yballoon_e, FALSE, FALSE,
8239 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
8242 Yballoon_eB, FALSE, TRUE,
8243 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
8246 Yballoon_s, FALSE, FALSE,
8247 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
8250 Yballoon_sB, FALSE, TRUE,
8251 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
8254 Yballoon_w, FALSE, FALSE,
8255 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
8258 Yballoon_wB, FALSE, TRUE,
8259 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
8263 Xball_1, TRUE, FALSE,
8264 EL_EMC_MAGIC_BALL, -1, -1
8267 Yball_1, FALSE, FALSE,
8268 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
8271 Xball_2, FALSE, FALSE,
8272 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
8275 Yball_2, FALSE, FALSE,
8276 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
8279 Yball_blank, FALSE, FALSE,
8280 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
8284 Xamoeba_1, TRUE, FALSE,
8285 EL_AMOEBA_DRY, ACTION_OTHER, -1
8288 Xamoeba_2, FALSE, FALSE,
8289 EL_AMOEBA_DRY, ACTION_OTHER, -1
8292 Xamoeba_3, FALSE, FALSE,
8293 EL_AMOEBA_DRY, ACTION_OTHER, -1
8296 Xamoeba_4, FALSE, FALSE,
8297 EL_AMOEBA_DRY, ACTION_OTHER, -1
8300 Xamoeba_5, TRUE, FALSE,
8301 EL_AMOEBA_WET, ACTION_OTHER, -1
8304 Xamoeba_6, FALSE, FALSE,
8305 EL_AMOEBA_WET, ACTION_OTHER, -1
8308 Xamoeba_7, FALSE, FALSE,
8309 EL_AMOEBA_WET, ACTION_OTHER, -1
8312 Xamoeba_8, FALSE, FALSE,
8313 EL_AMOEBA_WET, ACTION_OTHER, -1
8318 EL_AMOEBA_DROP, ACTION_GROWING, -1
8321 Xdrip_fall, FALSE, FALSE,
8322 EL_AMOEBA_DROP, -1, -1
8325 Xdrip_stretch, FALSE, FALSE,
8326 EL_AMOEBA_DROP, ACTION_FALLING, -1
8329 Xdrip_stretchB, FALSE, TRUE,
8330 EL_AMOEBA_DROP, ACTION_FALLING, -1
8333 Ydrip_1_s, FALSE, FALSE,
8334 EL_AMOEBA_DROP, ACTION_FALLING, -1
8337 Ydrip_1_sB, FALSE, TRUE,
8338 EL_AMOEBA_DROP, ACTION_FALLING, -1
8341 Ydrip_2_s, FALSE, FALSE,
8342 EL_AMOEBA_DROP, ACTION_FALLING, -1
8345 Ydrip_2_sB, FALSE, TRUE,
8346 EL_AMOEBA_DROP, ACTION_FALLING, -1
8350 Xwonderwall, TRUE, FALSE,
8351 EL_MAGIC_WALL, -1, -1
8354 Ywonderwall, FALSE, FALSE,
8355 EL_MAGIC_WALL, ACTION_ACTIVE, -1
8359 Xwheel, TRUE, FALSE,
8360 EL_ROBOT_WHEEL, -1, -1
8363 Ywheel, FALSE, FALSE,
8364 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
8368 Xswitch, TRUE, FALSE,
8369 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
8372 Yswitch, FALSE, FALSE,
8373 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
8377 Xbumper, TRUE, FALSE,
8378 EL_EMC_SPRING_BUMPER, -1, -1
8381 Ybumper, FALSE, FALSE,
8382 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
8386 Xacid_nw, TRUE, FALSE,
8387 EL_ACID_POOL_TOPLEFT, -1, -1
8390 Xacid_ne, TRUE, FALSE,
8391 EL_ACID_POOL_TOPRIGHT, -1, -1
8394 Xacid_sw, TRUE, FALSE,
8395 EL_ACID_POOL_BOTTOMLEFT, -1, -1
8398 Xacid_s, TRUE, FALSE,
8399 EL_ACID_POOL_BOTTOM, -1, -1
8402 Xacid_se, TRUE, FALSE,
8403 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
8407 Xfake_blank, TRUE, FALSE,
8408 EL_INVISIBLE_WALL, -1, -1
8411 Yfake_blank, FALSE, FALSE,
8412 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
8416 Xfake_grass, TRUE, FALSE,
8417 EL_EMC_FAKE_GRASS, -1, -1
8420 Yfake_grass, FALSE, FALSE,
8421 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
8425 Xfake_amoeba, TRUE, FALSE,
8426 EL_EMC_DRIPPER, -1, -1
8429 Yfake_amoeba, FALSE, FALSE,
8430 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
8434 Xlenses, TRUE, FALSE,
8435 EL_EMC_LENSES, -1, -1
8439 Xmagnify, TRUE, FALSE,
8440 EL_EMC_MAGNIFIER, -1, -1
8445 EL_QUICKSAND_EMPTY, -1, -1
8448 Xsand_stone, TRUE, FALSE,
8449 EL_QUICKSAND_FULL, -1, -1
8452 Xsand_stonein_1, FALSE, TRUE,
8453 EL_ROCK, ACTION_FILLING, -1
8456 Xsand_stonein_2, FALSE, TRUE,
8457 EL_ROCK, ACTION_FILLING, -1
8460 Xsand_stonein_3, FALSE, TRUE,
8461 EL_ROCK, ACTION_FILLING, -1
8464 Xsand_stonein_4, FALSE, TRUE,
8465 EL_ROCK, ACTION_FILLING, -1
8468 Xsand_sandstone_1, FALSE, FALSE,
8469 EL_QUICKSAND_FILLING, -1, -1
8472 Xsand_sandstone_2, FALSE, FALSE,
8473 EL_QUICKSAND_FILLING, -1, -1
8476 Xsand_sandstone_3, FALSE, FALSE,
8477 EL_QUICKSAND_FILLING, -1, -1
8480 Xsand_sandstone_4, FALSE, FALSE,
8481 EL_QUICKSAND_FILLING, -1, -1
8484 Xsand_stonesand_1, FALSE, FALSE,
8485 EL_QUICKSAND_EMPTYING, -1, -1
8488 Xsand_stonesand_2, FALSE, FALSE,
8489 EL_QUICKSAND_EMPTYING, -1, -1
8492 Xsand_stonesand_3, FALSE, FALSE,
8493 EL_QUICKSAND_EMPTYING, -1, -1
8496 Xsand_stonesand_4, FALSE, FALSE,
8497 EL_QUICKSAND_EMPTYING, -1, -1
8500 Xsand_stoneout_1, FALSE, FALSE,
8501 EL_ROCK, ACTION_EMPTYING, -1
8504 Xsand_stoneout_2, FALSE, FALSE,
8505 EL_ROCK, ACTION_EMPTYING, -1
8508 Xsand_stonesand_quickout_1, FALSE, FALSE,
8509 EL_QUICKSAND_EMPTYING, -1, -1
8512 Xsand_stonesand_quickout_2, FALSE, FALSE,
8513 EL_QUICKSAND_EMPTYING, -1, -1
8517 Xslide_ns, TRUE, FALSE,
8518 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
8521 Yslide_ns_blank, FALSE, FALSE,
8522 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
8525 Xslide_ew, TRUE, FALSE,
8526 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
8529 Yslide_ew_blank, FALSE, FALSE,
8530 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
8534 Xwind_n, TRUE, FALSE,
8535 EL_BALLOON_SWITCH_UP, -1, -1
8538 Xwind_e, TRUE, FALSE,
8539 EL_BALLOON_SWITCH_RIGHT, -1, -1
8542 Xwind_s, TRUE, FALSE,
8543 EL_BALLOON_SWITCH_DOWN, -1, -1
8546 Xwind_w, TRUE, FALSE,
8547 EL_BALLOON_SWITCH_LEFT, -1, -1
8550 Xwind_any, TRUE, FALSE,
8551 EL_BALLOON_SWITCH_ANY, -1, -1
8554 Xwind_stop, TRUE, FALSE,
8555 EL_BALLOON_SWITCH_NONE, -1, -1
8560 EL_EM_EXIT_CLOSED, -1, -1
8563 Xexit_1, TRUE, FALSE,
8564 EL_EM_EXIT_OPEN, -1, -1
8567 Xexit_2, FALSE, FALSE,
8568 EL_EM_EXIT_OPEN, -1, -1
8571 Xexit_3, FALSE, FALSE,
8572 EL_EM_EXIT_OPEN, -1, -1
8576 Xpause, FALSE, FALSE,
8581 Xwall_1, TRUE, FALSE,
8585 Xwall_2, TRUE, FALSE,
8586 EL_EMC_WALL_14, -1, -1
8589 Xwall_3, TRUE, FALSE,
8590 EL_EMC_WALL_15, -1, -1
8593 Xwall_4, TRUE, FALSE,
8594 EL_EMC_WALL_16, -1, -1
8598 Xroundwall_1, TRUE, FALSE,
8599 EL_WALL_SLIPPERY, -1, -1
8602 Xroundwall_2, TRUE, FALSE,
8603 EL_EMC_WALL_SLIPPERY_2, -1, -1
8606 Xroundwall_3, TRUE, FALSE,
8607 EL_EMC_WALL_SLIPPERY_3, -1, -1
8610 Xroundwall_4, TRUE, FALSE,
8611 EL_EMC_WALL_SLIPPERY_4, -1, -1
8615 Xsteel_1, TRUE, FALSE,
8616 EL_STEELWALL, -1, -1
8619 Xsteel_2, TRUE, FALSE,
8620 EL_EMC_STEELWALL_2, -1, -1
8623 Xsteel_3, TRUE, FALSE,
8624 EL_EMC_STEELWALL_3, -1, -1
8627 Xsteel_4, TRUE, FALSE,
8628 EL_EMC_STEELWALL_4, -1, -1
8632 Xdecor_1, TRUE, FALSE,
8633 EL_EMC_WALL_8, -1, -1
8636 Xdecor_2, TRUE, FALSE,
8637 EL_EMC_WALL_6, -1, -1
8640 Xdecor_3, TRUE, FALSE,
8641 EL_EMC_WALL_4, -1, -1
8644 Xdecor_4, TRUE, FALSE,
8645 EL_EMC_WALL_7, -1, -1
8648 Xdecor_5, TRUE, FALSE,
8649 EL_EMC_WALL_5, -1, -1
8652 Xdecor_6, TRUE, FALSE,
8653 EL_EMC_WALL_9, -1, -1
8656 Xdecor_7, TRUE, FALSE,
8657 EL_EMC_WALL_10, -1, -1
8660 Xdecor_8, TRUE, FALSE,
8661 EL_EMC_WALL_1, -1, -1
8664 Xdecor_9, TRUE, FALSE,
8665 EL_EMC_WALL_2, -1, -1
8668 Xdecor_10, TRUE, FALSE,
8669 EL_EMC_WALL_3, -1, -1
8672 Xdecor_11, TRUE, FALSE,
8673 EL_EMC_WALL_11, -1, -1
8676 Xdecor_12, TRUE, FALSE,
8677 EL_EMC_WALL_12, -1, -1
8681 Xalpha_0, TRUE, FALSE,
8682 EL_CHAR('0'), -1, -1
8685 Xalpha_1, TRUE, FALSE,
8686 EL_CHAR('1'), -1, -1
8689 Xalpha_2, TRUE, FALSE,
8690 EL_CHAR('2'), -1, -1
8693 Xalpha_3, TRUE, FALSE,
8694 EL_CHAR('3'), -1, -1
8697 Xalpha_4, TRUE, FALSE,
8698 EL_CHAR('4'), -1, -1
8701 Xalpha_5, TRUE, FALSE,
8702 EL_CHAR('5'), -1, -1
8705 Xalpha_6, TRUE, FALSE,
8706 EL_CHAR('6'), -1, -1
8709 Xalpha_7, TRUE, FALSE,
8710 EL_CHAR('7'), -1, -1
8713 Xalpha_8, TRUE, FALSE,
8714 EL_CHAR('8'), -1, -1
8717 Xalpha_9, TRUE, FALSE,
8718 EL_CHAR('9'), -1, -1
8721 Xalpha_excla, TRUE, FALSE,
8722 EL_CHAR('!'), -1, -1
8725 Xalpha_apost, TRUE, FALSE,
8726 EL_CHAR('\''), -1, -1
8729 Xalpha_comma, TRUE, FALSE,
8730 EL_CHAR(','), -1, -1
8733 Xalpha_minus, TRUE, FALSE,
8734 EL_CHAR('-'), -1, -1
8737 Xalpha_perio, TRUE, FALSE,
8738 EL_CHAR('.'), -1, -1
8741 Xalpha_colon, TRUE, FALSE,
8742 EL_CHAR(':'), -1, -1
8745 Xalpha_quest, TRUE, FALSE,
8746 EL_CHAR('?'), -1, -1
8749 Xalpha_a, TRUE, FALSE,
8750 EL_CHAR('A'), -1, -1
8753 Xalpha_b, TRUE, FALSE,
8754 EL_CHAR('B'), -1, -1
8757 Xalpha_c, TRUE, FALSE,
8758 EL_CHAR('C'), -1, -1
8761 Xalpha_d, TRUE, FALSE,
8762 EL_CHAR('D'), -1, -1
8765 Xalpha_e, TRUE, FALSE,
8766 EL_CHAR('E'), -1, -1
8769 Xalpha_f, TRUE, FALSE,
8770 EL_CHAR('F'), -1, -1
8773 Xalpha_g, TRUE, FALSE,
8774 EL_CHAR('G'), -1, -1
8777 Xalpha_h, TRUE, FALSE,
8778 EL_CHAR('H'), -1, -1
8781 Xalpha_i, TRUE, FALSE,
8782 EL_CHAR('I'), -1, -1
8785 Xalpha_j, TRUE, FALSE,
8786 EL_CHAR('J'), -1, -1
8789 Xalpha_k, TRUE, FALSE,
8790 EL_CHAR('K'), -1, -1
8793 Xalpha_l, TRUE, FALSE,
8794 EL_CHAR('L'), -1, -1
8797 Xalpha_m, TRUE, FALSE,
8798 EL_CHAR('M'), -1, -1
8801 Xalpha_n, TRUE, FALSE,
8802 EL_CHAR('N'), -1, -1
8805 Xalpha_o, TRUE, FALSE,
8806 EL_CHAR('O'), -1, -1
8809 Xalpha_p, TRUE, FALSE,
8810 EL_CHAR('P'), -1, -1
8813 Xalpha_q, TRUE, FALSE,
8814 EL_CHAR('Q'), -1, -1
8817 Xalpha_r, TRUE, FALSE,
8818 EL_CHAR('R'), -1, -1
8821 Xalpha_s, TRUE, FALSE,
8822 EL_CHAR('S'), -1, -1
8825 Xalpha_t, TRUE, FALSE,
8826 EL_CHAR('T'), -1, -1
8829 Xalpha_u, TRUE, FALSE,
8830 EL_CHAR('U'), -1, -1
8833 Xalpha_v, TRUE, FALSE,
8834 EL_CHAR('V'), -1, -1
8837 Xalpha_w, TRUE, FALSE,
8838 EL_CHAR('W'), -1, -1
8841 Xalpha_x, TRUE, FALSE,
8842 EL_CHAR('X'), -1, -1
8845 Xalpha_y, TRUE, FALSE,
8846 EL_CHAR('Y'), -1, -1
8849 Xalpha_z, TRUE, FALSE,
8850 EL_CHAR('Z'), -1, -1
8853 Xalpha_arrow_e, TRUE, FALSE,
8854 EL_CHAR('>'), -1, -1
8857 Xalpha_arrow_w, TRUE, FALSE,
8858 EL_CHAR('<'), -1, -1
8861 Xalpha_copyr, TRUE, FALSE,
8862 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
8866 Ykey_1_blank, FALSE, FALSE,
8867 EL_EM_KEY_1, ACTION_COLLECTING, -1
8870 Ykey_2_blank, FALSE, FALSE,
8871 EL_EM_KEY_2, ACTION_COLLECTING, -1
8874 Ykey_3_blank, FALSE, FALSE,
8875 EL_EM_KEY_3, ACTION_COLLECTING, -1
8878 Ykey_4_blank, FALSE, FALSE,
8879 EL_EM_KEY_4, ACTION_COLLECTING, -1
8882 Ykey_5_blank, FALSE, FALSE,
8883 EL_EMC_KEY_5, ACTION_COLLECTING, -1
8886 Ykey_6_blank, FALSE, FALSE,
8887 EL_EMC_KEY_6, ACTION_COLLECTING, -1
8890 Ykey_7_blank, FALSE, FALSE,
8891 EL_EMC_KEY_7, ACTION_COLLECTING, -1
8894 Ykey_8_blank, FALSE, FALSE,
8895 EL_EMC_KEY_8, ACTION_COLLECTING, -1
8898 Ylenses_blank, FALSE, FALSE,
8899 EL_EMC_LENSES, ACTION_COLLECTING, -1
8902 Ymagnify_blank, FALSE, FALSE,
8903 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
8906 Ygrass_blank, FALSE, FALSE,
8907 EL_EMC_GRASS, ACTION_SNAPPING, -1
8910 Ydirt_blank, FALSE, FALSE,
8911 EL_SAND, ACTION_SNAPPING, -1
8920 static struct Mapping_EM_to_RND_player
8929 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
8933 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
8937 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
8941 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
8945 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
8949 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
8953 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
8957 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
8961 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
8965 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
8969 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
8973 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
8977 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
8981 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
8985 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
8989 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
8993 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
8997 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
9001 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
9005 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
9009 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
9013 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
9017 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
9021 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
9025 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
9029 EL_PLAYER_1, ACTION_DEFAULT, -1,
9033 EL_PLAYER_2, ACTION_DEFAULT, -1,
9037 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
9041 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
9045 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
9049 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
9053 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
9057 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
9061 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
9065 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
9069 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
9073 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
9077 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
9081 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
9085 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
9089 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
9093 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
9097 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
9101 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
9105 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
9109 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
9113 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
9117 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
9121 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
9125 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
9129 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
9133 EL_PLAYER_3, ACTION_DEFAULT, -1,
9137 EL_PLAYER_4, ACTION_DEFAULT, -1,
9146 int map_element_RND_to_EM_cave(int element_rnd)
9148 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
9149 static boolean mapping_initialized = FALSE;
9151 if (!mapping_initialized)
9155 // return "Xalpha_quest" for all undefined elements in mapping array
9156 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9157 mapping_RND_to_EM[i] = Xalpha_quest;
9159 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
9160 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
9161 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
9162 em_object_mapping_list[i].element_em;
9164 mapping_initialized = TRUE;
9167 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
9169 Warn("invalid RND level element %d", element_rnd);
9174 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
9177 int map_element_EM_to_RND_cave(int element_em_cave)
9179 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
9180 static boolean mapping_initialized = FALSE;
9182 if (!mapping_initialized)
9186 // return "EL_UNKNOWN" for all undefined elements in mapping array
9187 for (i = 0; i < GAME_TILE_MAX; i++)
9188 mapping_EM_to_RND[i] = EL_UNKNOWN;
9190 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
9191 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
9192 em_object_mapping_list[i].element_rnd;
9194 mapping_initialized = TRUE;
9197 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
9199 Warn("invalid EM cave element %d", element_em_cave);
9204 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
9207 int map_element_EM_to_RND_game(int element_em_game)
9209 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
9210 static boolean mapping_initialized = FALSE;
9212 if (!mapping_initialized)
9216 // return "EL_UNKNOWN" for all undefined elements in mapping array
9217 for (i = 0; i < GAME_TILE_MAX; i++)
9218 mapping_EM_to_RND[i] = EL_UNKNOWN;
9220 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
9221 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
9222 em_object_mapping_list[i].element_rnd;
9224 mapping_initialized = TRUE;
9227 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
9229 Warn("invalid EM game element %d", element_em_game);
9234 return mapping_EM_to_RND[element_em_game];
9237 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
9239 struct LevelInfo_EM *level_em = level->native_em_level;
9240 struct CAVE *cav = level_em->cav;
9243 for (i = 0; i < GAME_TILE_MAX; i++)
9244 cav->android_array[i] = Cblank;
9246 for (i = 0; i < level->num_android_clone_elements; i++)
9248 int element_rnd = level->android_clone_element[i];
9249 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
9251 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
9252 if (em_object_mapping_list[j].element_rnd == element_rnd)
9253 cav->android_array[em_object_mapping_list[j].element_em] =
9258 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
9260 struct LevelInfo_EM *level_em = level->native_em_level;
9261 struct CAVE *cav = level_em->cav;
9264 level->num_android_clone_elements = 0;
9266 for (i = 0; i < GAME_TILE_MAX; i++)
9268 int element_em_cave = cav->android_array[i];
9270 boolean element_found = FALSE;
9272 if (element_em_cave == Cblank)
9275 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
9277 for (j = 0; j < level->num_android_clone_elements; j++)
9278 if (level->android_clone_element[j] == element_rnd)
9279 element_found = TRUE;
9283 level->android_clone_element[level->num_android_clone_elements++] =
9286 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
9291 if (level->num_android_clone_elements == 0)
9293 level->num_android_clone_elements = 1;
9294 level->android_clone_element[0] = EL_EMPTY;
9298 int map_direction_RND_to_EM(int direction)
9300 return (direction == MV_UP ? 0 :
9301 direction == MV_RIGHT ? 1 :
9302 direction == MV_DOWN ? 2 :
9303 direction == MV_LEFT ? 3 :
9307 int map_direction_EM_to_RND(int direction)
9309 return (direction == 0 ? MV_UP :
9310 direction == 1 ? MV_RIGHT :
9311 direction == 2 ? MV_DOWN :
9312 direction == 3 ? MV_LEFT :
9316 int map_element_RND_to_SP(int element_rnd)
9318 int element_sp = 0x20; // map unknown elements to yellow "hardware"
9320 if (element_rnd >= EL_SP_START &&
9321 element_rnd <= EL_SP_END)
9322 element_sp = element_rnd - EL_SP_START;
9323 else if (element_rnd == EL_EMPTY_SPACE)
9325 else if (element_rnd == EL_INVISIBLE_WALL)
9331 int map_element_SP_to_RND(int element_sp)
9333 int element_rnd = EL_UNKNOWN;
9335 if (element_sp >= 0x00 &&
9337 element_rnd = EL_SP_START + element_sp;
9338 else if (element_sp == 0x28)
9339 element_rnd = EL_INVISIBLE_WALL;
9344 int map_action_SP_to_RND(int action_sp)
9348 case actActive: return ACTION_ACTIVE;
9349 case actImpact: return ACTION_IMPACT;
9350 case actExploding: return ACTION_EXPLODING;
9351 case actDigging: return ACTION_DIGGING;
9352 case actSnapping: return ACTION_SNAPPING;
9353 case actCollecting: return ACTION_COLLECTING;
9354 case actPassing: return ACTION_PASSING;
9355 case actPushing: return ACTION_PUSHING;
9356 case actDropping: return ACTION_DROPPING;
9358 default: return ACTION_DEFAULT;
9362 int map_element_RND_to_MM(int element_rnd)
9364 return (element_rnd >= EL_MM_START_1 &&
9365 element_rnd <= EL_MM_END_1 ?
9366 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
9368 element_rnd >= EL_MM_START_2 &&
9369 element_rnd <= EL_MM_END_2 ?
9370 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
9372 element_rnd >= EL_MM_START_3 &&
9373 element_rnd <= EL_MM_END_3 ?
9374 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
9376 element_rnd >= EL_CHAR_START &&
9377 element_rnd <= EL_CHAR_END ?
9378 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
9380 element_rnd >= EL_MM_RUNTIME_START &&
9381 element_rnd <= EL_MM_RUNTIME_END ?
9382 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
9384 EL_MM_EMPTY_NATIVE);
9387 int map_element_MM_to_RND(int element_mm)
9389 return (element_mm == EL_MM_EMPTY_NATIVE ||
9390 element_mm == EL_DF_EMPTY_NATIVE ?
9393 element_mm >= EL_MM_START_1_NATIVE &&
9394 element_mm <= EL_MM_END_1_NATIVE ?
9395 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
9397 element_mm >= EL_MM_START_2_NATIVE &&
9398 element_mm <= EL_MM_END_2_NATIVE ?
9399 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
9401 element_mm >= EL_MM_START_3_NATIVE &&
9402 element_mm <= EL_MM_END_3_NATIVE ?
9403 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
9405 element_mm >= EL_MM_CHAR_START_NATIVE &&
9406 element_mm <= EL_MM_CHAR_END_NATIVE ?
9407 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
9409 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
9410 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
9411 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
9416 int map_action_MM_to_RND(int action_mm)
9418 // all MM actions are defined to exactly match their RND counterparts
9422 int map_sound_MM_to_RND(int sound_mm)
9426 case SND_MM_GAME_LEVELTIME_CHARGING:
9427 return SND_GAME_LEVELTIME_CHARGING;
9429 case SND_MM_GAME_HEALTH_CHARGING:
9430 return SND_GAME_HEALTH_CHARGING;
9433 return SND_UNDEFINED;
9437 int map_mm_wall_element(int element)
9439 return (element >= EL_MM_STEEL_WALL_START &&
9440 element <= EL_MM_STEEL_WALL_END ?
9443 element >= EL_MM_WOODEN_WALL_START &&
9444 element <= EL_MM_WOODEN_WALL_END ?
9447 element >= EL_MM_ICE_WALL_START &&
9448 element <= EL_MM_ICE_WALL_END ?
9451 element >= EL_MM_AMOEBA_WALL_START &&
9452 element <= EL_MM_AMOEBA_WALL_END ?
9455 element >= EL_DF_STEEL_WALL_START &&
9456 element <= EL_DF_STEEL_WALL_END ?
9459 element >= EL_DF_WOODEN_WALL_START &&
9460 element <= EL_DF_WOODEN_WALL_END ?
9466 int map_mm_wall_element_editor(int element)
9470 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
9471 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
9472 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
9473 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
9474 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
9475 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
9477 default: return element;
9481 int get_next_element(int element)
9485 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
9486 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
9487 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
9488 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
9489 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
9490 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
9491 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
9492 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
9493 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
9494 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
9495 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
9497 default: return element;
9501 int el2img_mm(int element_mm)
9503 return el2img(map_element_MM_to_RND(element_mm));
9506 int el_act2img_mm(int element_mm, int action)
9508 return el_act2img(map_element_MM_to_RND(element_mm), action);
9511 int el_act_dir2img(int element, int action, int direction)
9513 element = GFX_ELEMENT(element);
9514 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
9516 // direction_graphic[][] == graphic[] for undefined direction graphics
9517 return element_info[element].direction_graphic[action][direction];
9520 static int el_act_dir2crm(int element, int action, int direction)
9522 element = GFX_ELEMENT(element);
9523 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
9525 // direction_graphic[][] == graphic[] for undefined direction graphics
9526 return element_info[element].direction_crumbled[action][direction];
9529 int el_act2img(int element, int action)
9531 element = GFX_ELEMENT(element);
9533 return element_info[element].graphic[action];
9536 int el_act2crm(int element, int action)
9538 element = GFX_ELEMENT(element);
9540 return element_info[element].crumbled[action];
9543 int el_dir2img(int element, int direction)
9545 element = GFX_ELEMENT(element);
9547 return el_act_dir2img(element, ACTION_DEFAULT, direction);
9550 int el2baseimg(int element)
9552 return element_info[element].graphic[ACTION_DEFAULT];
9555 int el2img(int element)
9557 element = GFX_ELEMENT(element);
9559 return element_info[element].graphic[ACTION_DEFAULT];
9562 int el2edimg(int element)
9564 element = GFX_ELEMENT(element);
9566 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
9569 int el2preimg(int element)
9571 element = GFX_ELEMENT(element);
9573 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
9576 int el2panelimg(int element)
9578 element = GFX_ELEMENT(element);
9580 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
9583 int font2baseimg(int font_nr)
9585 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
9588 int getBeltNrFromBeltElement(int element)
9590 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
9591 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
9592 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
9595 int getBeltNrFromBeltActiveElement(int element)
9597 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
9598 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
9599 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
9602 int getBeltNrFromBeltSwitchElement(int element)
9604 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
9605 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
9606 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
9609 int getBeltDirNrFromBeltElement(int element)
9611 static int belt_base_element[4] =
9613 EL_CONVEYOR_BELT_1_LEFT,
9614 EL_CONVEYOR_BELT_2_LEFT,
9615 EL_CONVEYOR_BELT_3_LEFT,
9616 EL_CONVEYOR_BELT_4_LEFT
9619 int belt_nr = getBeltNrFromBeltElement(element);
9620 int belt_dir_nr = element - belt_base_element[belt_nr];
9622 return (belt_dir_nr % 3);
9625 int getBeltDirNrFromBeltSwitchElement(int element)
9627 static int belt_base_element[4] =
9629 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
9630 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
9631 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
9632 EL_CONVEYOR_BELT_4_SWITCH_LEFT
9635 int belt_nr = getBeltNrFromBeltSwitchElement(element);
9636 int belt_dir_nr = element - belt_base_element[belt_nr];
9638 return (belt_dir_nr % 3);
9641 int getBeltDirFromBeltElement(int element)
9643 static int belt_move_dir[3] =
9650 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
9652 return belt_move_dir[belt_dir_nr];
9655 int getBeltDirFromBeltSwitchElement(int element)
9657 static int belt_move_dir[3] =
9664 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
9666 return belt_move_dir[belt_dir_nr];
9669 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
9671 static int belt_base_element[4] =
9673 EL_CONVEYOR_BELT_1_LEFT,
9674 EL_CONVEYOR_BELT_2_LEFT,
9675 EL_CONVEYOR_BELT_3_LEFT,
9676 EL_CONVEYOR_BELT_4_LEFT
9679 return belt_base_element[belt_nr] + belt_dir_nr;
9682 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
9684 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
9686 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
9689 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
9691 static int belt_base_element[4] =
9693 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
9694 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
9695 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
9696 EL_CONVEYOR_BELT_4_SWITCH_LEFT
9699 return belt_base_element[belt_nr] + belt_dir_nr;
9702 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
9704 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
9706 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
9709 boolean swapTiles_EM(boolean is_pre_emc_cave)
9711 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
9714 boolean getTeamMode_EM(void)
9716 return game.team_mode || network_playing;
9719 boolean isActivePlayer_EM(int player_nr)
9721 return stored_player[player_nr].active;
9724 unsigned int InitRND(int seed)
9726 if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
9727 return InitEngineRandom_BD(seed);
9728 else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9729 return InitEngineRandom_EM(seed);
9730 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
9731 return InitEngineRandom_SP(seed);
9732 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
9733 return InitEngineRandom_MM(seed);
9735 return InitEngineRandom_RND(seed);
9738 static struct Mapping_BD_to_RND_object bd_object_mapping[O_MAX_ALL];
9739 static struct Mapping_EM_to_RND_object em_object_mapping[GAME_TILE_MAX];
9740 static struct Mapping_EM_to_RND_player em_player_mapping[MAX_PLAYERS][PLY_MAX];
9742 static int get_effective_element_EM(int tile, int frame_em)
9744 int element = em_object_mapping[tile].element_rnd;
9745 int action = em_object_mapping[tile].action;
9746 boolean is_backside = em_object_mapping[tile].is_backside;
9747 boolean action_removing = (action == ACTION_DIGGING ||
9748 action == ACTION_SNAPPING ||
9749 action == ACTION_COLLECTING);
9757 return (frame_em > 5 ? EL_EMPTY : element);
9763 else // frame_em == 7
9774 case Ydiamond_stone:
9778 case Xdrip_stretchB:
9794 case Ymagnify_blank:
9797 case Xsand_stonein_1:
9798 case Xsand_stonein_2:
9799 case Xsand_stonein_3:
9800 case Xsand_stonein_4:
9804 return (is_backside || action_removing ? EL_EMPTY : element);
9809 static boolean check_linear_animation_EM(int tile)
9813 case Xsand_stonesand_1:
9814 case Xsand_stonesand_quickout_1:
9815 case Xsand_sandstone_1:
9816 case Xsand_stonein_1:
9817 case Xsand_stoneout_1:
9845 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
9846 boolean has_crumbled_graphics,
9847 int crumbled, int sync_frame)
9849 // if element can be crumbled, but certain action graphics are just empty
9850 // space (like instantly snapping sand to empty space in 1 frame), do not
9851 // treat these empty space graphics as crumbled graphics in EMC engine
9852 if (crumbled == IMG_EMPTY_SPACE)
9853 has_crumbled_graphics = FALSE;
9855 if (has_crumbled_graphics)
9857 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
9858 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
9859 g_crumbled->anim_delay,
9860 g_crumbled->anim_mode,
9861 g_crumbled->anim_start_frame,
9864 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
9865 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
9867 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
9868 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
9870 g_em->has_crumbled_graphics = TRUE;
9874 g_em->crumbled_bitmap = NULL;
9875 g_em->crumbled_src_x = 0;
9876 g_em->crumbled_src_y = 0;
9877 g_em->crumbled_border_size = 0;
9878 g_em->crumbled_tile_size = 0;
9880 g_em->has_crumbled_graphics = FALSE;
9885 void ResetGfxAnimation_EM(int x, int y, int tile)
9891 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
9892 int tile, int frame_em, int x, int y)
9894 int action = em_object_mapping[tile].action;
9895 int direction = em_object_mapping[tile].direction;
9896 int effective_element = get_effective_element_EM(tile, frame_em);
9897 int graphic = (direction == MV_NONE ?
9898 el_act2img(effective_element, action) :
9899 el_act_dir2img(effective_element, action, direction));
9900 struct GraphicInfo *g = &graphic_info[graphic];
9902 boolean action_removing = (action == ACTION_DIGGING ||
9903 action == ACTION_SNAPPING ||
9904 action == ACTION_COLLECTING);
9905 boolean action_moving = (action == ACTION_FALLING ||
9906 action == ACTION_MOVING ||
9907 action == ACTION_PUSHING ||
9908 action == ACTION_EATING ||
9909 action == ACTION_FILLING ||
9910 action == ACTION_EMPTYING);
9911 boolean action_falling = (action == ACTION_FALLING ||
9912 action == ACTION_FILLING ||
9913 action == ACTION_EMPTYING);
9915 // special case: graphic uses "2nd movement tile" and has defined
9916 // 7 frames for movement animation (or less) => use default graphic
9917 // for last (8th) frame which ends the movement animation
9918 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
9920 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
9921 graphic = (direction == MV_NONE ?
9922 el_act2img(effective_element, action) :
9923 el_act_dir2img(effective_element, action, direction));
9925 g = &graphic_info[graphic];
9928 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
9932 else if (action_moving)
9934 boolean is_backside = em_object_mapping[tile].is_backside;
9938 int direction = em_object_mapping[tile].direction;
9939 int move_dir = (action_falling ? MV_DOWN : direction);
9944 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
9945 if (g->double_movement && frame_em == 0)
9949 if (move_dir == MV_LEFT)
9950 GfxFrame[x - 1][y] = GfxFrame[x][y];
9951 else if (move_dir == MV_RIGHT)
9952 GfxFrame[x + 1][y] = GfxFrame[x][y];
9953 else if (move_dir == MV_UP)
9954 GfxFrame[x][y - 1] = GfxFrame[x][y];
9955 else if (move_dir == MV_DOWN)
9956 GfxFrame[x][y + 1] = GfxFrame[x][y];
9963 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
9964 if (tile == Xsand_stonesand_quickout_1 ||
9965 tile == Xsand_stonesand_quickout_2)
9969 if (graphic_info[graphic].anim_global_sync)
9970 sync_frame = FrameCounter;
9971 else if (graphic_info[graphic].anim_global_anim_sync)
9972 sync_frame = getGlobalAnimSyncFrame();
9973 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
9974 sync_frame = GfxFrame[x][y];
9976 sync_frame = 0; // playfield border (pseudo steel)
9978 SetRandomAnimationValue(x, y);
9980 int frame = getAnimationFrame(g->anim_frames,
9983 g->anim_start_frame,
9986 g_em->unique_identifier =
9987 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
9990 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
9991 int tile, int frame_em, int x, int y)
9993 int action = em_object_mapping[tile].action;
9994 int direction = em_object_mapping[tile].direction;
9995 boolean is_backside = em_object_mapping[tile].is_backside;
9996 int effective_element = get_effective_element_EM(tile, frame_em);
9997 int effective_action = action;
9998 int graphic = (direction == MV_NONE ?
9999 el_act2img(effective_element, effective_action) :
10000 el_act_dir2img(effective_element, effective_action,
10002 int crumbled = (direction == MV_NONE ?
10003 el_act2crm(effective_element, effective_action) :
10004 el_act_dir2crm(effective_element, effective_action,
10006 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
10007 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
10008 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
10009 struct GraphicInfo *g = &graphic_info[graphic];
10012 // special case: graphic uses "2nd movement tile" and has defined
10013 // 7 frames for movement animation (or less) => use default graphic
10014 // for last (8th) frame which ends the movement animation
10015 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
10017 effective_action = ACTION_DEFAULT;
10018 graphic = (direction == MV_NONE ?
10019 el_act2img(effective_element, effective_action) :
10020 el_act_dir2img(effective_element, effective_action,
10022 crumbled = (direction == MV_NONE ?
10023 el_act2crm(effective_element, effective_action) :
10024 el_act_dir2crm(effective_element, effective_action,
10027 g = &graphic_info[graphic];
10030 if (graphic_info[graphic].anim_global_sync)
10031 sync_frame = FrameCounter;
10032 else if (graphic_info[graphic].anim_global_anim_sync)
10033 sync_frame = getGlobalAnimSyncFrame();
10034 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
10035 sync_frame = GfxFrame[x][y];
10037 sync_frame = 0; // playfield border (pseudo steel)
10039 SetRandomAnimationValue(x, y);
10041 int frame = getAnimationFrame(g->anim_frames,
10044 g->anim_start_frame,
10047 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
10048 g->double_movement && is_backside);
10050 // (updating the "crumbled" graphic definitions is probably not really needed,
10051 // as animations for crumbled graphics can't be longer than one EMC cycle)
10052 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
10056 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
10057 int player_nr, int anim, int frame_em)
10059 int element = em_player_mapping[player_nr][anim].element_rnd;
10060 int action = em_player_mapping[player_nr][anim].action;
10061 int direction = em_player_mapping[player_nr][anim].direction;
10062 int graphic = (direction == MV_NONE ?
10063 el_act2img(element, action) :
10064 el_act_dir2img(element, action, direction));
10065 struct GraphicInfo *g = &graphic_info[graphic];
10068 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
10070 stored_player[player_nr].StepFrame = frame_em;
10072 sync_frame = stored_player[player_nr].Frame;
10074 int frame = getAnimationFrame(g->anim_frames,
10077 g->anim_start_frame,
10080 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
10081 &g_em->src_x, &g_em->src_y, FALSE);
10084 #define BD_GFX_RANGE(a, n, i) ((i) >= (a) && (i) < (a) + (n))
10085 #define BD_GFX_FRAME(b, i) (((i) - (b)) * 8)
10087 void InitGraphicInfo_BD(void)
10091 // always start with reliable default values
10092 for (i = 0; i < O_MAX_ALL; i++)
10094 bd_object_mapping[i].element_rnd = EL_UNKNOWN;
10095 bd_object_mapping[i].action = ACTION_DEFAULT;
10096 bd_object_mapping[i].direction = MV_NONE;
10099 for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
10101 int e = bd_object_mapping_list[i].element_bd;
10103 bd_object_mapping[e].element_rnd = bd_object_mapping_list[i].element_rnd;
10105 if (bd_object_mapping_list[i].action != -1)
10106 bd_object_mapping[e].action = bd_object_mapping_list[i].action;
10108 if (bd_object_mapping_list[i].direction != -1)
10109 bd_object_mapping[e].direction =
10110 MV_DIR_FROM_BIT(bd_object_mapping_list[i].direction);
10113 for (i = 0; i < O_MAX_ALL; i++)
10115 int element = bd_object_mapping[i].element_rnd;
10116 int action = bd_object_mapping[i].action;
10117 int direction = bd_object_mapping[i].direction;
10119 for (j = 0; j < 8; j++)
10121 int effective_element = element;
10122 int effective_action = action;
10123 int graphic = (el_act_dir2img(effective_element, effective_action,
10125 struct GraphicInfo *g = &graphic_info[graphic];
10126 struct GraphicInfo_BD *g_bd = &graphic_info_bd_object[i][j];
10127 Bitmap *src_bitmap;
10129 int sync_frame = (BD_GFX_RANGE(O_PRE_PL_1, 3, i) ? BD_GFX_FRAME(O_PRE_PL_1, i) :
10130 BD_GFX_RANGE(O_PRE_DIA_1, 5, i) ? BD_GFX_FRAME(O_PRE_DIA_1, i) :
10131 BD_GFX_RANGE(O_PRE_STONE_1, 4, i) ? BD_GFX_FRAME(O_PRE_STONE_1, i) :
10132 BD_GFX_RANGE(O_PRE_STEEL_1, 4, i) ? BD_GFX_FRAME(O_PRE_STEEL_1, i) :
10133 BD_GFX_RANGE(O_BOMB_TICK_1, 7, i) ? BD_GFX_FRAME(O_BOMB_TICK_1, i) :
10134 BD_GFX_RANGE(O_BOMB_EXPL_1, 4, i) ? BD_GFX_FRAME(O_BOMB_EXPL_1, i) :
10135 BD_GFX_RANGE(O_NUT_EXPL_1, 4, i) ? BD_GFX_FRAME(O_NUT_EXPL_1, i) :
10136 BD_GFX_RANGE(O_GHOST_EXPL_1, 4, i) ? BD_GFX_FRAME(O_GHOST_EXPL_1, i) :
10137 BD_GFX_RANGE(O_EXPLODE_1, 5, i) ? BD_GFX_FRAME(O_EXPLODE_1, i) :
10138 BD_GFX_RANGE(O_PRE_CLOCK_1, 4, i) ? BD_GFX_FRAME(O_PRE_CLOCK_1, i) :
10139 BD_GFX_RANGE(O_NITRO_EXPL_1, 4, i) ? BD_GFX_FRAME(O_NITRO_EXPL_1, i) :
10140 BD_GFX_RANGE(O_AMOEBA_2_EXPL_1, 4, i) ? BD_GFX_FRAME(O_AMOEBA_2_EXPL_1, i):
10141 i == O_INBOX_OPEN || i == O_OUTBOX_OPEN ? j :
10143 int frame = getAnimationFrame(g->anim_frames,
10146 g->anim_start_frame,
10149 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
10151 g_bd->bitmap = src_bitmap;
10152 g_bd->src_x = src_x;
10153 g_bd->src_y = src_y;
10154 g_bd->width = TILEX;
10155 g_bd->height = TILEY;
10160 void InitGraphicInfo_EM(void)
10164 // always start with reliable default values
10165 for (i = 0; i < GAME_TILE_MAX; i++)
10167 em_object_mapping[i].element_rnd = EL_UNKNOWN;
10168 em_object_mapping[i].is_backside = FALSE;
10169 em_object_mapping[i].action = ACTION_DEFAULT;
10170 em_object_mapping[i].direction = MV_NONE;
10173 // always start with reliable default values
10174 for (p = 0; p < MAX_PLAYERS; p++)
10176 for (i = 0; i < PLY_MAX; i++)
10178 em_player_mapping[p][i].element_rnd = EL_UNKNOWN;
10179 em_player_mapping[p][i].action = ACTION_DEFAULT;
10180 em_player_mapping[p][i].direction = MV_NONE;
10184 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
10186 int e = em_object_mapping_list[i].element_em;
10188 em_object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
10189 em_object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
10191 if (em_object_mapping_list[i].action != -1)
10192 em_object_mapping[e].action = em_object_mapping_list[i].action;
10194 if (em_object_mapping_list[i].direction != -1)
10195 em_object_mapping[e].direction =
10196 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
10199 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
10201 int a = em_player_mapping_list[i].action_em;
10202 int p = em_player_mapping_list[i].player_nr;
10204 em_player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
10206 if (em_player_mapping_list[i].action != -1)
10207 em_player_mapping[p][a].action = em_player_mapping_list[i].action;
10209 if (em_player_mapping_list[i].direction != -1)
10210 em_player_mapping[p][a].direction =
10211 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
10214 for (i = 0; i < GAME_TILE_MAX; i++)
10216 int element = em_object_mapping[i].element_rnd;
10217 int action = em_object_mapping[i].action;
10218 int direction = em_object_mapping[i].direction;
10219 boolean is_backside = em_object_mapping[i].is_backside;
10220 boolean action_exploding = ((action == ACTION_EXPLODING ||
10221 action == ACTION_SMASHED_BY_ROCK ||
10222 action == ACTION_SMASHED_BY_SPRING) &&
10223 element != EL_DIAMOND);
10224 boolean action_active = (action == ACTION_ACTIVE);
10225 boolean action_other = (action == ACTION_OTHER);
10227 for (j = 0; j < 8; j++)
10229 int effective_element = get_effective_element_EM(i, j);
10230 int effective_action = (j < 7 ? action :
10231 i == Xdrip_stretch ? action :
10232 i == Xdrip_stretchB ? action :
10233 i == Ydrip_1_s ? action :
10234 i == Ydrip_1_sB ? action :
10235 i == Yball_1 ? action :
10236 i == Xball_2 ? action :
10237 i == Yball_2 ? action :
10238 i == Yball_blank ? action :
10239 i == Ykey_1_blank ? action :
10240 i == Ykey_2_blank ? action :
10241 i == Ykey_3_blank ? action :
10242 i == Ykey_4_blank ? action :
10243 i == Ykey_5_blank ? action :
10244 i == Ykey_6_blank ? action :
10245 i == Ykey_7_blank ? action :
10246 i == Ykey_8_blank ? action :
10247 i == Ylenses_blank ? action :
10248 i == Ymagnify_blank ? action :
10249 i == Ygrass_blank ? action :
10250 i == Ydirt_blank ? action :
10251 i == Xsand_stonein_1 ? action :
10252 i == Xsand_stonein_2 ? action :
10253 i == Xsand_stonein_3 ? action :
10254 i == Xsand_stonein_4 ? action :
10255 i == Xsand_stoneout_1 ? action :
10256 i == Xsand_stoneout_2 ? action :
10257 i == Xboom_android ? ACTION_EXPLODING :
10258 action_exploding ? ACTION_EXPLODING :
10259 action_active ? action :
10260 action_other ? action :
10262 int graphic = (el_act_dir2img(effective_element, effective_action,
10264 int crumbled = (el_act_dir2crm(effective_element, effective_action,
10266 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
10267 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
10268 boolean has_action_graphics = (graphic != base_graphic);
10269 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
10270 struct GraphicInfo *g = &graphic_info[graphic];
10271 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
10272 Bitmap *src_bitmap;
10274 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
10275 boolean special_animation = (action != ACTION_DEFAULT &&
10276 g->anim_frames == 3 &&
10277 g->anim_delay == 2 &&
10278 g->anim_mode & ANIM_LINEAR);
10279 int sync_frame = (i == Xdrip_stretch ? 7 :
10280 i == Xdrip_stretchB ? 7 :
10281 i == Ydrip_2_s ? j + 8 :
10282 i == Ydrip_2_sB ? j + 8 :
10284 i == Xacid_2 ? 10 :
10285 i == Xacid_3 ? 20 :
10286 i == Xacid_4 ? 30 :
10287 i == Xacid_5 ? 40 :
10288 i == Xacid_6 ? 50 :
10289 i == Xacid_7 ? 60 :
10290 i == Xacid_8 ? 70 :
10291 i == Xfake_acid_1 ? 0 :
10292 i == Xfake_acid_2 ? 10 :
10293 i == Xfake_acid_3 ? 20 :
10294 i == Xfake_acid_4 ? 30 :
10295 i == Xfake_acid_5 ? 40 :
10296 i == Xfake_acid_6 ? 50 :
10297 i == Xfake_acid_7 ? 60 :
10298 i == Xfake_acid_8 ? 70 :
10299 i == Xfake_acid_1_player ? 0 :
10300 i == Xfake_acid_2_player ? 10 :
10301 i == Xfake_acid_3_player ? 20 :
10302 i == Xfake_acid_4_player ? 30 :
10303 i == Xfake_acid_5_player ? 40 :
10304 i == Xfake_acid_6_player ? 50 :
10305 i == Xfake_acid_7_player ? 60 :
10306 i == Xfake_acid_8_player ? 70 :
10308 i == Yball_2 ? j + 8 :
10309 i == Yball_blank ? j + 1 :
10310 i == Ykey_1_blank ? j + 1 :
10311 i == Ykey_2_blank ? j + 1 :
10312 i == Ykey_3_blank ? j + 1 :
10313 i == Ykey_4_blank ? j + 1 :
10314 i == Ykey_5_blank ? j + 1 :
10315 i == Ykey_6_blank ? j + 1 :
10316 i == Ykey_7_blank ? j + 1 :
10317 i == Ykey_8_blank ? j + 1 :
10318 i == Ylenses_blank ? j + 1 :
10319 i == Ymagnify_blank ? j + 1 :
10320 i == Ygrass_blank ? j + 1 :
10321 i == Ydirt_blank ? j + 1 :
10322 i == Xamoeba_1 ? 0 :
10323 i == Xamoeba_2 ? 1 :
10324 i == Xamoeba_3 ? 2 :
10325 i == Xamoeba_4 ? 3 :
10326 i == Xamoeba_5 ? 0 :
10327 i == Xamoeba_6 ? 1 :
10328 i == Xamoeba_7 ? 2 :
10329 i == Xamoeba_8 ? 3 :
10330 i == Xexit_2 ? j + 8 :
10331 i == Xexit_3 ? j + 16 :
10332 i == Xdynamite_1 ? 0 :
10333 i == Xdynamite_2 ? 8 :
10334 i == Xdynamite_3 ? 16 :
10335 i == Xdynamite_4 ? 24 :
10336 i == Xsand_stonein_1 ? j + 1 :
10337 i == Xsand_stonein_2 ? j + 9 :
10338 i == Xsand_stonein_3 ? j + 17 :
10339 i == Xsand_stonein_4 ? j + 25 :
10340 i == Xsand_stoneout_1 && j == 0 ? 0 :
10341 i == Xsand_stoneout_1 && j == 1 ? 0 :
10342 i == Xsand_stoneout_1 && j == 2 ? 1 :
10343 i == Xsand_stoneout_1 && j == 3 ? 2 :
10344 i == Xsand_stoneout_1 && j == 4 ? 2 :
10345 i == Xsand_stoneout_1 && j == 5 ? 3 :
10346 i == Xsand_stoneout_1 && j == 6 ? 4 :
10347 i == Xsand_stoneout_1 && j == 7 ? 4 :
10348 i == Xsand_stoneout_2 && j == 0 ? 5 :
10349 i == Xsand_stoneout_2 && j == 1 ? 6 :
10350 i == Xsand_stoneout_2 && j == 2 ? 7 :
10351 i == Xsand_stoneout_2 && j == 3 ? 8 :
10352 i == Xsand_stoneout_2 && j == 4 ? 9 :
10353 i == Xsand_stoneout_2 && j == 5 ? 11 :
10354 i == Xsand_stoneout_2 && j == 6 ? 13 :
10355 i == Xsand_stoneout_2 && j == 7 ? 15 :
10356 i == Xboom_bug && j == 1 ? 2 :
10357 i == Xboom_bug && j == 2 ? 2 :
10358 i == Xboom_bug && j == 3 ? 4 :
10359 i == Xboom_bug && j == 4 ? 4 :
10360 i == Xboom_bug && j == 5 ? 2 :
10361 i == Xboom_bug && j == 6 ? 2 :
10362 i == Xboom_bug && j == 7 ? 0 :
10363 i == Xboom_tank && j == 1 ? 2 :
10364 i == Xboom_tank && j == 2 ? 2 :
10365 i == Xboom_tank && j == 3 ? 4 :
10366 i == Xboom_tank && j == 4 ? 4 :
10367 i == Xboom_tank && j == 5 ? 2 :
10368 i == Xboom_tank && j == 6 ? 2 :
10369 i == Xboom_tank && j == 7 ? 0 :
10370 i == Xboom_android && j == 7 ? 6 :
10371 i == Xboom_1 && j == 1 ? 2 :
10372 i == Xboom_1 && j == 2 ? 2 :
10373 i == Xboom_1 && j == 3 ? 4 :
10374 i == Xboom_1 && j == 4 ? 4 :
10375 i == Xboom_1 && j == 5 ? 6 :
10376 i == Xboom_1 && j == 6 ? 6 :
10377 i == Xboom_1 && j == 7 ? 8 :
10378 i == Xboom_2 && j == 0 ? 8 :
10379 i == Xboom_2 && j == 1 ? 8 :
10380 i == Xboom_2 && j == 2 ? 10 :
10381 i == Xboom_2 && j == 3 ? 10 :
10382 i == Xboom_2 && j == 4 ? 10 :
10383 i == Xboom_2 && j == 5 ? 12 :
10384 i == Xboom_2 && j == 6 ? 12 :
10385 i == Xboom_2 && j == 7 ? 12 :
10386 special_animation && j == 4 ? 3 :
10387 effective_action != action ? 0 :
10389 int frame = getAnimationFrame(g->anim_frames,
10392 g->anim_start_frame,
10395 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
10396 g->double_movement && is_backside);
10398 g_em->bitmap = src_bitmap;
10399 g_em->src_x = src_x;
10400 g_em->src_y = src_y;
10401 g_em->src_offset_x = 0;
10402 g_em->src_offset_y = 0;
10403 g_em->dst_offset_x = 0;
10404 g_em->dst_offset_y = 0;
10405 g_em->width = TILEX;
10406 g_em->height = TILEY;
10408 g_em->preserve_background = FALSE;
10410 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
10413 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
10414 effective_action == ACTION_MOVING ||
10415 effective_action == ACTION_PUSHING ||
10416 effective_action == ACTION_EATING)) ||
10417 (!has_action_graphics && (effective_action == ACTION_FILLING ||
10418 effective_action == ACTION_EMPTYING)))
10421 (effective_action == ACTION_FALLING ||
10422 effective_action == ACTION_FILLING ||
10423 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
10424 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
10425 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
10426 int num_steps = (i == Ydrip_1_s ? 16 :
10427 i == Ydrip_1_sB ? 16 :
10428 i == Ydrip_2_s ? 16 :
10429 i == Ydrip_2_sB ? 16 :
10430 i == Xsand_stonein_1 ? 32 :
10431 i == Xsand_stonein_2 ? 32 :
10432 i == Xsand_stonein_3 ? 32 :
10433 i == Xsand_stonein_4 ? 32 :
10434 i == Xsand_stoneout_1 ? 16 :
10435 i == Xsand_stoneout_2 ? 16 : 8);
10436 int cx = ABS(dx) * (TILEX / num_steps);
10437 int cy = ABS(dy) * (TILEY / num_steps);
10438 int step_frame = (i == Ydrip_2_s ? j + 8 :
10439 i == Ydrip_2_sB ? j + 8 :
10440 i == Xsand_stonein_2 ? j + 8 :
10441 i == Xsand_stonein_3 ? j + 16 :
10442 i == Xsand_stonein_4 ? j + 24 :
10443 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
10444 int step = (is_backside ? step_frame : num_steps - step_frame);
10446 if (is_backside) // tile where movement starts
10448 if (dx < 0 || dy < 0)
10450 g_em->src_offset_x = cx * step;
10451 g_em->src_offset_y = cy * step;
10455 g_em->dst_offset_x = cx * step;
10456 g_em->dst_offset_y = cy * step;
10459 else // tile where movement ends
10461 if (dx < 0 || dy < 0)
10463 g_em->dst_offset_x = cx * step;
10464 g_em->dst_offset_y = cy * step;
10468 g_em->src_offset_x = cx * step;
10469 g_em->src_offset_y = cy * step;
10473 g_em->width = TILEX - cx * step;
10474 g_em->height = TILEY - cy * step;
10477 // create unique graphic identifier to decide if tile must be redrawn
10478 /* bit 31 - 16 (16 bit): EM style graphic
10479 bit 15 - 12 ( 4 bit): EM style frame
10480 bit 11 - 6 ( 6 bit): graphic width
10481 bit 5 - 0 ( 6 bit): graphic height */
10482 g_em->unique_identifier =
10483 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
10487 for (i = 0; i < GAME_TILE_MAX; i++)
10489 for (j = 0; j < 8; j++)
10491 int element = em_object_mapping[i].element_rnd;
10492 int action = em_object_mapping[i].action;
10493 int direction = em_object_mapping[i].direction;
10494 boolean is_backside = em_object_mapping[i].is_backside;
10495 int graphic_action = el_act_dir2img(element, action, direction);
10496 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
10498 if ((action == ACTION_SMASHED_BY_ROCK ||
10499 action == ACTION_SMASHED_BY_SPRING ||
10500 action == ACTION_EATING) &&
10501 graphic_action == graphic_default)
10503 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
10504 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
10505 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
10506 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
10509 // no separate animation for "smashed by rock" -- use rock instead
10510 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
10511 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
10513 g_em->bitmap = g_xx->bitmap;
10514 g_em->src_x = g_xx->src_x;
10515 g_em->src_y = g_xx->src_y;
10516 g_em->src_offset_x = g_xx->src_offset_x;
10517 g_em->src_offset_y = g_xx->src_offset_y;
10518 g_em->dst_offset_x = g_xx->dst_offset_x;
10519 g_em->dst_offset_y = g_xx->dst_offset_y;
10520 g_em->width = g_xx->width;
10521 g_em->height = g_xx->height;
10522 g_em->unique_identifier = g_xx->unique_identifier;
10525 g_em->preserve_background = TRUE;
10530 for (p = 0; p < MAX_PLAYERS; p++)
10532 for (i = 0; i < PLY_MAX; i++)
10534 int element = em_player_mapping[p][i].element_rnd;
10535 int action = em_player_mapping[p][i].action;
10536 int direction = em_player_mapping[p][i].direction;
10538 for (j = 0; j < 8; j++)
10540 int effective_element = element;
10541 int effective_action = action;
10542 int graphic = (direction == MV_NONE ?
10543 el_act2img(effective_element, effective_action) :
10544 el_act_dir2img(effective_element, effective_action,
10546 struct GraphicInfo *g = &graphic_info[graphic];
10547 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
10548 Bitmap *src_bitmap;
10550 int sync_frame = j;
10551 int frame = getAnimationFrame(g->anim_frames,
10554 g->anim_start_frame,
10557 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
10559 g_em->bitmap = src_bitmap;
10560 g_em->src_x = src_x;
10561 g_em->src_y = src_y;
10562 g_em->src_offset_x = 0;
10563 g_em->src_offset_y = 0;
10564 g_em->dst_offset_x = 0;
10565 g_em->dst_offset_y = 0;
10566 g_em->width = TILEX;
10567 g_em->height = TILEY;
10573 static void CheckSaveEngineSnapshot_EM(int frame,
10574 boolean any_player_moving,
10575 boolean any_player_snapping,
10576 boolean any_player_dropping)
10578 if (frame == 7 && !any_player_dropping)
10580 if (!local_player->was_waiting)
10582 if (!CheckSaveEngineSnapshotToList())
10585 local_player->was_waiting = TRUE;
10588 else if (any_player_moving || any_player_snapping || any_player_dropping)
10590 local_player->was_waiting = FALSE;
10594 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
10595 boolean murphy_is_dropping)
10597 if (murphy_is_waiting)
10599 if (!local_player->was_waiting)
10601 if (!CheckSaveEngineSnapshotToList())
10604 local_player->was_waiting = TRUE;
10609 local_player->was_waiting = FALSE;
10613 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
10614 boolean button_released)
10616 if (button_released)
10618 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
10619 CheckSaveEngineSnapshotToList();
10621 else if (element_clicked)
10623 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
10624 CheckSaveEngineSnapshotToList();
10626 game.snapshot.changed_action = TRUE;
10630 boolean CheckSingleStepMode_EM(int frame,
10631 boolean any_player_moving,
10632 boolean any_player_snapping,
10633 boolean any_player_dropping)
10635 if (tape.single_step && tape.recording && !tape.pausing)
10636 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
10637 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10639 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
10640 any_player_snapping, any_player_dropping);
10642 return tape.pausing;
10645 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
10646 boolean murphy_is_dropping)
10648 boolean murphy_starts_dropping = FALSE;
10651 for (i = 0; i < MAX_PLAYERS; i++)
10652 if (stored_player[i].force_dropping)
10653 murphy_starts_dropping = TRUE;
10655 if (tape.single_step && tape.recording && !tape.pausing)
10656 if (murphy_is_waiting && !murphy_starts_dropping)
10657 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10659 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
10662 void CheckSingleStepMode_MM(boolean element_clicked,
10663 boolean button_released)
10665 if (tape.single_step && tape.recording && !tape.pausing)
10666 if (button_released)
10667 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10669 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
10672 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
10673 int graphic, int sync_frame)
10675 int frame = getGraphicAnimationFrame(graphic, sync_frame);
10677 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
10680 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
10682 return (IS_NEXT_FRAME(sync_frame, graphic));
10685 int getGraphicInfo_Delay(int graphic)
10687 return graphic_info[graphic].anim_delay;
10690 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
10692 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
10695 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
10701 void PlayMenuSoundExt(int sound)
10703 if (sound == SND_UNDEFINED)
10706 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
10707 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
10710 if (IS_LOOP_SOUND(sound))
10711 PlaySoundLoop(sound);
10716 void PlayMenuSound(void)
10718 PlayMenuSoundExt(menu.sound[game_status]);
10721 void PlayMenuSoundStereo(int sound, int stereo_position)
10723 if (sound == SND_UNDEFINED)
10726 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
10727 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
10730 if (IS_LOOP_SOUND(sound))
10731 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
10733 PlaySoundStereo(sound, stereo_position);
10736 void PlayMenuSoundIfLoopExt(int sound)
10738 if (sound == SND_UNDEFINED)
10741 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
10742 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
10745 if (IS_LOOP_SOUND(sound))
10746 PlaySoundLoop(sound);
10749 void PlayMenuSoundIfLoop(void)
10751 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
10754 void PlayMenuMusicExt(int music)
10756 if (music == MUS_UNDEFINED)
10759 if (!setup.sound_music)
10762 if (IS_LOOP_MUSIC(music))
10763 PlayMusicLoop(music);
10768 void PlayMenuMusic(void)
10770 char *curr_music = getCurrentlyPlayingMusicFilename();
10771 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
10773 if (!strEqual(curr_music, next_music))
10774 PlayMenuMusicExt(menu.music[game_status]);
10777 void PlayMenuSoundsAndMusic(void)
10783 static void FadeMenuSounds(void)
10788 static void FadeMenuMusic(void)
10790 char *curr_music = getCurrentlyPlayingMusicFilename();
10791 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
10793 if (!strEqual(curr_music, next_music))
10797 void FadeMenuSoundsAndMusic(void)
10803 void PlaySoundActivating(void)
10806 PlaySound(SND_MENU_ITEM_ACTIVATING);
10810 void PlaySoundSelecting(void)
10813 PlaySound(SND_MENU_ITEM_SELECTING);
10817 void ToggleFullscreenIfNeeded(void)
10819 // if setup and video fullscreen state are already matching, nothing do do
10820 if (setup.fullscreen == video.fullscreen_enabled ||
10821 !video.fullscreen_available)
10824 SDLSetWindowFullscreen(setup.fullscreen);
10826 // set setup value according to successfully changed fullscreen mode
10827 setup.fullscreen = video.fullscreen_enabled;
10830 void ChangeWindowScalingIfNeeded(void)
10832 // if setup and video window scaling are already matching, nothing do do
10833 if (setup.window_scaling_percent == video.window_scaling_percent ||
10834 video.fullscreen_enabled)
10837 SDLSetWindowScaling(setup.window_scaling_percent);
10839 // set setup value according to successfully changed window scaling
10840 setup.window_scaling_percent = video.window_scaling_percent;
10843 void ChangeVsyncModeIfNeeded(void)
10845 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
10846 int video_vsync_mode = video.vsync_mode;
10848 // if setup and video vsync mode are already matching, nothing do do
10849 if (setup_vsync_mode == video_vsync_mode)
10852 // if renderer is using OpenGL, vsync mode can directly be changed
10853 SDLSetScreenVsyncMode(setup.vsync_mode);
10855 // if vsync mode unchanged, try re-creating renderer to set vsync mode
10856 if (video.vsync_mode == video_vsync_mode)
10858 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
10860 // save backbuffer content which gets lost when re-creating screen
10861 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
10863 // force re-creating screen and renderer to set new vsync mode
10864 video.fullscreen_enabled = !setup.fullscreen;
10866 // when creating new renderer, destroy textures linked to old renderer
10867 FreeAllImageTextures(); // needs old renderer to free the textures
10869 // re-create screen and renderer (including change of vsync mode)
10870 ChangeVideoModeIfNeeded(setup.fullscreen);
10872 // set setup value according to successfully changed fullscreen mode
10873 setup.fullscreen = video.fullscreen_enabled;
10875 // restore backbuffer content from temporary backbuffer backup bitmap
10876 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
10877 FreeBitmap(tmp_backbuffer);
10879 // update visible window/screen
10880 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
10882 // when changing vsync mode, re-create textures for new renderer
10883 InitImageTextures();
10886 // set setup value according to successfully changed vsync mode
10887 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
10890 static void JoinRectangles(int *x, int *y, int *width, int *height,
10891 int x2, int y2, int width2, int height2)
10893 // do not join with "off-screen" rectangle
10894 if (x2 == -1 || y2 == -1)
10899 *width = MAX(*width, width2);
10900 *height = MAX(*height, height2);
10903 void SetAnimStatus(int anim_status_new)
10905 if (anim_status_new == GAME_MODE_MAIN)
10906 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
10907 else if (anim_status_new == GAME_MODE_NAMES)
10908 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
10909 else if (anim_status_new == GAME_MODE_SCORES)
10910 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
10912 global.anim_status_next = anim_status_new;
10914 // directly set screen modes that are entered without fading
10915 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
10916 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
10917 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
10918 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
10919 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
10920 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
10921 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
10922 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
10923 global.anim_status = global.anim_status_next;
10926 void SetGameStatus(int game_status_new)
10928 if (game_status_new != game_status)
10929 game_status_last_screen = game_status;
10931 game_status = game_status_new;
10933 SetAnimStatus(game_status_new);
10936 void SetFontStatus(int game_status_new)
10938 static int last_game_status = -1;
10940 if (game_status_new != -1)
10942 // set game status for font use after storing last game status
10943 last_game_status = game_status;
10944 game_status = game_status_new;
10948 // reset game status after font use from last stored game status
10949 game_status = last_game_status;
10953 void ResetFontStatus(void)
10958 void SetLevelSetInfo(char *identifier, int level_nr)
10960 setString(&levelset.identifier, identifier);
10962 levelset.level_nr = level_nr;
10965 boolean CheckIfAllViewportsHaveChanged(void)
10967 // if game status has not changed, viewports have not changed either
10968 if (game_status == game_status_last)
10971 // check if all viewports have changed with current game status
10973 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
10974 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
10975 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
10976 int new_real_sx = vp_playfield->x;
10977 int new_real_sy = vp_playfield->y;
10978 int new_full_sxsize = vp_playfield->width;
10979 int new_full_sysize = vp_playfield->height;
10980 int new_dx = vp_door_1->x;
10981 int new_dy = vp_door_1->y;
10982 int new_dxsize = vp_door_1->width;
10983 int new_dysize = vp_door_1->height;
10984 int new_vx = vp_door_2->x;
10985 int new_vy = vp_door_2->y;
10986 int new_vxsize = vp_door_2->width;
10987 int new_vysize = vp_door_2->height;
10989 boolean playfield_viewport_has_changed =
10990 (new_real_sx != REAL_SX ||
10991 new_real_sy != REAL_SY ||
10992 new_full_sxsize != FULL_SXSIZE ||
10993 new_full_sysize != FULL_SYSIZE);
10995 boolean door_1_viewport_has_changed =
10998 new_dxsize != DXSIZE ||
10999 new_dysize != DYSIZE);
11001 boolean door_2_viewport_has_changed =
11004 new_vxsize != VXSIZE ||
11005 new_vysize != VYSIZE ||
11006 game_status_last == GAME_MODE_EDITOR);
11008 return (playfield_viewport_has_changed &&
11009 door_1_viewport_has_changed &&
11010 door_2_viewport_has_changed);
11013 boolean CheckFadeAll(void)
11015 return (CheckIfGlobalBorderHasChanged() ||
11016 CheckIfAllViewportsHaveChanged());
11019 void ChangeViewportPropertiesIfNeeded(void)
11021 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
11022 FALSE : setup.small_game_graphics);
11023 int gfx_game_mode = getGlobalGameStatus(game_status);
11024 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
11026 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
11027 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
11028 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
11029 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
11030 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
11031 int new_win_xsize = vp_window->width;
11032 int new_win_ysize = vp_window->height;
11033 int border_left = vp_playfield->border_left;
11034 int border_right = vp_playfield->border_right;
11035 int border_top = vp_playfield->border_top;
11036 int border_bottom = vp_playfield->border_bottom;
11037 int new_sx = vp_playfield->x + border_left;
11038 int new_sy = vp_playfield->y + border_top;
11039 int new_sxsize = vp_playfield->width - border_left - border_right;
11040 int new_sysize = vp_playfield->height - border_top - border_bottom;
11041 int new_real_sx = vp_playfield->x;
11042 int new_real_sy = vp_playfield->y;
11043 int new_full_sxsize = vp_playfield->width;
11044 int new_full_sysize = vp_playfield->height;
11045 int new_dx = vp_door_1->x;
11046 int new_dy = vp_door_1->y;
11047 int new_dxsize = vp_door_1->width;
11048 int new_dysize = vp_door_1->height;
11049 int new_vx = vp_door_2->x;
11050 int new_vy = vp_door_2->y;
11051 int new_vxsize = vp_door_2->width;
11052 int new_vysize = vp_door_2->height;
11053 int new_ex = vp_door_3->x;
11054 int new_ey = vp_door_3->y;
11055 int new_exsize = vp_door_3->width;
11056 int new_eysize = vp_door_3->height;
11057 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
11058 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
11059 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
11060 int new_scr_fieldx = new_sxsize / tilesize;
11061 int new_scr_fieldy = new_sysize / tilesize;
11062 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
11063 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
11064 boolean init_gfx_buffers = FALSE;
11065 boolean init_video_buffer = FALSE;
11066 boolean init_gadgets_and_anims = FALSE;
11067 boolean init_bd_graphics = FALSE;
11068 boolean init_em_graphics = FALSE;
11070 if (new_win_xsize != WIN_XSIZE ||
11071 new_win_ysize != WIN_YSIZE)
11073 WIN_XSIZE = new_win_xsize;
11074 WIN_YSIZE = new_win_ysize;
11076 init_video_buffer = TRUE;
11077 init_gfx_buffers = TRUE;
11078 init_gadgets_and_anims = TRUE;
11080 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
11083 if (new_scr_fieldx != SCR_FIELDX ||
11084 new_scr_fieldy != SCR_FIELDY)
11086 // this always toggles between MAIN and GAME when using small tile size
11088 SCR_FIELDX = new_scr_fieldx;
11089 SCR_FIELDY = new_scr_fieldy;
11091 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
11094 if (new_sx != SX ||
11102 new_sxsize != SXSIZE ||
11103 new_sysize != SYSIZE ||
11104 new_dxsize != DXSIZE ||
11105 new_dysize != DYSIZE ||
11106 new_vxsize != VXSIZE ||
11107 new_vysize != VYSIZE ||
11108 new_exsize != EXSIZE ||
11109 new_eysize != EYSIZE ||
11110 new_real_sx != REAL_SX ||
11111 new_real_sy != REAL_SY ||
11112 new_full_sxsize != FULL_SXSIZE ||
11113 new_full_sysize != FULL_SYSIZE ||
11114 new_tilesize_var != TILESIZE_VAR
11117 // ------------------------------------------------------------------------
11118 // determine next fading area for changed viewport definitions
11119 // ------------------------------------------------------------------------
11121 // start with current playfield area (default fading area)
11124 FADE_SXSIZE = FULL_SXSIZE;
11125 FADE_SYSIZE = FULL_SYSIZE;
11127 // add new playfield area if position or size has changed
11128 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
11129 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
11131 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11132 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
11135 // add current and new door 1 area if position or size has changed
11136 if (new_dx != DX || new_dy != DY ||
11137 new_dxsize != DXSIZE || new_dysize != DYSIZE)
11139 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11140 DX, DY, DXSIZE, DYSIZE);
11141 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11142 new_dx, new_dy, new_dxsize, new_dysize);
11145 // add current and new door 2 area if position or size has changed
11146 if (new_vx != VX || new_vy != VY ||
11147 new_vxsize != VXSIZE || new_vysize != VYSIZE)
11149 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11150 VX, VY, VXSIZE, VYSIZE);
11151 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11152 new_vx, new_vy, new_vxsize, new_vysize);
11155 // ------------------------------------------------------------------------
11156 // handle changed tile size
11157 // ------------------------------------------------------------------------
11159 if (new_tilesize_var != TILESIZE_VAR)
11161 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
11163 // changing tile size invalidates scroll values of engine snapshots
11164 FreeEngineSnapshotSingle();
11166 // changing tile size requires update of graphic mapping for BD/EM engine
11167 init_bd_graphics = TRUE;
11168 init_em_graphics = TRUE;
11179 SXSIZE = new_sxsize;
11180 SYSIZE = new_sysize;
11181 DXSIZE = new_dxsize;
11182 DYSIZE = new_dysize;
11183 VXSIZE = new_vxsize;
11184 VYSIZE = new_vysize;
11185 EXSIZE = new_exsize;
11186 EYSIZE = new_eysize;
11187 REAL_SX = new_real_sx;
11188 REAL_SY = new_real_sy;
11189 FULL_SXSIZE = new_full_sxsize;
11190 FULL_SYSIZE = new_full_sysize;
11191 TILESIZE_VAR = new_tilesize_var;
11193 init_gfx_buffers = TRUE;
11194 init_gadgets_and_anims = TRUE;
11196 // Debug("tools:viewport", "viewports: init_gfx_buffers");
11197 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
11200 if (init_gfx_buffers)
11202 // Debug("tools:viewport", "init_gfx_buffers");
11204 SCR_FIELDX = new_scr_fieldx_buffers;
11205 SCR_FIELDY = new_scr_fieldy_buffers;
11209 SCR_FIELDX = new_scr_fieldx;
11210 SCR_FIELDY = new_scr_fieldy;
11212 SetDrawDeactivationMask(REDRAW_NONE);
11213 SetDrawBackgroundMask(REDRAW_FIELD);
11216 if (init_video_buffer)
11218 // Debug("tools:viewport", "init_video_buffer");
11220 FreeAllImageTextures(); // needs old renderer to free the textures
11222 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
11223 InitImageTextures();
11226 if (init_gadgets_and_anims)
11228 // Debug("tools:viewport", "init_gadgets_and_anims");
11231 InitGlobalAnimations();
11234 if (init_bd_graphics)
11236 InitGraphicInfo_BD();
11239 if (init_em_graphics)
11241 InitGraphicInfo_EM();
11245 void OpenURL(char *url)
11247 #if SDL_VERSION_ATLEAST(2,0,14)
11250 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
11251 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
11252 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
11256 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
11258 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
11261 char *getCurrentLevelsetName(void)
11263 return leveldir_current->name;
11267 // ============================================================================
11269 // ============================================================================
11271 #if defined(PLATFORM_WINDOWS)
11272 /* FILETIME of Jan 1 1970 00:00:00. */
11273 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
11276 * timezone information is stored outside the kernel so tzp isn't used anymore.
11278 * Note: this function is not for Win32 high precision timing purpose. See
11281 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
11283 FILETIME file_time;
11284 SYSTEMTIME system_time;
11285 ULARGE_INTEGER ularge;
11287 GetSystemTime(&system_time);
11288 SystemTimeToFileTime(&system_time, &file_time);
11289 ularge.LowPart = file_time.dwLowDateTime;
11290 ularge.HighPart = file_time.dwHighDateTime;
11292 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
11293 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
11299 static char *test_init_uuid_random_function_simple(void)
11301 static char seed_text[100];
11302 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
11304 sprintf(seed_text, "%d", seed);
11309 static char *test_init_uuid_random_function_better(void)
11311 static char seed_text[100];
11312 struct timeval current_time;
11314 gettimeofday(¤t_time, NULL);
11316 prng_seed_bytes(¤t_time, sizeof(current_time));
11318 sprintf(seed_text, "%ld.%ld",
11319 (long)current_time.tv_sec,
11320 (long)current_time.tv_usec);
11325 #if defined(PLATFORM_WINDOWS)
11326 static char *test_init_uuid_random_function_better_windows(void)
11328 static char seed_text[100];
11329 struct timeval current_time;
11331 gettimeofday_windows(¤t_time, NULL);
11333 prng_seed_bytes(¤t_time, sizeof(current_time));
11335 sprintf(seed_text, "%ld.%ld",
11336 (long)current_time.tv_sec,
11337 (long)current_time.tv_usec);
11343 static unsigned int test_uuid_random_function_simple(int max)
11345 return GetSimpleRandom(max);
11348 static unsigned int test_uuid_random_function_better(int max)
11350 return (max > 0 ? prng_get_uint() % max : 0);
11353 #if defined(PLATFORM_WINDOWS)
11354 #define NUM_UUID_TESTS 3
11356 #define NUM_UUID_TESTS 2
11359 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
11361 HashTable *hash_seeds =
11362 create_hashtable(get_hash_from_string, hash_key_strings_are_equal, free, NULL);
11363 HashTable *hash_uuids =
11364 create_hashtable(get_hash_from_string, hash_key_strings_are_equal, free, NULL);
11365 static char message[100];
11368 char *random_name = (nr == 0 ? "simple" : "better");
11369 char *random_type = (always_seed ? "always" : "only once");
11370 char *(*init_random_function)(void) =
11372 test_init_uuid_random_function_simple :
11373 test_init_uuid_random_function_better);
11374 unsigned int (*random_function)(int) =
11376 test_uuid_random_function_simple :
11377 test_uuid_random_function_better);
11380 #if defined(PLATFORM_WINDOWS)
11383 random_name = "windows";
11384 init_random_function = test_init_uuid_random_function_better_windows;
11390 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
11391 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
11393 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
11394 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
11395 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
11397 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
11401 // always initialize random number generator at least once
11402 init_random_function();
11404 unsigned int time_start = SDL_GetTicks();
11406 for (i = 0; i < num_uuids; i++)
11410 char *seed = getStringCopy(init_random_function());
11412 hashtable_remove(hash_seeds, seed);
11413 hashtable_insert(hash_seeds, seed, "1");
11416 char *uuid = getStringCopy(getUUIDExt(random_function));
11418 hashtable_remove(hash_uuids, uuid);
11419 hashtable_insert(hash_uuids, uuid, "1");
11422 int num_unique_seeds = hashtable_count(hash_seeds);
11423 int num_unique_uuids = hashtable_count(hash_uuids);
11425 unsigned int time_needed = SDL_GetTicks() - time_start;
11427 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
11429 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
11432 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
11434 if (nr == NUM_UUID_TESTS - 1 && always_seed)
11435 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
11437 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
11439 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
11441 Request(message, REQ_CONFIRM);
11443 hashtable_destroy(hash_seeds);
11444 hashtable_destroy(hash_uuids);
11447 void TestGeneratingUUIDs(void)
11449 int num_uuids = 1000000;
11452 for (i = 0; i < NUM_UUID_TESTS; i++)
11453 for (j = 0; j < 2; j++)
11454 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
11456 CloseAllAndExit(0);