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_template = EL_BD_GAME_GRAPHICS_COLOR_TEMPLATE;
3842 int graphic_template = el2preimg(element_template);
3843 int element_default = EL_BD_ROCK;
3844 int graphic_default = el2preimg(element_default);
3846 // create special preview bitmap and scale it down to preview tile size
3847 getSizedGraphicSource(graphic_template, 0, original_tilesize, &src_bitmap, &src_x, &src_y);
3848 PreparePreviewTileBitmap_BD(src_bitmap, scale_down_factor);
3850 // force using special preview bitmap to replace original preview bitmap
3851 getSizedGraphicSource(graphic_default, 0, preview.tile_size, &src_bitmap, &src_x, &src_y);
3852 SetPreviewTileBitmapReference_BD(src_bitmap);
3855 void DrawPreviewLevelInitial(void)
3857 PreparePreviewTileBitmap(); // only needed for native BD style levels
3859 DrawPreviewLevelExt(TRUE);
3860 DrawPreviewPlayers();
3863 void DrawPreviewLevelAnimation(void)
3865 DrawPreviewLevelExt(FALSE);
3868 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3869 int border_size, int font_nr)
3871 int graphic = el2img(EL_PLAYER_1 + player_nr);
3872 int font_height = getFontHeight(font_nr);
3873 int player_height = MAX(tile_size, font_height);
3874 int xoffset_text = tile_size + border_size;
3875 int yoffset_text = (player_height - font_height) / 2;
3876 int yoffset_graphic = (player_height - tile_size) / 2;
3877 char *player_name = getNetworkPlayerName(player_nr + 1);
3879 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3881 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3884 static void DrawNetworkPlayersExt(boolean force)
3886 if (game_status != GAME_MODE_MAIN)
3889 if (!network.connected && !force)
3892 // do not draw network players if level preview redefined, but players aren't
3893 if (preview.redefined && !menu.main.network_players.redefined)
3896 int num_players = 0;
3899 for (i = 0; i < MAX_PLAYERS; i++)
3900 if (stored_player[i].connected_network)
3903 struct TextPosInfo *pos = &menu.main.network_players;
3904 int tile_size = pos->tile_size;
3905 int border_size = pos->border_size;
3906 int xoffset_text = tile_size + border_size;
3907 int font_nr = pos->font;
3908 int font_width = getFontWidth(font_nr);
3909 int font_height = getFontHeight(font_nr);
3910 int player_height = MAX(tile_size, font_height);
3911 int player_yoffset = player_height + border_size;
3912 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3913 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3914 int all_players_height = num_players * player_yoffset - border_size;
3915 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3916 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3917 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3919 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3920 max_players_width, max_players_height);
3922 // first draw local network player ...
3923 for (i = 0; i < MAX_PLAYERS; i++)
3925 if (stored_player[i].connected_network &&
3926 stored_player[i].connected_locally)
3928 char *player_name = getNetworkPlayerName(i + 1);
3929 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3930 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3932 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3934 ypos += player_yoffset;
3938 // ... then draw all other network players
3939 for (i = 0; i < MAX_PLAYERS; i++)
3941 if (stored_player[i].connected_network &&
3942 !stored_player[i].connected_locally)
3944 char *player_name = getNetworkPlayerName(i + 1);
3945 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3946 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3948 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3950 ypos += player_yoffset;
3955 void DrawNetworkPlayers(void)
3957 DrawNetworkPlayersExt(FALSE);
3960 void ClearNetworkPlayers(void)
3962 DrawNetworkPlayersExt(TRUE);
3965 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3966 int graphic, int lx, int ly,
3969 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3971 if (mask_mode == USE_MASKING)
3972 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3974 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3977 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3978 int graphic, int sync_frame, int mask_mode)
3980 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3982 if (mask_mode == USE_MASKING)
3983 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3985 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3988 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3989 int graphic, int sync_frame, int tilesize,
3992 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3994 if (mask_mode == USE_MASKING)
3995 DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
3997 DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
4000 static void DrawGraphicAnimation(int x, int y, int graphic)
4002 int lx = LEVELX(x), ly = LEVELY(y);
4003 int mask_mode = NO_MASKING;
4005 if (!IN_SCR_FIELD(x, y))
4008 if (game.use_masked_elements)
4010 if (Tile[lx][ly] != EL_EMPTY)
4012 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
4014 mask_mode = USE_MASKING;
4018 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
4019 graphic, lx, ly, mask_mode);
4021 MarkTileDirty(x, y);
4024 void DrawFixedGraphicAnimation(int x, int y, int graphic)
4026 int lx = LEVELX(x), ly = LEVELY(y);
4027 int mask_mode = NO_MASKING;
4029 if (!IN_SCR_FIELD(x, y))
4032 if (game.use_masked_elements)
4034 if (Tile[lx][ly] != EL_EMPTY)
4036 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
4038 mask_mode = USE_MASKING;
4042 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
4043 graphic, lx, ly, mask_mode);
4045 MarkTileDirty(x, y);
4048 void DrawLevelGraphicAnimation(int x, int y, int graphic)
4050 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4053 void DrawLevelElementAnimation(int x, int y, int element)
4055 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4057 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4060 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
4062 int sx = SCREENX(x), sy = SCREENY(y);
4064 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4067 if (Tile[x][y] == EL_EMPTY)
4068 graphic = el2img(GfxElementEmpty[x][y]);
4070 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4073 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4076 DrawGraphicAnimation(sx, sy, graphic);
4079 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4080 DrawLevelFieldCrumbled(x, y);
4082 if (GFX_CRUMBLED(Tile[x][y]))
4083 DrawLevelFieldCrumbled(x, y);
4087 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4089 int sx = SCREENX(x), sy = SCREENY(y);
4092 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4095 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4097 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4100 DrawGraphicAnimation(sx, sy, graphic);
4102 if (GFX_CRUMBLED(element))
4103 DrawLevelFieldCrumbled(x, y);
4106 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4108 if (player->use_murphy)
4110 // this works only because currently only one player can be "murphy" ...
4111 static int last_horizontal_dir = MV_LEFT;
4112 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4114 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4115 last_horizontal_dir = move_dir;
4117 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4119 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4121 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4127 return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4130 static boolean equalGraphics(int graphic1, int graphic2)
4132 struct GraphicInfo *g1 = &graphic_info[graphic1];
4133 struct GraphicInfo *g2 = &graphic_info[graphic2];
4135 return (g1->bitmap == g2->bitmap &&
4136 g1->src_x == g2->src_x &&
4137 g1->src_y == g2->src_y &&
4138 g1->anim_frames == g2->anim_frames &&
4139 g1->anim_delay == g2->anim_delay &&
4140 g1->anim_mode == g2->anim_mode);
4143 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4147 DRAW_PLAYER_STAGE_INIT = 0,
4148 DRAW_PLAYER_STAGE_LAST_FIELD,
4149 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4150 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4151 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4152 DRAW_PLAYER_STAGE_PLAYER,
4154 DRAW_PLAYER_STAGE_PLAYER,
4155 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4157 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4158 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4160 NUM_DRAW_PLAYER_STAGES
4163 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4165 static int static_last_player_graphic[MAX_PLAYERS];
4166 static int static_last_player_frame[MAX_PLAYERS];
4167 static boolean static_player_is_opaque[MAX_PLAYERS];
4168 static boolean draw_player[MAX_PLAYERS];
4169 int pnr = player->index_nr;
4171 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4173 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4174 static_last_player_frame[pnr] = player->Frame;
4175 static_player_is_opaque[pnr] = FALSE;
4177 draw_player[pnr] = TRUE;
4180 if (!draw_player[pnr])
4184 if (!IN_LEV_FIELD(player->jx, player->jy))
4186 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4187 Debug("draw:DrawPlayerExt", "This should never happen!");
4189 draw_player[pnr] = FALSE;
4195 int last_player_graphic = static_last_player_graphic[pnr];
4196 int last_player_frame = static_last_player_frame[pnr];
4197 boolean player_is_opaque = static_player_is_opaque[pnr];
4199 int jx = player->jx;
4200 int jy = player->jy;
4201 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4202 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4203 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4204 int last_jx = (player->is_moving ? jx - dx : jx);
4205 int last_jy = (player->is_moving ? jy - dy : jy);
4206 int next_jx = jx + dx;
4207 int next_jy = jy + dy;
4208 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4209 int sx = SCREENX(jx);
4210 int sy = SCREENY(jy);
4211 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4212 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4213 int element = Tile[jx][jy];
4214 int last_element = Tile[last_jx][last_jy];
4215 int action = (player->is_pushing ? ACTION_PUSHING :
4216 player->is_digging ? ACTION_DIGGING :
4217 player->is_collecting ? ACTION_COLLECTING :
4218 player->is_moving ? ACTION_MOVING :
4219 player->is_snapping ? ACTION_SNAPPING :
4220 player->is_dropping ? ACTION_DROPPING :
4221 player->is_waiting ? player->action_waiting :
4224 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4226 // ------------------------------------------------------------------------
4227 // initialize drawing the player
4228 // ------------------------------------------------------------------------
4230 draw_player[pnr] = FALSE;
4232 // GfxElement[][] is set to the element the player is digging or collecting;
4233 // remove also for off-screen player if the player is not moving anymore
4234 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4235 GfxElement[jx][jy] = EL_UNDEFINED;
4237 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4240 if (element == EL_EXPLOSION)
4243 InitPlayerGfxAnimation(player, action, move_dir);
4245 draw_player[pnr] = TRUE;
4247 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4249 // ------------------------------------------------------------------------
4250 // draw things in the field the player is leaving, if needed
4251 // ------------------------------------------------------------------------
4253 if (!IN_SCR_FIELD(sx, sy))
4254 draw_player[pnr] = FALSE;
4256 if (!player->is_moving)
4259 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4261 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4263 if (last_element == EL_DYNAMITE_ACTIVE ||
4264 last_element == EL_EM_DYNAMITE_ACTIVE ||
4265 last_element == EL_SP_DISK_RED_ACTIVE)
4266 DrawDynamite(last_jx, last_jy);
4268 DrawLevelFieldThruMask(last_jx, last_jy);
4270 else if (last_element == EL_DYNAMITE_ACTIVE ||
4271 last_element == EL_EM_DYNAMITE_ACTIVE ||
4272 last_element == EL_SP_DISK_RED_ACTIVE)
4273 DrawDynamite(last_jx, last_jy);
4275 DrawLevelField(last_jx, last_jy);
4277 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4279 // ------------------------------------------------------------------------
4280 // draw things behind the player, if needed
4281 // ------------------------------------------------------------------------
4285 DrawLevelElement(jx, jy, Back[jx][jy]);
4290 if (IS_ACTIVE_BOMB(element))
4292 DrawLevelElement(jx, jy, EL_EMPTY);
4297 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4299 int old_element = GfxElement[jx][jy];
4300 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4301 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4303 if (GFX_CRUMBLED(old_element))
4304 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4306 DrawScreenGraphic(sx, sy, old_graphic, frame);
4308 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4309 static_player_is_opaque[pnr] = TRUE;
4313 GfxElement[jx][jy] = EL_UNDEFINED;
4315 // make sure that pushed elements are drawn with correct frame rate
4316 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4318 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4319 GfxFrame[jx][jy] = player->StepFrame;
4321 DrawLevelField(jx, jy);
4324 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4326 // ------------------------------------------------------------------------
4327 // draw things the player is pushing, if needed
4328 // ------------------------------------------------------------------------
4330 if (!player->is_pushing || !player->is_moving)
4333 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4336 int gfx_frame = GfxFrame[jx][jy];
4338 if (!IS_MOVING(jx, jy)) // push movement already finished
4340 element = Tile[next_jx][next_jy];
4341 gfx_frame = GfxFrame[next_jx][next_jy];
4344 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4345 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4346 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4348 // draw background element under pushed element (like the Sokoban field)
4349 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4351 // this allows transparent pushing animation over non-black background
4354 DrawLevelElement(jx, jy, Back[jx][jy]);
4356 DrawLevelElement(jx, jy, EL_EMPTY);
4359 if (Back[next_jx][next_jy])
4360 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4362 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4364 int px = SCREENX(jx), py = SCREENY(jy);
4365 int pxx = (TILEX - ABS(sxx)) * dx;
4366 int pyy = (TILEY - ABS(syy)) * dy;
4369 // do not draw (EM style) pushing animation when pushing is finished
4370 // (two-tile animations usually do not contain start and end frame)
4371 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4372 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4374 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4376 // masked drawing is needed for EMC style (double) movement graphics
4377 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4378 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4381 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4383 // ------------------------------------------------------------------------
4384 // draw player himself
4385 // ------------------------------------------------------------------------
4387 int graphic = getPlayerGraphic(player, move_dir);
4389 // in the case of changed player action or direction, prevent the current
4390 // animation frame from being restarted for identical animations
4391 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4392 player->Frame = last_player_frame;
4394 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4396 if (player_is_opaque)
4397 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4399 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4401 if (SHIELD_ON(player))
4403 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4404 IMG_SHIELD_NORMAL_ACTIVE);
4405 frame = getGraphicAnimationFrame(graphic, -1);
4407 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4410 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4412 // ------------------------------------------------------------------------
4413 // draw things in front of player (active dynamite or dynabombs)
4414 // ------------------------------------------------------------------------
4416 if (IS_ACTIVE_BOMB(element))
4418 int graphic = el2img(element);
4419 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4421 if (game.emulation == EMU_SUPAPLEX)
4422 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4424 DrawGraphicThruMask(sx, sy, graphic, frame);
4427 if (player_is_moving && last_element == EL_EXPLOSION)
4429 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4430 GfxElement[last_jx][last_jy] : EL_EMPTY);
4431 int graphic = el_act2img(element, ACTION_EXPLODING);
4432 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4433 int phase = ExplodePhase[last_jx][last_jy] - 1;
4434 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4437 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4440 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4442 // ------------------------------------------------------------------------
4443 // draw elements the player is just walking/passing through/under
4444 // ------------------------------------------------------------------------
4446 if (player_is_moving)
4448 // handle the field the player is leaving ...
4449 if (IS_ACCESSIBLE_INSIDE(last_element))
4450 DrawLevelField(last_jx, last_jy);
4451 else if (IS_ACCESSIBLE_UNDER(last_element))
4452 DrawLevelFieldThruMask(last_jx, last_jy);
4455 // do not redraw accessible elements if the player is just pushing them
4456 if (!player_is_moving || !player->is_pushing)
4458 // ... and the field the player is entering
4459 if (IS_ACCESSIBLE_INSIDE(element))
4460 DrawLevelField(jx, jy);
4461 else if (IS_ACCESSIBLE_UNDER(element))
4462 DrawLevelFieldThruMask(jx, jy);
4465 MarkTileDirty(sx, sy);
4469 void DrawPlayer(struct PlayerInfo *player)
4473 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4474 DrawPlayerExt(player, i);
4477 void DrawAllPlayers(void)
4481 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4482 for (j = 0; j < MAX_PLAYERS; j++)
4483 if (stored_player[j].active)
4484 DrawPlayerExt(&stored_player[j], i);
4487 void DrawPlayerField(int x, int y)
4489 if (!IS_PLAYER(x, y))
4492 DrawPlayer(PLAYERINFO(x, y));
4495 // ----------------------------------------------------------------------------
4497 void WaitForEventToContinue(void)
4499 boolean first_wait = TRUE;
4500 boolean still_wait = TRUE;
4502 if (program.headless)
4505 // simulate releasing mouse button over last gadget, if still pressed
4507 HandleGadgets(-1, -1, 0);
4509 button_status = MB_RELEASED;
4512 ClearPlayerAction();
4518 if (NextValidEvent(&event))
4522 case EVENT_BUTTONPRESS:
4523 case EVENT_FINGERPRESS:
4527 case EVENT_BUTTONRELEASE:
4528 case EVENT_FINGERRELEASE:
4529 still_wait = first_wait;
4532 case EVENT_KEYPRESS:
4533 case SDL_CONTROLLERBUTTONDOWN:
4534 case SDL_JOYBUTTONDOWN:
4539 HandleOtherEvents(&event);
4543 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4548 if (!PendingEvent())
4553 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4555 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4556 int draw_buffer_last = GetDrawtoField();
4557 int width = request.width;
4558 int height = request.height;
4562 setRequestPosition(&sx, &sy, FALSE);
4564 button_status = MB_RELEASED;
4566 request_gadget_id = -1;
4573 SetDrawtoField(draw_buffer_game);
4575 HandleGameActions();
4577 SetDrawtoField(DRAW_TO_BACKBUFFER);
4584 while (NextValidEvent(&event))
4588 case EVENT_BUTTONPRESS:
4589 case EVENT_BUTTONRELEASE:
4590 case EVENT_MOTIONNOTIFY:
4592 DrawBuffer *drawto_last = drawto;
4595 if (event.type == EVENT_MOTIONNOTIFY)
4600 motion_status = TRUE;
4601 mx = ((MotionEvent *) &event)->x;
4602 my = ((MotionEvent *) &event)->y;
4606 motion_status = FALSE;
4607 mx = ((ButtonEvent *) &event)->x;
4608 my = ((ButtonEvent *) &event)->y;
4609 if (event.type == EVENT_BUTTONPRESS)
4610 button_status = ((ButtonEvent *) &event)->button;
4612 button_status = MB_RELEASED;
4615 if (global.use_envelope_request)
4617 // draw changed button states to temporary bitmap
4618 drawto = bitmap_db_store_1;
4621 // this sets 'request_gadget_id'
4622 HandleGadgets(mx, my, button_status);
4624 if (global.use_envelope_request)
4626 // restore pointer to drawing buffer
4627 drawto = drawto_last;
4629 // prepare complete envelope request from temporary bitmap
4630 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy,
4634 switch (request_gadget_id)
4636 case TOOL_CTRL_ID_YES:
4637 case TOOL_CTRL_ID_TOUCH_YES:
4640 case TOOL_CTRL_ID_NO:
4641 case TOOL_CTRL_ID_TOUCH_NO:
4644 case TOOL_CTRL_ID_CONFIRM:
4645 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4646 result = TRUE | FALSE;
4649 case TOOL_CTRL_ID_PLAYER_1:
4652 case TOOL_CTRL_ID_PLAYER_2:
4655 case TOOL_CTRL_ID_PLAYER_3:
4658 case TOOL_CTRL_ID_PLAYER_4:
4666 // only needed to handle clickable pointer animations here
4667 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4672 case SDL_WINDOWEVENT:
4673 HandleWindowEvent((WindowEvent *) &event);
4676 case SDL_APP_WILLENTERBACKGROUND:
4677 case SDL_APP_DIDENTERBACKGROUND:
4678 case SDL_APP_WILLENTERFOREGROUND:
4679 case SDL_APP_DIDENTERFOREGROUND:
4680 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4683 case EVENT_KEYPRESS:
4685 Key key = GetEventKey((KeyEvent *)&event);
4690 if (req_state & REQ_CONFIRM)
4699 #if defined(KSYM_Rewind)
4700 case KSYM_Rewind: // for Amazon Fire TV remote
4709 #if defined(KSYM_FastForward)
4710 case KSYM_FastForward: // for Amazon Fire TV remote
4716 HandleKeysDebug(key, KEY_PRESSED);
4720 if (req_state & REQ_PLAYER)
4722 int old_player_nr = setup.network_player_nr;
4725 result = old_player_nr + 1;
4730 result = old_player_nr + 1;
4761 case EVENT_FINGERRELEASE:
4762 case EVENT_KEYRELEASE:
4763 ClearPlayerAction();
4766 case SDL_CONTROLLERBUTTONDOWN:
4767 switch (event.cbutton.button)
4769 case SDL_CONTROLLER_BUTTON_A:
4770 case SDL_CONTROLLER_BUTTON_X:
4771 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4772 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4776 case SDL_CONTROLLER_BUTTON_B:
4777 case SDL_CONTROLLER_BUTTON_Y:
4778 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4779 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4780 case SDL_CONTROLLER_BUTTON_BACK:
4785 if (req_state & REQ_PLAYER)
4787 int old_player_nr = setup.network_player_nr;
4790 result = old_player_nr + 1;
4792 switch (event.cbutton.button)
4794 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4795 case SDL_CONTROLLER_BUTTON_Y:
4799 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4800 case SDL_CONTROLLER_BUTTON_B:
4804 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4805 case SDL_CONTROLLER_BUTTON_A:
4809 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4810 case SDL_CONTROLLER_BUTTON_X:
4821 case SDL_CONTROLLERBUTTONUP:
4822 HandleJoystickEvent(&event);
4823 ClearPlayerAction();
4827 HandleOtherEvents(&event);
4832 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4834 int joy = AnyJoystick();
4836 if (joy & JOY_BUTTON_1)
4838 else if (joy & JOY_BUTTON_2)
4841 else if (AnyJoystick())
4843 int joy = AnyJoystick();
4845 if (req_state & REQ_PLAYER)
4849 else if (joy & JOY_RIGHT)
4851 else if (joy & JOY_DOWN)
4853 else if (joy & JOY_LEFT)
4861 SetDrawtoField(draw_buffer_last);
4866 static void DoRequestBefore(void)
4868 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4870 // when showing request dialog after game ended, deactivate game panel
4872 game.panel.active = FALSE;
4874 if (game_status == GAME_MODE_PLAYING)
4875 BlitScreenToBitmap(backbuffer);
4877 // disable deactivated drawing when quick-loading level tape recording
4878 if (tape.playing && tape.deactivate_display)
4879 TapeDeactivateDisplayOff(TRUE);
4881 SetMouseCursor(CURSOR_DEFAULT);
4883 // pause network game while waiting for request to answer
4884 if (network.enabled &&
4885 game_status == GAME_MODE_PLAYING &&
4886 !game.all_players_gone)
4887 SendToServer_PausePlaying();
4889 // simulate releasing mouse button over last gadget, if still pressed
4891 HandleGadgets(-1, -1, 0);
4896 static void DoRequestAfter(void)
4900 if (game_status == GAME_MODE_PLAYING)
4902 SetPanelBackground();
4903 SetDrawBackgroundMask(REDRAW_DOOR_1);
4907 SetDrawBackgroundMask(REDRAW_FIELD);
4910 // continue network game after request
4911 if (network.enabled &&
4912 game_status == GAME_MODE_PLAYING &&
4913 !game.all_players_gone)
4914 SendToServer_ContinuePlaying();
4916 // restore deactivated drawing when quick-loading level tape recording
4917 if (tape.playing && tape.deactivate_display)
4918 TapeDeactivateDisplayOn();
4921 static void setRequestDoorTextProperties(char *text,
4926 int *set_max_line_length)
4928 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
4929 struct TextPosInfo *pos = &request.button.confirm;
4930 int button_ypos = pos->y;
4931 int font_nr = FONT_TEXT_2;
4932 int font_width = getFontWidth(font_nr);
4933 int font_height = getFontHeight(font_nr);
4934 int line_height = font_height + line_spacing;
4935 int max_text_width = vp_door_1->width;
4936 int max_text_height = button_ypos - 2 * text_spacing;
4937 int max_line_length = max_text_width / font_width;
4938 int max_lines = max_text_height / line_height;
4940 if (maxWordLengthInRequestString(text) > max_line_length)
4942 font_nr = FONT_TEXT_1;
4943 font_width = getFontWidth(font_nr);
4944 max_line_length = max_text_width / font_width;
4947 *set_font_nr = font_nr;
4948 *set_max_lines = max_lines;
4949 *set_max_line_length = max_line_length;
4952 static void DrawRequestDoorText(char *text)
4954 char *text_ptr = text;
4955 int text_spacing = 8;
4956 int line_spacing = 2;
4957 int max_request_lines;
4958 int max_request_line_len;
4962 // force DOOR font inside door area
4963 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4965 setRequestDoorTextProperties(text, text_spacing, line_spacing, &font_nr,
4966 &max_request_lines, &max_request_line_len);
4968 for (text_ptr = text, ty = 0; ty < max_request_lines; ty++)
4970 char text_line[max_request_line_len + 1];
4976 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4978 tc = *(text_ptr + tx);
4979 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4983 if ((tc == '?' || tc == '!') && tl == 0)
4993 strncpy(text_line, text_ptr, tl);
4996 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4997 DY + text_spacing + ty * (getFontHeight(font_nr) + line_spacing),
4998 text_line, font_nr);
5000 text_ptr += tl + (tc == ' ' ? 1 : 0);
5006 static int RequestDoor(char *text, unsigned int req_state)
5008 unsigned int old_door_state = GetDoorState();
5009 int draw_buffer_last = GetDrawtoField();
5012 if (old_door_state & DOOR_OPEN_1)
5014 CloseDoor(DOOR_CLOSE_1);
5016 // save old door content
5017 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5018 0, 0, DXSIZE, DYSIZE, DXSIZE, 0);
5021 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5022 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5024 // clear door drawing field
5025 DrawBackground(DX, DY, DXSIZE, DYSIZE);
5027 // write text for request
5028 DrawRequestDoorText(text);
5030 MapToolButtons(req_state);
5032 // copy request gadgets to door backbuffer
5033 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
5035 OpenDoor(DOOR_OPEN_1);
5037 // ---------- handle request buttons ----------
5038 result = RequestHandleEvents(req_state, draw_buffer_last);
5042 if (!(req_state & REQ_STAY_OPEN))
5044 CloseDoor(DOOR_CLOSE_1);
5046 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
5047 (req_state & REQ_REOPEN))
5048 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
5054 static int RequestEnvelope(char *text, unsigned int req_state)
5056 int draw_buffer_last = GetDrawtoField();
5059 DrawEnvelopeRequest(text, req_state);
5060 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5062 // ---------- handle request buttons ----------
5063 result = RequestHandleEvents(req_state, draw_buffer_last);
5067 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5072 int Request(char *text, unsigned int req_state)
5074 boolean overlay_enabled = GetOverlayEnabled();
5077 game.request_active = TRUE;
5079 SetOverlayEnabled(FALSE);
5083 if (global.use_envelope_request)
5084 result = RequestEnvelope(text, req_state);
5086 result = RequestDoor(text, req_state);
5090 SetOverlayEnabled(overlay_enabled);
5092 game.request_active = FALSE;
5097 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5099 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5100 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5103 if (dpo1->sort_priority != dpo2->sort_priority)
5104 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5106 compare_result = dpo1->nr - dpo2->nr;
5108 return compare_result;
5111 void InitGraphicCompatibilityInfo_Doors(void)
5117 struct DoorInfo *door;
5121 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5122 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5124 { -1, -1, -1, NULL }
5126 struct Rect door_rect_list[] =
5128 { DX, DY, DXSIZE, DYSIZE },
5129 { VX, VY, VXSIZE, VYSIZE }
5133 for (i = 0; doors[i].door_token != -1; i++)
5135 int door_token = doors[i].door_token;
5136 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5137 int part_1 = doors[i].part_1;
5138 int part_8 = doors[i].part_8;
5139 int part_2 = part_1 + 1;
5140 int part_3 = part_1 + 2;
5141 struct DoorInfo *door = doors[i].door;
5142 struct Rect *door_rect = &door_rect_list[door_index];
5143 boolean door_gfx_redefined = FALSE;
5145 // check if any door part graphic definitions have been redefined
5147 for (j = 0; door_part_controls[j].door_token != -1; j++)
5149 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5150 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5152 if (dpc->door_token == door_token && fi->redefined)
5153 door_gfx_redefined = TRUE;
5156 // check for old-style door graphic/animation modifications
5158 if (!door_gfx_redefined)
5160 if (door->anim_mode & ANIM_STATIC_PANEL)
5162 door->panel.step_xoffset = 0;
5163 door->panel.step_yoffset = 0;
5166 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5168 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5169 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5170 int num_door_steps, num_panel_steps;
5172 // remove door part graphics other than the two default wings
5174 for (j = 0; door_part_controls[j].door_token != -1; j++)
5176 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5177 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5179 if (dpc->graphic >= part_3 &&
5180 dpc->graphic <= part_8)
5184 // set graphics and screen positions of the default wings
5186 g_part_1->width = door_rect->width;
5187 g_part_1->height = door_rect->height;
5188 g_part_2->width = door_rect->width;
5189 g_part_2->height = door_rect->height;
5190 g_part_2->src_x = door_rect->width;
5191 g_part_2->src_y = g_part_1->src_y;
5193 door->part_2.x = door->part_1.x;
5194 door->part_2.y = door->part_1.y;
5196 if (door->width != -1)
5198 g_part_1->width = door->width;
5199 g_part_2->width = door->width;
5201 // special treatment for graphics and screen position of right wing
5202 g_part_2->src_x += door_rect->width - door->width;
5203 door->part_2.x += door_rect->width - door->width;
5206 if (door->height != -1)
5208 g_part_1->height = door->height;
5209 g_part_2->height = door->height;
5211 // special treatment for graphics and screen position of bottom wing
5212 g_part_2->src_y += door_rect->height - door->height;
5213 door->part_2.y += door_rect->height - door->height;
5216 // set animation delays for the default wings and panels
5218 door->part_1.step_delay = door->step_delay;
5219 door->part_2.step_delay = door->step_delay;
5220 door->panel.step_delay = door->step_delay;
5222 // set animation draw order for the default wings
5224 door->part_1.sort_priority = 2; // draw left wing over ...
5225 door->part_2.sort_priority = 1; // ... right wing
5227 // set animation draw offset for the default wings
5229 if (door->anim_mode & ANIM_HORIZONTAL)
5231 door->part_1.step_xoffset = door->step_offset;
5232 door->part_1.step_yoffset = 0;
5233 door->part_2.step_xoffset = door->step_offset * -1;
5234 door->part_2.step_yoffset = 0;
5236 num_door_steps = g_part_1->width / door->step_offset;
5238 else // ANIM_VERTICAL
5240 door->part_1.step_xoffset = 0;
5241 door->part_1.step_yoffset = door->step_offset;
5242 door->part_2.step_xoffset = 0;
5243 door->part_2.step_yoffset = door->step_offset * -1;
5245 num_door_steps = g_part_1->height / door->step_offset;
5248 // set animation draw offset for the default panels
5250 if (door->step_offset > 1)
5252 num_panel_steps = 2 * door_rect->height / door->step_offset;
5253 door->panel.start_step = num_panel_steps - num_door_steps;
5254 door->panel.start_step_closing = door->panel.start_step;
5258 num_panel_steps = door_rect->height / door->step_offset;
5259 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5260 door->panel.start_step_closing = door->panel.start_step;
5261 door->panel.step_delay *= 2;
5268 void InitDoors(void)
5272 for (i = 0; door_part_controls[i].door_token != -1; i++)
5274 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5275 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5277 // initialize "start_step_opening" and "start_step_closing", if needed
5278 if (dpc->pos->start_step_opening == 0 &&
5279 dpc->pos->start_step_closing == 0)
5281 // dpc->pos->start_step_opening = dpc->pos->start_step;
5282 dpc->pos->start_step_closing = dpc->pos->start_step;
5285 // fill structure for door part draw order (sorted below)
5287 dpo->sort_priority = dpc->pos->sort_priority;
5290 // sort door part controls according to sort_priority and graphic number
5291 qsort(door_part_order, MAX_DOOR_PARTS,
5292 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5295 unsigned int OpenDoor(unsigned int door_state)
5297 if (door_state & DOOR_COPY_BACK)
5299 if (door_state & DOOR_OPEN_1)
5300 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5301 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5303 if (door_state & DOOR_OPEN_2)
5304 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5305 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5307 door_state &= ~DOOR_COPY_BACK;
5310 return MoveDoor(door_state);
5313 unsigned int CloseDoor(unsigned int door_state)
5315 unsigned int old_door_state = GetDoorState();
5317 if (!(door_state & DOOR_NO_COPY_BACK))
5319 if (old_door_state & DOOR_OPEN_1)
5320 BlitBitmap(backbuffer, bitmap_db_door_1,
5321 DX, DY, DXSIZE, DYSIZE, 0, 0);
5323 if (old_door_state & DOOR_OPEN_2)
5324 BlitBitmap(backbuffer, bitmap_db_door_2,
5325 VX, VY, VXSIZE, VYSIZE, 0, 0);
5327 door_state &= ~DOOR_NO_COPY_BACK;
5330 return MoveDoor(door_state);
5333 unsigned int GetDoorState(void)
5335 return MoveDoor(DOOR_GET_STATE);
5338 unsigned int SetDoorState(unsigned int door_state)
5340 return MoveDoor(door_state | DOOR_SET_STATE);
5343 static int euclid(int a, int b)
5345 return (b ? euclid(b, a % b) : a);
5348 unsigned int MoveDoor(unsigned int door_state)
5350 struct Rect door_rect_list[] =
5352 { DX, DY, DXSIZE, DYSIZE },
5353 { VX, VY, VXSIZE, VYSIZE }
5355 static int door1 = DOOR_CLOSE_1;
5356 static int door2 = DOOR_CLOSE_2;
5357 DelayCounter door_delay = { 0 };
5360 if (door_state == DOOR_GET_STATE)
5361 return (door1 | door2);
5363 if (door_state & DOOR_SET_STATE)
5365 if (door_state & DOOR_ACTION_1)
5366 door1 = door_state & DOOR_ACTION_1;
5367 if (door_state & DOOR_ACTION_2)
5368 door2 = door_state & DOOR_ACTION_2;
5370 return (door1 | door2);
5373 if (!(door_state & DOOR_FORCE_REDRAW))
5375 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5376 door_state &= ~DOOR_OPEN_1;
5377 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5378 door_state &= ~DOOR_CLOSE_1;
5379 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5380 door_state &= ~DOOR_OPEN_2;
5381 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5382 door_state &= ~DOOR_CLOSE_2;
5385 if (global.autoplay_leveldir)
5387 door_state |= DOOR_NO_DELAY;
5388 door_state &= ~DOOR_CLOSE_ALL;
5391 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5392 door_state |= DOOR_NO_DELAY;
5394 if (door_state & DOOR_ACTION)
5396 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
5397 boolean door_panel_drawn[NUM_DOORS];
5398 boolean panel_has_doors[NUM_DOORS];
5399 boolean door_part_skip[MAX_DOOR_PARTS];
5400 boolean door_part_done[MAX_DOOR_PARTS];
5401 boolean door_part_done_all;
5402 int num_steps[MAX_DOOR_PARTS];
5403 int max_move_delay = 0; // delay for complete animations of all doors
5404 int max_step_delay = 0; // delay (ms) between two animation frames
5405 int num_move_steps = 0; // number of animation steps for all doors
5406 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5407 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5411 for (i = 0; i < NUM_DOORS; i++)
5412 panel_has_doors[i] = FALSE;
5414 for (i = 0; i < MAX_DOOR_PARTS; i++)
5416 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5417 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5418 int door_token = dpc->door_token;
5420 door_part_done[i] = FALSE;
5421 door_part_skip[i] = (!(door_state & door_token) ||
5425 for (i = 0; i < MAX_DOOR_PARTS; i++)
5427 int nr = door_part_order[i].nr;
5428 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5429 struct DoorPartPosInfo *pos = dpc->pos;
5430 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5431 int door_token = dpc->door_token;
5432 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5433 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5434 int step_xoffset = ABS(pos->step_xoffset);
5435 int step_yoffset = ABS(pos->step_yoffset);
5436 int step_delay = pos->step_delay;
5437 int current_door_state = door_state & door_token;
5438 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5439 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5440 boolean part_opening = (is_panel ? door_closing : door_opening);
5441 int start_step = (part_opening ? pos->start_step_opening :
5442 pos->start_step_closing);
5443 float move_xsize = (step_xoffset ? g->width : 0);
5444 float move_ysize = (step_yoffset ? g->height : 0);
5445 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5446 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5447 int move_steps = (move_xsteps && move_ysteps ?
5448 MIN(move_xsteps, move_ysteps) :
5449 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5450 int move_delay = move_steps * step_delay;
5452 if (door_part_skip[nr])
5455 max_move_delay = MAX(max_move_delay, move_delay);
5456 max_step_delay = (max_step_delay == 0 ? step_delay :
5457 euclid(max_step_delay, step_delay));
5458 num_steps[nr] = move_steps;
5462 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5464 panel_has_doors[door_index] = TRUE;
5468 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5470 num_move_steps = max_move_delay / max_step_delay;
5471 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5473 door_delay.value = max_step_delay;
5475 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5477 start = num_move_steps - 1;
5481 // opening door sound has priority over simultaneously closing door
5482 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5484 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5486 if (door_state & DOOR_OPEN_1)
5487 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5488 if (door_state & DOOR_OPEN_2)
5489 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5491 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5493 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5495 if (door_state & DOOR_CLOSE_1)
5496 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5497 if (door_state & DOOR_CLOSE_2)
5498 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5502 for (k = start; k < num_move_steps; k++)
5504 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5506 door_part_done_all = TRUE;
5508 for (i = 0; i < NUM_DOORS; i++)
5509 door_panel_drawn[i] = FALSE;
5511 for (i = 0; i < MAX_DOOR_PARTS; i++)
5513 int nr = door_part_order[i].nr;
5514 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5515 struct DoorPartPosInfo *pos = dpc->pos;
5516 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5517 int door_token = dpc->door_token;
5518 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5519 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5520 boolean is_panel_and_door_has_closed = FALSE;
5521 struct Rect *door_rect = &door_rect_list[door_index];
5522 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5524 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5525 int current_door_state = door_state & door_token;
5526 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5527 boolean door_closing = !door_opening;
5528 boolean part_opening = (is_panel ? door_closing : door_opening);
5529 boolean part_closing = !part_opening;
5530 int start_step = (part_opening ? pos->start_step_opening :
5531 pos->start_step_closing);
5532 int step_delay = pos->step_delay;
5533 int step_factor = step_delay / max_step_delay;
5534 int k1 = (step_factor ? k / step_factor + 1 : k);
5535 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5536 int kk = MAX(0, k2);
5539 int src_x, src_y, src_xx, src_yy;
5540 int dst_x, dst_y, dst_xx, dst_yy;
5543 if (door_part_skip[nr])
5546 if (!(door_state & door_token))
5554 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5555 int kk_door = MAX(0, k2_door);
5556 int sync_frame = kk_door * door_delay.value;
5557 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5559 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5560 &g_src_x, &g_src_y);
5565 if (!door_panel_drawn[door_index])
5567 ClearRectangle(drawto, door_rect->x, door_rect->y,
5568 door_rect->width, door_rect->height);
5570 door_panel_drawn[door_index] = TRUE;
5573 // draw opening or closing door parts
5575 if (pos->step_xoffset < 0) // door part on right side
5578 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5581 if (dst_xx + width > door_rect->width)
5582 width = door_rect->width - dst_xx;
5584 else // door part on left side
5587 dst_xx = pos->x - kk * pos->step_xoffset;
5591 src_xx = ABS(dst_xx);
5595 width = g->width - src_xx;
5597 if (width > door_rect->width)
5598 width = door_rect->width;
5600 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5603 if (pos->step_yoffset < 0) // door part on bottom side
5606 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5609 if (dst_yy + height > door_rect->height)
5610 height = door_rect->height - dst_yy;
5612 else // door part on top side
5615 dst_yy = pos->y - kk * pos->step_yoffset;
5619 src_yy = ABS(dst_yy);
5623 height = g->height - src_yy;
5626 src_x = g_src_x + src_xx;
5627 src_y = g_src_y + src_yy;
5629 dst_x = door_rect->x + dst_xx;
5630 dst_y = door_rect->y + dst_yy;
5632 is_panel_and_door_has_closed =
5635 panel_has_doors[door_index] &&
5636 k >= num_move_steps_doors_only - 1);
5638 if (width >= 0 && width <= g->width &&
5639 height >= 0 && height <= g->height &&
5640 !is_panel_and_door_has_closed)
5642 if (is_panel || !pos->draw_masked)
5643 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5646 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5650 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5652 if ((part_opening && (width < 0 || height < 0)) ||
5653 (part_closing && (width >= g->width && height >= g->height)))
5654 door_part_done[nr] = TRUE;
5656 // continue door part animations, but not panel after door has closed
5657 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5658 door_part_done_all = FALSE;
5661 if (!(door_state & DOOR_NO_DELAY))
5664 HandleGameActions();
5668 SkipUntilDelayReached(&door_delay, &k, last_frame);
5670 // prevent OS (Windows) from complaining about program not responding
5674 if (door_part_done_all)
5678 if (!(door_state & DOOR_NO_DELAY))
5680 // wait for specified door action post delay
5681 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5682 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5683 else if (door_state & DOOR_ACTION_1)
5684 door_delay.value = door_1.post_delay;
5685 else if (door_state & DOOR_ACTION_2)
5686 door_delay.value = door_2.post_delay;
5688 while (!DelayReached(&door_delay))
5691 HandleGameActions();
5698 if (door_state & DOOR_ACTION_1)
5699 door1 = door_state & DOOR_ACTION_1;
5700 if (door_state & DOOR_ACTION_2)
5701 door2 = door_state & DOOR_ACTION_2;
5703 // draw masked border over door area
5704 DrawMaskedBorder(REDRAW_DOOR_1);
5705 DrawMaskedBorder(REDRAW_DOOR_2);
5707 ClearAutoRepeatKeyEvents();
5709 return (door1 | door2);
5712 static boolean useSpecialEditorDoor(void)
5714 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5715 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5717 // do not draw special editor door if editor border defined or redefined
5718 if (graphic_info[graphic].bitmap != NULL || redefined)
5721 // do not draw special editor door if global border defined to be empty
5722 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5725 // do not draw special editor door if viewport definitions do not match
5729 EY + EYSIZE != VY + VYSIZE)
5735 void DrawSpecialEditorDoor(void)
5737 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5738 int top_border_width = gfx1->width;
5739 int top_border_height = gfx1->height;
5740 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5741 int ex = EX - outer_border;
5742 int ey = EY - outer_border;
5743 int vy = VY - outer_border;
5744 int exsize = EXSIZE + 2 * outer_border;
5746 if (!useSpecialEditorDoor())
5749 // draw bigger level editor toolbox window
5750 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5751 top_border_width, top_border_height, ex, ey - top_border_height);
5752 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5753 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5755 redraw_mask |= REDRAW_ALL;
5758 void UndrawSpecialEditorDoor(void)
5760 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5761 int top_border_width = gfx1->width;
5762 int top_border_height = gfx1->height;
5763 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5764 int ex = EX - outer_border;
5765 int ey = EY - outer_border;
5766 int ey_top = ey - top_border_height;
5767 int exsize = EXSIZE + 2 * outer_border;
5768 int eysize = EYSIZE + 2 * outer_border;
5770 if (!useSpecialEditorDoor())
5773 // draw normal tape recorder window
5774 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5776 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5777 ex, ey_top, top_border_width, top_border_height,
5779 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5780 ex, ey, exsize, eysize, ex, ey);
5784 // if screen background is set to "[NONE]", clear editor toolbox window
5785 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5786 ClearRectangle(drawto, ex, ey, exsize, eysize);
5789 redraw_mask |= REDRAW_ALL;
5793 // ---------- new tool button stuff -------------------------------------------
5798 struct TextPosInfo *pos;
5800 boolean is_touch_button;
5802 } toolbutton_info[NUM_TOOL_BUTTONS] =
5805 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5806 TOOL_CTRL_ID_YES, FALSE, "yes"
5809 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5810 TOOL_CTRL_ID_NO, FALSE, "no"
5813 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5814 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5817 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5818 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5821 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5822 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5825 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5826 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5829 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5830 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5833 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5834 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5837 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5838 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5841 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5842 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5846 void CreateToolButtons(void)
5850 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5852 int graphic = toolbutton_info[i].graphic;
5853 struct GraphicInfo *gfx = &graphic_info[graphic];
5854 struct TextPosInfo *pos = toolbutton_info[i].pos;
5855 struct GadgetInfo *gi;
5856 Bitmap *deco_bitmap = None;
5857 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5858 unsigned int event_mask = GD_EVENT_RELEASED;
5859 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5860 int base_x = (is_touch_button ? 0 : DX);
5861 int base_y = (is_touch_button ? 0 : DY);
5862 int gd_x = gfx->src_x;
5863 int gd_y = gfx->src_y;
5864 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5865 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5870 // do not use touch buttons if overlay touch buttons are disabled
5871 if (is_touch_button && !setup.touch.overlay_buttons)
5874 if (global.use_envelope_request && !is_touch_button)
5876 setRequestPosition(&base_x, &base_y, TRUE);
5878 // check if request buttons are outside of envelope and fix, if needed
5879 if (x < 0 || x + gfx->width > request.width ||
5880 y < 0 || y + gfx->height > request.height)
5882 if (id == TOOL_CTRL_ID_YES)
5885 y = request.height - 2 * request.border_size - gfx->height;
5887 else if (id == TOOL_CTRL_ID_NO)
5889 x = request.width - 2 * request.border_size - gfx->width;
5890 y = request.height - 2 * request.border_size - gfx->height;
5892 else if (id == TOOL_CTRL_ID_CONFIRM)
5894 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5895 y = request.height - 2 * request.border_size - gfx->height;
5897 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5899 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5901 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5902 y = request.height - 2 * request.border_size - gfx->height * 2;
5904 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5905 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5910 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5913 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5915 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5916 pos->size, &deco_bitmap, &deco_x, &deco_y);
5917 deco_xpos = (gfx->width - pos->size) / 2;
5918 deco_ypos = (gfx->height - pos->size) / 2;
5921 gi = CreateGadget(GDI_CUSTOM_ID, id,
5922 GDI_IMAGE_ID, graphic,
5923 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5926 GDI_WIDTH, gfx->width,
5927 GDI_HEIGHT, gfx->height,
5928 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5929 GDI_STATE, GD_BUTTON_UNPRESSED,
5930 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5931 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5932 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5933 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5934 GDI_DECORATION_SIZE, pos->size, pos->size,
5935 GDI_DECORATION_SHIFTING, 1, 1,
5936 GDI_DIRECT_DRAW, FALSE,
5937 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5938 GDI_EVENT_MASK, event_mask,
5939 GDI_CALLBACK_ACTION, HandleToolButtons,
5943 Fail("cannot create gadget");
5945 tool_gadget[id] = gi;
5949 void FreeToolButtons(void)
5953 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5954 FreeGadget(tool_gadget[i]);
5957 static void MapToolButtons(unsigned int req_state)
5959 if (req_state & REQ_ASK)
5961 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
5962 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
5963 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
5964 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
5966 else if (req_state & REQ_CONFIRM)
5968 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
5969 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
5971 else if (req_state & REQ_PLAYER)
5973 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
5974 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
5975 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
5976 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
5980 static void UnmapToolButtons(void)
5984 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5985 UnmapGadget(tool_gadget[i]);
5988 static void HandleToolButtons(struct GadgetInfo *gi)
5990 request_gadget_id = gi->custom_id;
5993 static struct Mapping_BD_to_RND_object
5996 boolean is_rnd_to_bd_mapping; // unique mapping BD <-> RND
6002 bd_object_mapping_list[] =
6004 // additional RND style elements mapped to BD style elements must be listed first
6020 EL_STEELWALL, -1, -1
6024 EL_BD_DIAMOND, -1, -1
6044 EL_EXIT_CLOSED, -1, -1
6047 // BD style elements with their corresponding RND style elements
6058 O_DIRT_SLOPED_UP_RIGHT, TRUE,
6059 EL_BD_SAND_SLOPED_UP_RIGHT, -1, -1
6062 O_DIRT_SLOPED_UP_LEFT, TRUE,
6063 EL_BD_SAND_SLOPED_UP_LEFT, -1, -1
6066 O_DIRT_SLOPED_DOWN_LEFT, TRUE,
6067 EL_BD_SAND_SLOPED_DOWN_LEFT, -1, -1
6070 O_DIRT_SLOPED_DOWN_RIGHT, TRUE,
6071 EL_BD_SAND_SLOPED_DOWN_RIGHT, -1, -1
6075 EL_BD_SAND_BALL, -1, -1
6078 O_DIRT_BALL_F, FALSE,
6079 EL_BD_SAND_BALL, ACTION_FALLING, -1
6083 EL_BD_SAND_LOOSE, -1, -1
6086 O_DIRT_LOOSE_F, FALSE,
6087 EL_BD_SAND_LOOSE, ACTION_FALLING, -1
6091 EL_BD_SAND_2, -1, -1
6098 O_BRICK_SLOPED_UP_RIGHT, TRUE,
6099 EL_BD_WALL_SLOPED_UP_RIGHT, -1, -1
6102 O_BRICK_SLOPED_UP_LEFT, TRUE,
6103 EL_BD_WALL_SLOPED_UP_LEFT, -1, -1
6106 O_BRICK_SLOPED_DOWN_LEFT, TRUE,
6107 EL_BD_WALL_SLOPED_DOWN_LEFT, -1, -1
6110 O_BRICK_SLOPED_DOWN_RIGHT, TRUE,
6111 EL_BD_WALL_SLOPED_DOWN_RIGHT, -1, -1
6114 O_BRICK_NON_SLOPED, TRUE,
6115 EL_BD_WALL_NON_SLOPED, -1, -1
6119 EL_BD_MAGIC_WALL, ACTION_ACTIVE, -1
6123 EL_BD_EXIT_CLOSED, -1, -1
6127 EL_BD_EXIT_OPEN, -1, -1
6130 O_PRE_INVIS_OUTBOX, TRUE,
6131 EL_BD_INVISIBLE_EXIT_CLOSED, -1, -1
6134 O_INVIS_OUTBOX, TRUE,
6135 EL_BD_INVISIBLE_EXIT_OPEN, -1, -1
6139 EL_BD_STEELWALL, -1, -1
6142 O_STEEL_SLOPED_UP_RIGHT, TRUE,
6143 EL_BD_STEELWALL_SLOPED_UP_RIGHT, -1, -1
6146 O_STEEL_SLOPED_UP_LEFT, TRUE,
6147 EL_BD_STEELWALL_SLOPED_UP_LEFT, -1, -1
6150 O_STEEL_SLOPED_DOWN_LEFT, TRUE,
6151 EL_BD_STEELWALL_SLOPED_DOWN_LEFT, -1, -1
6154 O_STEEL_SLOPED_DOWN_RIGHT, TRUE,
6155 EL_BD_STEELWALL_SLOPED_DOWN_RIGHT, -1, -1
6158 O_STEEL_EXPLODABLE, TRUE,
6159 EL_BD_STEELWALL_EXPLODABLE, -1, -1
6162 O_STEEL_EATABLE, TRUE,
6163 EL_BD_STEELWALL_DIGGABLE, -1, -1
6166 O_BRICK_EATABLE, TRUE,
6167 EL_BD_WALL_DIGGABLE, -1, -1
6175 EL_BD_ROCK, ACTION_FALLING, -1
6178 O_FLYING_STONE, TRUE,
6179 EL_BD_FLYING_ROCK, -1, -1
6182 O_FLYING_STONE_F, FALSE,
6183 EL_BD_FLYING_ROCK, ACTION_FALLING, -1
6187 EL_BD_MEGA_ROCK, -1, -1
6190 O_MEGA_STONE_F, FALSE,
6191 EL_BD_MEGA_ROCK, ACTION_FALLING, -1
6195 EL_BD_DIAMOND, -1, -1
6199 EL_BD_DIAMOND, ACTION_FALLING, -1
6202 O_FLYING_DIAMOND, TRUE,
6203 EL_BD_FLYING_DIAMOND, -1, -1
6206 O_FLYING_DIAMOND_F, FALSE,
6207 EL_BD_FLYING_DIAMOND, ACTION_FALLING, -1
6215 EL_BD_NUT, ACTION_FALLING, -1
6218 O_BLADDER_SPENDER, TRUE,
6219 EL_BD_BLADDER_SPENDER, -1, -1
6226 O_H_EXPANDING_WALL, TRUE,
6227 EL_BD_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6230 O_V_EXPANDING_WALL, TRUE,
6231 EL_BD_EXPANDABLE_WALL_VERTICAL, -1, -1
6234 O_EXPANDING_WALL, TRUE,
6235 EL_BD_EXPANDABLE_WALL_ANY, -1, -1
6238 O_H_EXPANDING_STEEL_WALL, TRUE,
6239 EL_BD_EXPANDABLE_STEELWALL_HORIZONTAL, -1, -1
6242 O_V_EXPANDING_STEEL_WALL, TRUE,
6243 EL_BD_EXPANDABLE_STEELWALL_VERTICAL, -1, -1
6246 O_EXPANDING_STEEL_WALL, TRUE,
6247 EL_BD_EXPANDABLE_STEELWALL_ANY, -1, -1
6250 O_EXPANDING_WALL_SWITCH, TRUE,
6251 EL_BD_EXPANDABLE_WALL_SWITCH_HORIZONTAL, -1, -1
6254 O_CREATURE_SWITCH, TRUE,
6255 EL_BD_CREATURE_SWITCH, -1, -1
6258 O_BITER_SWITCH, TRUE,
6259 EL_BD_BITER_SWITCH_1, -1, -1
6262 O_REPLICATOR_SWITCH, TRUE,
6263 EL_BD_REPLICATOR_SWITCH, -1, -1
6266 O_CONVEYOR_SWITCH, TRUE,
6267 EL_BD_CONVEYOR_SWITCH, -1, -1
6270 O_CONVEYOR_DIR_SWITCH, TRUE,
6271 EL_BD_CONVEYOR_DIR_SWITCH_RIGHT, -1, -1
6278 O_FALLING_WALL, TRUE,
6279 EL_BD_FALLING_WALL, -1, -1
6282 O_FALLING_WALL_F, FALSE,
6283 EL_BD_FALLING_WALL, ACTION_FALLING, -1
6290 O_TIME_PENALTY, TRUE,
6291 EL_BD_TIME_PENALTY, -1, -1
6295 EL_BD_GRAVESTONE, -1, -1
6298 O_STONE_GLUED, TRUE,
6299 EL_BD_ROCK_GLUED, -1, -1
6302 O_DIAMOND_GLUED, TRUE,
6303 EL_BD_DIAMOND_GLUED, -1, -1
6306 O_DIAMOND_KEY, TRUE,
6307 EL_BD_DIAMOND_KEY, -1, -1
6310 O_TRAPPED_DIAMOND, TRUE,
6311 EL_BD_TRAPPED_DIAMOND, -1, -1
6319 EL_BD_SAND_GLUED, -1, -1
6335 EL_BD_GATE_1, -1, -1
6339 EL_BD_GATE_2, -1, -1
6343 EL_BD_GATE_3, -1, -1
6350 O_GRAVITY_SWITCH, TRUE,
6351 EL_BD_GRAVITY_SWITCH, -1, -1
6354 O_PNEUMATIC_HAMMER, TRUE,
6355 EL_BD_PNEUMATIC_HAMMER, -1, -1
6359 EL_BD_TELEPORTER, -1, -1
6363 EL_BD_SKELETON, -1, -1
6435 EL_BD_COW_LEFT, -1, -1
6439 EL_BD_COW_UP, -1, -1
6443 EL_BD_COW_RIGHT, -1, -1
6447 EL_BD_COW_DOWN, -1, -1
6450 O_COW_ENCLOSED_1, FALSE,
6451 EL_BD_COW_DOWN, -1, -1
6454 O_COW_ENCLOSED_2, FALSE,
6455 EL_BD_COW_DOWN, -1, -1
6458 O_COW_ENCLOSED_3, FALSE,
6459 EL_BD_COW_DOWN, -1, -1
6462 O_COW_ENCLOSED_4, FALSE,
6463 EL_BD_COW_DOWN, -1, -1
6466 O_COW_ENCLOSED_5, FALSE,
6467 EL_BD_COW_DOWN, -1, -1
6470 O_COW_ENCLOSED_6, FALSE,
6471 EL_BD_COW_DOWN, -1, -1
6474 O_COW_ENCLOSED_7, FALSE,
6475 EL_BD_COW_DOWN, -1, -1
6478 O_WALLED_DIAMOND, TRUE,
6479 EL_BD_WALL_DIAMOND, -1, -1
6482 O_WALLED_KEY_1, TRUE,
6483 EL_BD_WALL_KEY_1, -1, -1
6486 O_WALLED_KEY_2, TRUE,
6487 EL_BD_WALL_KEY_2, -1, -1
6490 O_WALLED_KEY_3, TRUE,
6491 EL_BD_WALL_KEY_3, -1, -1
6495 EL_BD_AMOEBA, -1, -1
6499 EL_BD_AMOEBA_2, -1, -1
6503 EL_BD_REPLICATOR, -1, -1
6506 O_CONVEYOR_LEFT, TRUE,
6507 EL_BD_CONVEYOR_LEFT, -1, -1
6510 O_CONVEYOR_RIGHT, TRUE,
6511 EL_BD_CONVEYOR_RIGHT, -1, -1
6523 EL_BD_VOODOO_DOLL, -1, -1
6531 EL_BD_BLADDER, -1, -1
6535 EL_BD_BLADDER, -1, -1
6539 EL_BD_BLADDER, -1, -1
6543 EL_BD_BLADDER, -1, -1
6547 EL_BD_BLADDER, -1, -1
6551 EL_BD_BLADDER, -1, -1
6555 EL_BD_BLADDER, -1, -1
6559 EL_BD_BLADDER, -1, -1
6563 EL_BD_BLADDER, -1, -1
6566 O_WAITING_STONE, TRUE,
6567 EL_BD_WAITING_ROCK, -1, -1
6570 O_CHASING_STONE, TRUE,
6571 EL_BD_CHASING_ROCK, -1, -1
6579 EL_BD_FIREFLY_LEFT, -1, -1
6583 EL_BD_FIREFLY_UP, -1, -1
6587 EL_BD_FIREFLY_RIGHT, -1, -1
6591 EL_BD_FIREFLY_DOWN, -1, -1
6594 O_ALT_FIREFLY_1, TRUE,
6595 EL_BD_FIREFLY_2_LEFT, -1, -1
6598 O_ALT_FIREFLY_2, TRUE,
6599 EL_BD_FIREFLY_2_UP, -1, -1
6602 O_ALT_FIREFLY_3, TRUE,
6603 EL_BD_FIREFLY_2_RIGHT, -1, -1
6606 O_ALT_FIREFLY_4, TRUE,
6607 EL_BD_FIREFLY_2_DOWN, -1, -1
6611 EL_BD_BUTTERFLY_LEFT, -1, -1
6615 EL_BD_BUTTERFLY_UP, -1, -1
6619 EL_BD_BUTTERFLY_RIGHT, -1, -1
6623 EL_BD_BUTTERFLY_DOWN, -1, -1
6626 O_ALT_BUTTER_1, TRUE,
6627 EL_BD_BUTTERFLY_2_LEFT, -1, -1
6630 O_ALT_BUTTER_2, TRUE,
6631 EL_BD_BUTTERFLY_2_UP, -1, -1
6634 O_ALT_BUTTER_3, TRUE,
6635 EL_BD_BUTTERFLY_2_RIGHT, -1, -1
6638 O_ALT_BUTTER_4, TRUE,
6639 EL_BD_BUTTERFLY_2_DOWN, -1, -1
6643 EL_BD_STONEFLY_LEFT, -1, -1
6647 EL_BD_STONEFLY_UP, -1, -1
6651 EL_BD_STONEFLY_RIGHT, -1, -1
6655 EL_BD_STONEFLY_DOWN, -1, -1
6659 EL_BD_BITER_UP, -1, -1
6663 EL_BD_BITER_RIGHT, -1, -1
6667 EL_BD_BITER_DOWN, -1, -1
6671 EL_BD_BITER_LEFT, -1, -1
6674 O_DRAGONFLY_1, TRUE,
6675 EL_BD_DRAGONFLY_LEFT, -1, -1
6678 O_DRAGONFLY_2, TRUE,
6679 EL_BD_DRAGONFLY_UP, -1, -1
6682 O_DRAGONFLY_3, TRUE,
6683 EL_BD_DRAGONFLY_RIGHT, -1, -1
6686 O_DRAGONFLY_4, TRUE,
6687 EL_BD_DRAGONFLY_DOWN, -1, -1
6691 EL_BD_PLAYER, ACTION_GROWING, -1
6695 EL_BD_PLAYER, ACTION_GROWING, -1
6699 EL_BD_PLAYER, ACTION_GROWING, -1
6703 EL_BD_PLAYER, -1, -1
6706 O_PLAYER_BOMB, TRUE,
6707 EL_BD_PLAYER_WITH_BOMB, -1, -1
6710 O_PLAYER_GLUED, TRUE,
6711 EL_BD_PLAYER_GLUED, -1, -1
6714 O_PLAYER_STIRRING, TRUE,
6715 EL_BD_PLAYER_STIRRING, -1, -1
6722 O_BOMB_TICK_1, FALSE,
6723 EL_BD_BOMB, ACTION_ACTIVE, -1
6726 O_BOMB_TICK_2, FALSE,
6727 EL_BD_BOMB, ACTION_ACTIVE, -1
6730 O_BOMB_TICK_3, FALSE,
6731 EL_BD_BOMB, ACTION_ACTIVE, -1
6734 O_BOMB_TICK_4, FALSE,
6735 EL_BD_BOMB, ACTION_ACTIVE, -1
6738 O_BOMB_TICK_5, FALSE,
6739 EL_BD_BOMB, ACTION_ACTIVE, -1
6742 O_BOMB_TICK_6, FALSE,
6743 EL_BD_BOMB, ACTION_ACTIVE, -1
6746 O_BOMB_TICK_7, FALSE,
6747 EL_BD_BOMB, ACTION_ACTIVE, -1
6751 EL_BD_NITRO_PACK, -1, -1
6754 O_NITRO_PACK_F, FALSE,
6755 EL_BD_NITRO_PACK, ACTION_FALLING, -1
6758 O_NITRO_PACK_EXPLODE, FALSE,
6759 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
6762 O_PRE_CLOCK_1, FALSE,
6763 EL_BD_CLOCK, ACTION_GROWING, -1
6766 O_PRE_CLOCK_2, FALSE,
6767 EL_BD_CLOCK, ACTION_GROWING, -1
6770 O_PRE_CLOCK_3, FALSE,
6771 EL_BD_CLOCK, ACTION_GROWING, -1
6774 O_PRE_CLOCK_4, FALSE,
6775 EL_BD_CLOCK, ACTION_GROWING, -1
6779 EL_BD_DIAMOND, ACTION_GROWING, -1
6783 EL_BD_DIAMOND, ACTION_GROWING, -1
6787 EL_BD_DIAMOND, ACTION_GROWING, -1
6791 EL_BD_DIAMOND, ACTION_GROWING, -1
6795 EL_BD_DIAMOND, ACTION_GROWING, -1
6799 EL_DEFAULT, ACTION_EXPLODING, -1
6803 EL_DEFAULT, ACTION_EXPLODING, -1
6807 EL_DEFAULT, ACTION_EXPLODING, -1
6811 EL_DEFAULT, ACTION_EXPLODING, -1
6815 EL_DEFAULT, ACTION_EXPLODING, -1
6818 O_PRE_STONE_1, FALSE,
6819 EL_BD_ROCK, ACTION_GROWING, -1
6822 O_PRE_STONE_2, FALSE,
6823 EL_BD_ROCK, ACTION_GROWING, -1
6826 O_PRE_STONE_3, FALSE,
6827 EL_BD_ROCK, ACTION_GROWING, -1
6830 O_PRE_STONE_4, FALSE,
6831 EL_BD_ROCK, ACTION_GROWING, -1
6834 O_PRE_STEEL_1, FALSE,
6835 EL_BD_STEELWALL, ACTION_GROWING, -1
6838 O_PRE_STEEL_2, FALSE,
6839 EL_BD_STEELWALL, ACTION_GROWING, -1
6842 O_PRE_STEEL_3, FALSE,
6843 EL_BD_STEELWALL, ACTION_GROWING, -1
6846 O_PRE_STEEL_4, FALSE,
6847 EL_BD_STEELWALL, ACTION_GROWING, -1
6850 O_GHOST_EXPL_1, FALSE,
6851 EL_BD_GHOST, ACTION_EXPLODING, -1
6854 O_GHOST_EXPL_2, FALSE,
6855 EL_BD_GHOST, ACTION_EXPLODING, -1
6858 O_GHOST_EXPL_3, FALSE,
6859 EL_BD_GHOST, ACTION_EXPLODING, -1
6862 O_GHOST_EXPL_4, FALSE,
6863 EL_BD_GHOST, ACTION_EXPLODING, -1
6866 O_BOMB_EXPL_1, FALSE,
6867 EL_BD_BOMB, ACTION_EXPLODING, -1
6870 O_BOMB_EXPL_2, FALSE,
6871 EL_BD_BOMB, ACTION_EXPLODING, -1
6874 O_BOMB_EXPL_3, FALSE,
6875 EL_BD_BOMB, ACTION_EXPLODING, -1
6878 O_BOMB_EXPL_4, FALSE,
6879 EL_BD_BOMB, ACTION_EXPLODING, -1
6882 O_NITRO_EXPL_1, FALSE,
6883 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
6886 O_NITRO_EXPL_2, FALSE,
6887 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
6890 O_NITRO_EXPL_3, FALSE,
6891 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
6894 O_NITRO_EXPL_4, FALSE,
6895 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
6898 O_AMOEBA_2_EXPL_1, FALSE,
6899 EL_BD_AMOEBA_2, ACTION_EXPLODING, -1
6902 O_AMOEBA_2_EXPL_2, FALSE,
6903 EL_BD_AMOEBA_2, ACTION_EXPLODING, -1
6906 O_AMOEBA_2_EXPL_3, FALSE,
6907 EL_BD_AMOEBA_2, ACTION_EXPLODING, -1
6910 O_AMOEBA_2_EXPL_4, FALSE,
6911 EL_BD_AMOEBA_2, ACTION_EXPLODING, -1
6914 O_NUT_EXPL_1, FALSE,
6915 EL_BD_NUT, ACTION_BREAKING, -1
6918 O_NUT_EXPL_2, FALSE,
6919 EL_BD_NUT, ACTION_BREAKING, -1
6922 O_NUT_EXPL_3, FALSE,
6923 EL_BD_NUT, ACTION_BREAKING, -1
6926 O_NUT_EXPL_4, FALSE,
6927 EL_BD_NUT, ACTION_BREAKING, -1
6930 O_PLAYER_PNEUMATIC_LEFT, FALSE,
6931 EL_BD_PLAYER, ACTION_HITTING, MV_BIT_LEFT
6934 O_PLAYER_PNEUMATIC_RIGHT, FALSE,
6935 EL_BD_PLAYER, ACTION_HITTING, MV_BIT_RIGHT
6938 O_PNEUMATIC_ACTIVE_LEFT, TRUE,
6939 EL_BD_PNEUMATIC_HAMMER, ACTION_HITTING, MV_BIT_LEFT
6942 O_PNEUMATIC_ACTIVE_RIGHT, TRUE,
6943 EL_BD_PNEUMATIC_HAMMER, ACTION_HITTING, MV_BIT_RIGHT
6946 // helper (runtime) elements
6949 O_FAKE_BONUS, FALSE,
6950 EL_BD_FAKE_BONUS, -1, -1
6953 O_INBOX_CLOSED, FALSE,
6957 O_INBOX_OPEN, FALSE,
6958 EL_BD_INBOX, ACTION_OPENING, -1
6961 O_OUTBOX_CLOSED, FALSE,
6962 EL_BD_EXIT_CLOSED, -1, -1
6965 O_OUTBOX_OPEN, FALSE,
6966 EL_BD_EXIT_OPEN, -1, -1
6970 EL_BD_COVERED, -1, -1
6973 O_PLAYER_LEFT, FALSE,
6974 EL_BD_PLAYER, ACTION_MOVING, MV_BIT_LEFT
6977 O_PLAYER_RIGHT, FALSE,
6978 EL_BD_PLAYER, ACTION_MOVING, MV_BIT_RIGHT
6981 O_PLAYER_BLINK, FALSE,
6982 EL_BD_PLAYER, ACTION_BORING_1, -1
6985 O_PLAYER_TAP, FALSE,
6986 EL_BD_PLAYER, ACTION_BORING_2, -1
6989 O_PLAYER_TAP_BLINK, FALSE,
6990 EL_BD_PLAYER, ACTION_BORING_3, -1
6993 O_CREATURE_SWITCH_ON, FALSE,
6994 EL_BD_CREATURE_SWITCH_ACTIVE, -1, -1
6997 O_EXPANDING_WALL_SWITCH_HORIZ, FALSE,
6998 EL_BD_EXPANDABLE_WALL_SWITCH_HORIZONTAL, -1, -1
7001 O_EXPANDING_WALL_SWITCH_VERT, FALSE,
7002 EL_BD_EXPANDABLE_WALL_SWITCH_VERTICAL, -1, -1
7005 O_GRAVITY_SWITCH_ACTIVE, FALSE,
7006 EL_BD_GRAVITY_SWITCH_ACTIVE, -1, -1
7009 O_REPLICATOR_SWITCH_OFF, FALSE,
7010 EL_BD_REPLICATOR_SWITCH, -1, -1
7013 O_REPLICATOR_SWITCH_ON, FALSE,
7014 EL_BD_REPLICATOR_SWITCH_ACTIVE, -1, -1
7017 O_CONVEYOR_DIR_NORMAL, FALSE,
7018 EL_BD_CONVEYOR_DIR_SWITCH_RIGHT, -1, -1
7021 O_CONVEYOR_DIR_CHANGED, FALSE,
7022 EL_BD_CONVEYOR_DIR_SWITCH_LEFT, -1, -1
7025 O_CONVEYOR_SWITCH_OFF, FALSE,
7026 EL_BD_CONVEYOR_SWITCH, -1, -1
7029 O_CONVEYOR_SWITCH_ON, FALSE,
7030 EL_BD_CONVEYOR_SWITCH_ACTIVE, -1, -1
7033 O_MAGIC_WALL_ACTIVE, FALSE,
7034 EL_BD_MAGIC_WALL_ACTIVE, -1, -1
7037 O_REPLICATOR_ACTIVE, FALSE,
7038 EL_BD_REPLICATOR_ACTIVE, -1, -1
7041 O_CONVEYOR_LEFT_ACTIVE, FALSE,
7042 EL_BD_CONVEYOR_LEFT_ACTIVE, -1, -1
7045 O_CONVEYOR_RIGHT_ACTIVE, FALSE,
7046 EL_BD_CONVEYOR_RIGHT_ACTIVE, -1, -1
7049 O_BITER_SWITCH_1, FALSE,
7050 EL_BD_BITER_SWITCH_1, -1, -1
7053 O_BITER_SWITCH_2, FALSE,
7054 EL_BD_BITER_SWITCH_2, -1, -1
7057 O_BITER_SWITCH_3, FALSE,
7058 EL_BD_BITER_SWITCH_3, -1, -1
7061 O_BITER_SWITCH_4, FALSE,
7062 EL_BD_BITER_SWITCH_4, -1, -1
7071 int map_element_RND_to_BD(int element_rnd)
7073 static unsigned short mapping_RND_to_BD[NUM_FILE_ELEMENTS];
7074 static boolean mapping_initialized = FALSE;
7076 if (!mapping_initialized)
7080 // return "O_UNKNOWN" for all undefined elements in mapping array
7081 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7082 mapping_RND_to_BD[i] = O_UNKNOWN;
7084 for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
7085 if (bd_object_mapping_list[i].is_rnd_to_bd_mapping)
7086 mapping_RND_to_BD[bd_object_mapping_list[i].element_rnd] =
7087 bd_object_mapping_list[i].element_bd;
7089 mapping_initialized = TRUE;
7092 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7094 Warn("invalid RND element %d", element_rnd);
7099 return mapping_RND_to_BD[element_rnd];
7102 int map_element_BD_to_RND(int element_bd)
7104 static unsigned short mapping_BD_to_RND[O_MAX_ALL];
7105 static boolean mapping_initialized = FALSE;
7107 if (!mapping_initialized)
7111 // return "EL_UNKNOWN" for all undefined elements in mapping array
7112 for (i = 0; i < O_MAX_ALL; i++)
7113 mapping_BD_to_RND[i] = EL_UNKNOWN;
7115 for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
7116 mapping_BD_to_RND[bd_object_mapping_list[i].element_bd] =
7117 bd_object_mapping_list[i].element_rnd;
7119 mapping_initialized = TRUE;
7122 if (element_bd < 0 || element_bd >= O_MAX_ALL)
7124 Warn("invalid BD element %d", element_bd);
7129 return mapping_BD_to_RND[element_bd];
7132 static struct Mapping_EM_to_RND_object
7135 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
7136 boolean is_backside; // backside of moving element
7142 em_object_mapping_list[GAME_TILE_MAX + 1] =
7145 Zborder, FALSE, FALSE,
7149 Zplayer, FALSE, FALSE,
7158 Ztank, FALSE, FALSE,
7162 Zeater, FALSE, FALSE,
7166 Zdynamite, FALSE, FALSE,
7170 Zboom, FALSE, FALSE,
7175 Xchain, FALSE, FALSE,
7176 EL_DEFAULT, ACTION_EXPLODING, -1
7179 Xboom_bug, FALSE, FALSE,
7180 EL_BUG, ACTION_EXPLODING, -1
7183 Xboom_tank, FALSE, FALSE,
7184 EL_SPACESHIP, ACTION_EXPLODING, -1
7187 Xboom_android, FALSE, FALSE,
7188 EL_EMC_ANDROID, ACTION_OTHER, -1
7191 Xboom_1, FALSE, FALSE,
7192 EL_DEFAULT, ACTION_EXPLODING, -1
7195 Xboom_2, FALSE, FALSE,
7196 EL_DEFAULT, ACTION_EXPLODING, -1
7200 Xblank, TRUE, FALSE,
7205 Xsplash_e, FALSE, FALSE,
7206 EL_ACID_SPLASH_RIGHT, -1, -1
7209 Xsplash_w, FALSE, FALSE,
7210 EL_ACID_SPLASH_LEFT, -1, -1
7214 Xplant, TRUE, FALSE,
7215 EL_EMC_PLANT, -1, -1
7218 Yplant, FALSE, FALSE,
7219 EL_EMC_PLANT, -1, -1
7223 Xacid_1, TRUE, FALSE,
7227 Xacid_2, FALSE, FALSE,
7231 Xacid_3, FALSE, FALSE,
7235 Xacid_4, FALSE, FALSE,
7239 Xacid_5, FALSE, FALSE,
7243 Xacid_6, FALSE, FALSE,
7247 Xacid_7, FALSE, FALSE,
7251 Xacid_8, FALSE, FALSE,
7256 Xfake_acid_1, TRUE, FALSE,
7257 EL_EMC_FAKE_ACID, -1, -1
7260 Xfake_acid_2, FALSE, FALSE,
7261 EL_EMC_FAKE_ACID, -1, -1
7264 Xfake_acid_3, FALSE, FALSE,
7265 EL_EMC_FAKE_ACID, -1, -1
7268 Xfake_acid_4, FALSE, FALSE,
7269 EL_EMC_FAKE_ACID, -1, -1
7272 Xfake_acid_5, FALSE, FALSE,
7273 EL_EMC_FAKE_ACID, -1, -1
7276 Xfake_acid_6, FALSE, FALSE,
7277 EL_EMC_FAKE_ACID, -1, -1
7280 Xfake_acid_7, FALSE, FALSE,
7281 EL_EMC_FAKE_ACID, -1, -1
7284 Xfake_acid_8, FALSE, FALSE,
7285 EL_EMC_FAKE_ACID, -1, -1
7289 Xfake_acid_1_player, FALSE, FALSE,
7290 EL_EMC_FAKE_ACID, -1, -1
7293 Xfake_acid_2_player, FALSE, FALSE,
7294 EL_EMC_FAKE_ACID, -1, -1
7297 Xfake_acid_3_player, FALSE, FALSE,
7298 EL_EMC_FAKE_ACID, -1, -1
7301 Xfake_acid_4_player, FALSE, FALSE,
7302 EL_EMC_FAKE_ACID, -1, -1
7305 Xfake_acid_5_player, FALSE, FALSE,
7306 EL_EMC_FAKE_ACID, -1, -1
7309 Xfake_acid_6_player, FALSE, FALSE,
7310 EL_EMC_FAKE_ACID, -1, -1
7313 Xfake_acid_7_player, FALSE, FALSE,
7314 EL_EMC_FAKE_ACID, -1, -1
7317 Xfake_acid_8_player, FALSE, FALSE,
7318 EL_EMC_FAKE_ACID, -1, -1
7322 Xgrass, TRUE, FALSE,
7323 EL_EMC_GRASS, -1, -1
7326 Ygrass_nB, FALSE, FALSE,
7327 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
7330 Ygrass_eB, FALSE, FALSE,
7331 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
7334 Ygrass_sB, FALSE, FALSE,
7335 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
7338 Ygrass_wB, FALSE, FALSE,
7339 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
7347 Ydirt_nB, FALSE, FALSE,
7348 EL_SAND, ACTION_DIGGING, MV_BIT_UP
7351 Ydirt_eB, FALSE, FALSE,
7352 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
7355 Ydirt_sB, FALSE, FALSE,
7356 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
7359 Ydirt_wB, FALSE, FALSE,
7360 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
7364 Xandroid, TRUE, FALSE,
7365 EL_EMC_ANDROID, ACTION_ACTIVE, -1
7368 Xandroid_1_n, FALSE, FALSE,
7369 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
7372 Xandroid_2_n, FALSE, FALSE,
7373 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
7376 Xandroid_1_e, FALSE, FALSE,
7377 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
7380 Xandroid_2_e, FALSE, FALSE,
7381 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
7384 Xandroid_1_w, FALSE, FALSE,
7385 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
7388 Xandroid_2_w, FALSE, FALSE,
7389 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
7392 Xandroid_1_s, FALSE, FALSE,
7393 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
7396 Xandroid_2_s, FALSE, FALSE,
7397 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
7400 Yandroid_n, FALSE, FALSE,
7401 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
7404 Yandroid_nB, FALSE, TRUE,
7405 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
7408 Yandroid_ne, FALSE, FALSE,
7409 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
7412 Yandroid_neB, FALSE, TRUE,
7413 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
7416 Yandroid_e, FALSE, FALSE,
7417 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
7420 Yandroid_eB, FALSE, TRUE,
7421 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
7424 Yandroid_se, FALSE, FALSE,
7425 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
7428 Yandroid_seB, FALSE, TRUE,
7429 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
7432 Yandroid_s, FALSE, FALSE,
7433 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
7436 Yandroid_sB, FALSE, TRUE,
7437 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
7440 Yandroid_sw, FALSE, FALSE,
7441 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
7444 Yandroid_swB, FALSE, TRUE,
7445 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
7448 Yandroid_w, FALSE, FALSE,
7449 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
7452 Yandroid_wB, FALSE, TRUE,
7453 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
7456 Yandroid_nw, FALSE, FALSE,
7457 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
7460 Yandroid_nwB, FALSE, TRUE,
7461 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
7465 Xeater_n, TRUE, FALSE,
7466 EL_YAMYAM_UP, -1, -1
7469 Xeater_e, TRUE, FALSE,
7470 EL_YAMYAM_RIGHT, -1, -1
7473 Xeater_w, TRUE, FALSE,
7474 EL_YAMYAM_LEFT, -1, -1
7477 Xeater_s, TRUE, FALSE,
7478 EL_YAMYAM_DOWN, -1, -1
7481 Yeater_n, FALSE, FALSE,
7482 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
7485 Yeater_nB, FALSE, TRUE,
7486 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
7489 Yeater_e, FALSE, FALSE,
7490 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
7493 Yeater_eB, FALSE, TRUE,
7494 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
7497 Yeater_s, FALSE, FALSE,
7498 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
7501 Yeater_sB, FALSE, TRUE,
7502 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
7505 Yeater_w, FALSE, FALSE,
7506 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
7509 Yeater_wB, FALSE, TRUE,
7510 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
7513 Yeater_stone, FALSE, FALSE,
7514 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
7517 Yeater_spring, FALSE, FALSE,
7518 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
7522 Xalien, TRUE, FALSE,
7526 Xalien_pause, FALSE, FALSE,
7530 Yalien_n, FALSE, FALSE,
7531 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
7534 Yalien_nB, FALSE, TRUE,
7535 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
7538 Yalien_e, FALSE, FALSE,
7539 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
7542 Yalien_eB, FALSE, TRUE,
7543 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
7546 Yalien_s, FALSE, FALSE,
7547 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
7550 Yalien_sB, FALSE, TRUE,
7551 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
7554 Yalien_w, FALSE, FALSE,
7555 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
7558 Yalien_wB, FALSE, TRUE,
7559 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
7562 Yalien_stone, FALSE, FALSE,
7563 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
7566 Yalien_spring, FALSE, FALSE,
7567 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
7571 Xbug_1_n, TRUE, FALSE,
7575 Xbug_1_e, TRUE, FALSE,
7576 EL_BUG_RIGHT, -1, -1
7579 Xbug_1_s, TRUE, FALSE,
7583 Xbug_1_w, TRUE, FALSE,
7587 Xbug_2_n, FALSE, FALSE,
7591 Xbug_2_e, FALSE, FALSE,
7592 EL_BUG_RIGHT, -1, -1
7595 Xbug_2_s, FALSE, FALSE,
7599 Xbug_2_w, FALSE, FALSE,
7603 Ybug_n, FALSE, FALSE,
7604 EL_BUG, ACTION_MOVING, MV_BIT_UP
7607 Ybug_nB, FALSE, TRUE,
7608 EL_BUG, ACTION_MOVING, MV_BIT_UP
7611 Ybug_e, FALSE, FALSE,
7612 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
7615 Ybug_eB, FALSE, TRUE,
7616 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
7619 Ybug_s, FALSE, FALSE,
7620 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
7623 Ybug_sB, FALSE, TRUE,
7624 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
7627 Ybug_w, FALSE, FALSE,
7628 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
7631 Ybug_wB, FALSE, TRUE,
7632 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
7635 Ybug_w_n, FALSE, FALSE,
7636 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
7639 Ybug_n_e, FALSE, FALSE,
7640 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
7643 Ybug_e_s, FALSE, FALSE,
7644 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
7647 Ybug_s_w, FALSE, FALSE,
7648 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
7651 Ybug_e_n, FALSE, FALSE,
7652 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
7655 Ybug_s_e, FALSE, FALSE,
7656 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
7659 Ybug_w_s, FALSE, FALSE,
7660 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
7663 Ybug_n_w, FALSE, FALSE,
7664 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
7667 Ybug_stone, FALSE, FALSE,
7668 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
7671 Ybug_spring, FALSE, FALSE,
7672 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
7676 Xtank_1_n, TRUE, FALSE,
7677 EL_SPACESHIP_UP, -1, -1
7680 Xtank_1_e, TRUE, FALSE,
7681 EL_SPACESHIP_RIGHT, -1, -1
7684 Xtank_1_s, TRUE, FALSE,
7685 EL_SPACESHIP_DOWN, -1, -1
7688 Xtank_1_w, TRUE, FALSE,
7689 EL_SPACESHIP_LEFT, -1, -1
7692 Xtank_2_n, FALSE, FALSE,
7693 EL_SPACESHIP_UP, -1, -1
7696 Xtank_2_e, FALSE, FALSE,
7697 EL_SPACESHIP_RIGHT, -1, -1
7700 Xtank_2_s, FALSE, FALSE,
7701 EL_SPACESHIP_DOWN, -1, -1
7704 Xtank_2_w, FALSE, FALSE,
7705 EL_SPACESHIP_LEFT, -1, -1
7708 Ytank_n, FALSE, FALSE,
7709 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
7712 Ytank_nB, FALSE, TRUE,
7713 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
7716 Ytank_e, FALSE, FALSE,
7717 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
7720 Ytank_eB, FALSE, TRUE,
7721 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
7724 Ytank_s, FALSE, FALSE,
7725 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
7728 Ytank_sB, FALSE, TRUE,
7729 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
7732 Ytank_w, FALSE, FALSE,
7733 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
7736 Ytank_wB, FALSE, TRUE,
7737 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
7740 Ytank_w_n, FALSE, FALSE,
7741 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
7744 Ytank_n_e, FALSE, FALSE,
7745 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
7748 Ytank_e_s, FALSE, FALSE,
7749 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
7752 Ytank_s_w, FALSE, FALSE,
7753 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
7756 Ytank_e_n, FALSE, FALSE,
7757 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
7760 Ytank_s_e, FALSE, FALSE,
7761 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
7764 Ytank_w_s, FALSE, FALSE,
7765 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
7768 Ytank_n_w, FALSE, FALSE,
7769 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
7772 Ytank_stone, FALSE, FALSE,
7773 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
7776 Ytank_spring, FALSE, FALSE,
7777 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
7781 Xemerald, TRUE, FALSE,
7785 Xemerald_pause, FALSE, FALSE,
7789 Xemerald_fall, FALSE, FALSE,
7793 Xemerald_shine, FALSE, FALSE,
7794 EL_EMERALD, ACTION_TWINKLING, -1
7797 Yemerald_s, FALSE, FALSE,
7798 EL_EMERALD, ACTION_FALLING, -1
7801 Yemerald_sB, FALSE, TRUE,
7802 EL_EMERALD, ACTION_FALLING, -1
7805 Yemerald_e, FALSE, FALSE,
7806 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
7809 Yemerald_eB, FALSE, TRUE,
7810 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
7813 Yemerald_w, FALSE, FALSE,
7814 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
7817 Yemerald_wB, FALSE, TRUE,
7818 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
7821 Yemerald_blank, FALSE, FALSE,
7822 EL_EMERALD, ACTION_COLLECTING, -1
7826 Xdiamond, TRUE, FALSE,
7830 Xdiamond_pause, FALSE, FALSE,
7834 Xdiamond_fall, FALSE, FALSE,
7838 Xdiamond_shine, FALSE, FALSE,
7839 EL_DIAMOND, ACTION_TWINKLING, -1
7842 Ydiamond_s, FALSE, FALSE,
7843 EL_DIAMOND, ACTION_FALLING, -1
7846 Ydiamond_sB, FALSE, TRUE,
7847 EL_DIAMOND, ACTION_FALLING, -1
7850 Ydiamond_e, FALSE, FALSE,
7851 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
7854 Ydiamond_eB, FALSE, TRUE,
7855 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
7858 Ydiamond_w, FALSE, FALSE,
7859 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
7862 Ydiamond_wB, FALSE, TRUE,
7863 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
7866 Ydiamond_blank, FALSE, FALSE,
7867 EL_DIAMOND, ACTION_COLLECTING, -1
7870 Ydiamond_stone, FALSE, FALSE,
7871 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
7875 Xstone, TRUE, FALSE,
7879 Xstone_pause, FALSE, FALSE,
7883 Xstone_fall, FALSE, FALSE,
7887 Ystone_s, FALSE, FALSE,
7888 EL_ROCK, ACTION_FALLING, -1
7891 Ystone_sB, FALSE, TRUE,
7892 EL_ROCK, ACTION_FALLING, -1
7895 Ystone_e, FALSE, FALSE,
7896 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
7899 Ystone_eB, FALSE, TRUE,
7900 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
7903 Ystone_w, FALSE, FALSE,
7904 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
7907 Ystone_wB, FALSE, TRUE,
7908 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
7916 Xbomb_pause, FALSE, FALSE,
7920 Xbomb_fall, FALSE, FALSE,
7924 Ybomb_s, FALSE, FALSE,
7925 EL_BOMB, ACTION_FALLING, -1
7928 Ybomb_sB, FALSE, TRUE,
7929 EL_BOMB, ACTION_FALLING, -1
7932 Ybomb_e, FALSE, FALSE,
7933 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
7936 Ybomb_eB, FALSE, TRUE,
7937 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
7940 Ybomb_w, FALSE, FALSE,
7941 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
7944 Ybomb_wB, FALSE, TRUE,
7945 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
7948 Ybomb_blank, FALSE, FALSE,
7949 EL_BOMB, ACTION_ACTIVATING, -1
7957 Xnut_pause, FALSE, FALSE,
7961 Xnut_fall, FALSE, FALSE,
7965 Ynut_s, FALSE, FALSE,
7966 EL_NUT, ACTION_FALLING, -1
7969 Ynut_sB, FALSE, TRUE,
7970 EL_NUT, ACTION_FALLING, -1
7973 Ynut_e, FALSE, FALSE,
7974 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
7977 Ynut_eB, FALSE, TRUE,
7978 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
7981 Ynut_w, FALSE, FALSE,
7982 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
7985 Ynut_wB, FALSE, TRUE,
7986 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
7989 Ynut_stone, FALSE, FALSE,
7990 EL_NUT, ACTION_BREAKING, -1
7994 Xspring, TRUE, FALSE,
7998 Xspring_pause, FALSE, FALSE,
8002 Xspring_e, TRUE, FALSE,
8003 EL_SPRING_RIGHT, -1, -1
8006 Xspring_w, TRUE, FALSE,
8007 EL_SPRING_LEFT, -1, -1
8010 Xspring_fall, FALSE, FALSE,
8014 Yspring_s, FALSE, FALSE,
8015 EL_SPRING, ACTION_FALLING, -1
8018 Yspring_sB, FALSE, TRUE,
8019 EL_SPRING, ACTION_FALLING, -1
8022 Yspring_e, FALSE, FALSE,
8023 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
8026 Yspring_eB, FALSE, TRUE,
8027 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
8030 Yspring_w, FALSE, FALSE,
8031 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
8034 Yspring_wB, FALSE, TRUE,
8035 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
8038 Yspring_alien_e, FALSE, FALSE,
8039 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
8042 Yspring_alien_eB, FALSE, TRUE,
8043 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
8046 Yspring_alien_w, FALSE, FALSE,
8047 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
8050 Yspring_alien_wB, FALSE, TRUE,
8051 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
8055 Xpush_emerald_e, FALSE, FALSE,
8056 EL_EMERALD, -1, MV_BIT_RIGHT
8059 Xpush_emerald_w, FALSE, FALSE,
8060 EL_EMERALD, -1, MV_BIT_LEFT
8063 Xpush_diamond_e, FALSE, FALSE,
8064 EL_DIAMOND, -1, MV_BIT_RIGHT
8067 Xpush_diamond_w, FALSE, FALSE,
8068 EL_DIAMOND, -1, MV_BIT_LEFT
8071 Xpush_stone_e, FALSE, FALSE,
8072 EL_ROCK, -1, MV_BIT_RIGHT
8075 Xpush_stone_w, FALSE, FALSE,
8076 EL_ROCK, -1, MV_BIT_LEFT
8079 Xpush_bomb_e, FALSE, FALSE,
8080 EL_BOMB, -1, MV_BIT_RIGHT
8083 Xpush_bomb_w, FALSE, FALSE,
8084 EL_BOMB, -1, MV_BIT_LEFT
8087 Xpush_nut_e, FALSE, FALSE,
8088 EL_NUT, -1, MV_BIT_RIGHT
8091 Xpush_nut_w, FALSE, FALSE,
8092 EL_NUT, -1, MV_BIT_LEFT
8095 Xpush_spring_e, FALSE, FALSE,
8096 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
8099 Xpush_spring_w, FALSE, FALSE,
8100 EL_SPRING_LEFT, -1, MV_BIT_LEFT
8104 Xdynamite, TRUE, FALSE,
8105 EL_EM_DYNAMITE, -1, -1
8108 Ydynamite_blank, FALSE, FALSE,
8109 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
8112 Xdynamite_1, TRUE, FALSE,
8113 EL_EM_DYNAMITE_ACTIVE, -1, -1
8116 Xdynamite_2, FALSE, FALSE,
8117 EL_EM_DYNAMITE_ACTIVE, -1, -1
8120 Xdynamite_3, FALSE, FALSE,
8121 EL_EM_DYNAMITE_ACTIVE, -1, -1
8124 Xdynamite_4, FALSE, FALSE,
8125 EL_EM_DYNAMITE_ACTIVE, -1, -1
8129 Xkey_1, TRUE, FALSE,
8133 Xkey_2, TRUE, FALSE,
8137 Xkey_3, TRUE, FALSE,
8141 Xkey_4, TRUE, FALSE,
8145 Xkey_5, TRUE, FALSE,
8146 EL_EMC_KEY_5, -1, -1
8149 Xkey_6, TRUE, FALSE,
8150 EL_EMC_KEY_6, -1, -1
8153 Xkey_7, TRUE, FALSE,
8154 EL_EMC_KEY_7, -1, -1
8157 Xkey_8, TRUE, FALSE,
8158 EL_EMC_KEY_8, -1, -1
8162 Xdoor_1, TRUE, FALSE,
8163 EL_EM_GATE_1, -1, -1
8166 Xdoor_2, TRUE, FALSE,
8167 EL_EM_GATE_2, -1, -1
8170 Xdoor_3, TRUE, FALSE,
8171 EL_EM_GATE_3, -1, -1
8174 Xdoor_4, TRUE, FALSE,
8175 EL_EM_GATE_4, -1, -1
8178 Xdoor_5, TRUE, FALSE,
8179 EL_EMC_GATE_5, -1, -1
8182 Xdoor_6, TRUE, FALSE,
8183 EL_EMC_GATE_6, -1, -1
8186 Xdoor_7, TRUE, FALSE,
8187 EL_EMC_GATE_7, -1, -1
8190 Xdoor_8, TRUE, FALSE,
8191 EL_EMC_GATE_8, -1, -1
8195 Xfake_door_1, TRUE, FALSE,
8196 EL_EM_GATE_1_GRAY, -1, -1
8199 Xfake_door_2, TRUE, FALSE,
8200 EL_EM_GATE_2_GRAY, -1, -1
8203 Xfake_door_3, TRUE, FALSE,
8204 EL_EM_GATE_3_GRAY, -1, -1
8207 Xfake_door_4, TRUE, FALSE,
8208 EL_EM_GATE_4_GRAY, -1, -1
8211 Xfake_door_5, TRUE, FALSE,
8212 EL_EMC_GATE_5_GRAY, -1, -1
8215 Xfake_door_6, TRUE, FALSE,
8216 EL_EMC_GATE_6_GRAY, -1, -1
8219 Xfake_door_7, TRUE, FALSE,
8220 EL_EMC_GATE_7_GRAY, -1, -1
8223 Xfake_door_8, TRUE, FALSE,
8224 EL_EMC_GATE_8_GRAY, -1, -1
8228 Xballoon, TRUE, FALSE,
8232 Yballoon_n, FALSE, FALSE,
8233 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
8236 Yballoon_nB, FALSE, TRUE,
8237 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
8240 Yballoon_e, FALSE, FALSE,
8241 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
8244 Yballoon_eB, FALSE, TRUE,
8245 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
8248 Yballoon_s, FALSE, FALSE,
8249 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
8252 Yballoon_sB, FALSE, TRUE,
8253 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
8256 Yballoon_w, FALSE, FALSE,
8257 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
8260 Yballoon_wB, FALSE, TRUE,
8261 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
8265 Xball_1, TRUE, FALSE,
8266 EL_EMC_MAGIC_BALL, -1, -1
8269 Yball_1, FALSE, FALSE,
8270 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
8273 Xball_2, FALSE, FALSE,
8274 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
8277 Yball_2, FALSE, FALSE,
8278 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
8281 Yball_blank, FALSE, FALSE,
8282 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
8286 Xamoeba_1, TRUE, FALSE,
8287 EL_AMOEBA_DRY, ACTION_OTHER, -1
8290 Xamoeba_2, FALSE, FALSE,
8291 EL_AMOEBA_DRY, ACTION_OTHER, -1
8294 Xamoeba_3, FALSE, FALSE,
8295 EL_AMOEBA_DRY, ACTION_OTHER, -1
8298 Xamoeba_4, FALSE, FALSE,
8299 EL_AMOEBA_DRY, ACTION_OTHER, -1
8302 Xamoeba_5, TRUE, FALSE,
8303 EL_AMOEBA_WET, ACTION_OTHER, -1
8306 Xamoeba_6, FALSE, FALSE,
8307 EL_AMOEBA_WET, ACTION_OTHER, -1
8310 Xamoeba_7, FALSE, FALSE,
8311 EL_AMOEBA_WET, ACTION_OTHER, -1
8314 Xamoeba_8, FALSE, FALSE,
8315 EL_AMOEBA_WET, ACTION_OTHER, -1
8320 EL_AMOEBA_DROP, ACTION_GROWING, -1
8323 Xdrip_fall, FALSE, FALSE,
8324 EL_AMOEBA_DROP, -1, -1
8327 Xdrip_stretch, FALSE, FALSE,
8328 EL_AMOEBA_DROP, ACTION_FALLING, -1
8331 Xdrip_stretchB, FALSE, TRUE,
8332 EL_AMOEBA_DROP, ACTION_FALLING, -1
8335 Ydrip_1_s, FALSE, FALSE,
8336 EL_AMOEBA_DROP, ACTION_FALLING, -1
8339 Ydrip_1_sB, FALSE, TRUE,
8340 EL_AMOEBA_DROP, ACTION_FALLING, -1
8343 Ydrip_2_s, FALSE, FALSE,
8344 EL_AMOEBA_DROP, ACTION_FALLING, -1
8347 Ydrip_2_sB, FALSE, TRUE,
8348 EL_AMOEBA_DROP, ACTION_FALLING, -1
8352 Xwonderwall, TRUE, FALSE,
8353 EL_MAGIC_WALL, -1, -1
8356 Ywonderwall, FALSE, FALSE,
8357 EL_MAGIC_WALL, ACTION_ACTIVE, -1
8361 Xwheel, TRUE, FALSE,
8362 EL_ROBOT_WHEEL, -1, -1
8365 Ywheel, FALSE, FALSE,
8366 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
8370 Xswitch, TRUE, FALSE,
8371 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
8374 Yswitch, FALSE, FALSE,
8375 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
8379 Xbumper, TRUE, FALSE,
8380 EL_EMC_SPRING_BUMPER, -1, -1
8383 Ybumper, FALSE, FALSE,
8384 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
8388 Xacid_nw, TRUE, FALSE,
8389 EL_ACID_POOL_TOPLEFT, -1, -1
8392 Xacid_ne, TRUE, FALSE,
8393 EL_ACID_POOL_TOPRIGHT, -1, -1
8396 Xacid_sw, TRUE, FALSE,
8397 EL_ACID_POOL_BOTTOMLEFT, -1, -1
8400 Xacid_s, TRUE, FALSE,
8401 EL_ACID_POOL_BOTTOM, -1, -1
8404 Xacid_se, TRUE, FALSE,
8405 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
8409 Xfake_blank, TRUE, FALSE,
8410 EL_INVISIBLE_WALL, -1, -1
8413 Yfake_blank, FALSE, FALSE,
8414 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
8418 Xfake_grass, TRUE, FALSE,
8419 EL_EMC_FAKE_GRASS, -1, -1
8422 Yfake_grass, FALSE, FALSE,
8423 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
8427 Xfake_amoeba, TRUE, FALSE,
8428 EL_EMC_DRIPPER, -1, -1
8431 Yfake_amoeba, FALSE, FALSE,
8432 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
8436 Xlenses, TRUE, FALSE,
8437 EL_EMC_LENSES, -1, -1
8441 Xmagnify, TRUE, FALSE,
8442 EL_EMC_MAGNIFIER, -1, -1
8447 EL_QUICKSAND_EMPTY, -1, -1
8450 Xsand_stone, TRUE, FALSE,
8451 EL_QUICKSAND_FULL, -1, -1
8454 Xsand_stonein_1, FALSE, TRUE,
8455 EL_ROCK, ACTION_FILLING, -1
8458 Xsand_stonein_2, FALSE, TRUE,
8459 EL_ROCK, ACTION_FILLING, -1
8462 Xsand_stonein_3, FALSE, TRUE,
8463 EL_ROCK, ACTION_FILLING, -1
8466 Xsand_stonein_4, FALSE, TRUE,
8467 EL_ROCK, ACTION_FILLING, -1
8470 Xsand_sandstone_1, FALSE, FALSE,
8471 EL_QUICKSAND_FILLING, -1, -1
8474 Xsand_sandstone_2, FALSE, FALSE,
8475 EL_QUICKSAND_FILLING, -1, -1
8478 Xsand_sandstone_3, FALSE, FALSE,
8479 EL_QUICKSAND_FILLING, -1, -1
8482 Xsand_sandstone_4, FALSE, FALSE,
8483 EL_QUICKSAND_FILLING, -1, -1
8486 Xsand_stonesand_1, FALSE, FALSE,
8487 EL_QUICKSAND_EMPTYING, -1, -1
8490 Xsand_stonesand_2, FALSE, FALSE,
8491 EL_QUICKSAND_EMPTYING, -1, -1
8494 Xsand_stonesand_3, FALSE, FALSE,
8495 EL_QUICKSAND_EMPTYING, -1, -1
8498 Xsand_stonesand_4, FALSE, FALSE,
8499 EL_QUICKSAND_EMPTYING, -1, -1
8502 Xsand_stoneout_1, FALSE, FALSE,
8503 EL_ROCK, ACTION_EMPTYING, -1
8506 Xsand_stoneout_2, FALSE, FALSE,
8507 EL_ROCK, ACTION_EMPTYING, -1
8510 Xsand_stonesand_quickout_1, FALSE, FALSE,
8511 EL_QUICKSAND_EMPTYING, -1, -1
8514 Xsand_stonesand_quickout_2, FALSE, FALSE,
8515 EL_QUICKSAND_EMPTYING, -1, -1
8519 Xslide_ns, TRUE, FALSE,
8520 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
8523 Yslide_ns_blank, FALSE, FALSE,
8524 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
8527 Xslide_ew, TRUE, FALSE,
8528 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
8531 Yslide_ew_blank, FALSE, FALSE,
8532 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
8536 Xwind_n, TRUE, FALSE,
8537 EL_BALLOON_SWITCH_UP, -1, -1
8540 Xwind_e, TRUE, FALSE,
8541 EL_BALLOON_SWITCH_RIGHT, -1, -1
8544 Xwind_s, TRUE, FALSE,
8545 EL_BALLOON_SWITCH_DOWN, -1, -1
8548 Xwind_w, TRUE, FALSE,
8549 EL_BALLOON_SWITCH_LEFT, -1, -1
8552 Xwind_any, TRUE, FALSE,
8553 EL_BALLOON_SWITCH_ANY, -1, -1
8556 Xwind_stop, TRUE, FALSE,
8557 EL_BALLOON_SWITCH_NONE, -1, -1
8562 EL_EM_EXIT_CLOSED, -1, -1
8565 Xexit_1, TRUE, FALSE,
8566 EL_EM_EXIT_OPEN, -1, -1
8569 Xexit_2, FALSE, FALSE,
8570 EL_EM_EXIT_OPEN, -1, -1
8573 Xexit_3, FALSE, FALSE,
8574 EL_EM_EXIT_OPEN, -1, -1
8578 Xpause, FALSE, FALSE,
8583 Xwall_1, TRUE, FALSE,
8587 Xwall_2, TRUE, FALSE,
8588 EL_EMC_WALL_14, -1, -1
8591 Xwall_3, TRUE, FALSE,
8592 EL_EMC_WALL_15, -1, -1
8595 Xwall_4, TRUE, FALSE,
8596 EL_EMC_WALL_16, -1, -1
8600 Xroundwall_1, TRUE, FALSE,
8601 EL_WALL_SLIPPERY, -1, -1
8604 Xroundwall_2, TRUE, FALSE,
8605 EL_EMC_WALL_SLIPPERY_2, -1, -1
8608 Xroundwall_3, TRUE, FALSE,
8609 EL_EMC_WALL_SLIPPERY_3, -1, -1
8612 Xroundwall_4, TRUE, FALSE,
8613 EL_EMC_WALL_SLIPPERY_4, -1, -1
8617 Xsteel_1, TRUE, FALSE,
8618 EL_STEELWALL, -1, -1
8621 Xsteel_2, TRUE, FALSE,
8622 EL_EMC_STEELWALL_2, -1, -1
8625 Xsteel_3, TRUE, FALSE,
8626 EL_EMC_STEELWALL_3, -1, -1
8629 Xsteel_4, TRUE, FALSE,
8630 EL_EMC_STEELWALL_4, -1, -1
8634 Xdecor_1, TRUE, FALSE,
8635 EL_EMC_WALL_8, -1, -1
8638 Xdecor_2, TRUE, FALSE,
8639 EL_EMC_WALL_6, -1, -1
8642 Xdecor_3, TRUE, FALSE,
8643 EL_EMC_WALL_4, -1, -1
8646 Xdecor_4, TRUE, FALSE,
8647 EL_EMC_WALL_7, -1, -1
8650 Xdecor_5, TRUE, FALSE,
8651 EL_EMC_WALL_5, -1, -1
8654 Xdecor_6, TRUE, FALSE,
8655 EL_EMC_WALL_9, -1, -1
8658 Xdecor_7, TRUE, FALSE,
8659 EL_EMC_WALL_10, -1, -1
8662 Xdecor_8, TRUE, FALSE,
8663 EL_EMC_WALL_1, -1, -1
8666 Xdecor_9, TRUE, FALSE,
8667 EL_EMC_WALL_2, -1, -1
8670 Xdecor_10, TRUE, FALSE,
8671 EL_EMC_WALL_3, -1, -1
8674 Xdecor_11, TRUE, FALSE,
8675 EL_EMC_WALL_11, -1, -1
8678 Xdecor_12, TRUE, FALSE,
8679 EL_EMC_WALL_12, -1, -1
8683 Xalpha_0, TRUE, FALSE,
8684 EL_CHAR('0'), -1, -1
8687 Xalpha_1, TRUE, FALSE,
8688 EL_CHAR('1'), -1, -1
8691 Xalpha_2, TRUE, FALSE,
8692 EL_CHAR('2'), -1, -1
8695 Xalpha_3, TRUE, FALSE,
8696 EL_CHAR('3'), -1, -1
8699 Xalpha_4, TRUE, FALSE,
8700 EL_CHAR('4'), -1, -1
8703 Xalpha_5, TRUE, FALSE,
8704 EL_CHAR('5'), -1, -1
8707 Xalpha_6, TRUE, FALSE,
8708 EL_CHAR('6'), -1, -1
8711 Xalpha_7, TRUE, FALSE,
8712 EL_CHAR('7'), -1, -1
8715 Xalpha_8, TRUE, FALSE,
8716 EL_CHAR('8'), -1, -1
8719 Xalpha_9, TRUE, FALSE,
8720 EL_CHAR('9'), -1, -1
8723 Xalpha_excla, TRUE, FALSE,
8724 EL_CHAR('!'), -1, -1
8727 Xalpha_apost, TRUE, FALSE,
8728 EL_CHAR('\''), -1, -1
8731 Xalpha_comma, TRUE, FALSE,
8732 EL_CHAR(','), -1, -1
8735 Xalpha_minus, TRUE, FALSE,
8736 EL_CHAR('-'), -1, -1
8739 Xalpha_perio, TRUE, FALSE,
8740 EL_CHAR('.'), -1, -1
8743 Xalpha_colon, TRUE, FALSE,
8744 EL_CHAR(':'), -1, -1
8747 Xalpha_quest, TRUE, FALSE,
8748 EL_CHAR('?'), -1, -1
8751 Xalpha_a, TRUE, FALSE,
8752 EL_CHAR('A'), -1, -1
8755 Xalpha_b, TRUE, FALSE,
8756 EL_CHAR('B'), -1, -1
8759 Xalpha_c, TRUE, FALSE,
8760 EL_CHAR('C'), -1, -1
8763 Xalpha_d, TRUE, FALSE,
8764 EL_CHAR('D'), -1, -1
8767 Xalpha_e, TRUE, FALSE,
8768 EL_CHAR('E'), -1, -1
8771 Xalpha_f, TRUE, FALSE,
8772 EL_CHAR('F'), -1, -1
8775 Xalpha_g, TRUE, FALSE,
8776 EL_CHAR('G'), -1, -1
8779 Xalpha_h, TRUE, FALSE,
8780 EL_CHAR('H'), -1, -1
8783 Xalpha_i, TRUE, FALSE,
8784 EL_CHAR('I'), -1, -1
8787 Xalpha_j, TRUE, FALSE,
8788 EL_CHAR('J'), -1, -1
8791 Xalpha_k, TRUE, FALSE,
8792 EL_CHAR('K'), -1, -1
8795 Xalpha_l, TRUE, FALSE,
8796 EL_CHAR('L'), -1, -1
8799 Xalpha_m, TRUE, FALSE,
8800 EL_CHAR('M'), -1, -1
8803 Xalpha_n, TRUE, FALSE,
8804 EL_CHAR('N'), -1, -1
8807 Xalpha_o, TRUE, FALSE,
8808 EL_CHAR('O'), -1, -1
8811 Xalpha_p, TRUE, FALSE,
8812 EL_CHAR('P'), -1, -1
8815 Xalpha_q, TRUE, FALSE,
8816 EL_CHAR('Q'), -1, -1
8819 Xalpha_r, TRUE, FALSE,
8820 EL_CHAR('R'), -1, -1
8823 Xalpha_s, TRUE, FALSE,
8824 EL_CHAR('S'), -1, -1
8827 Xalpha_t, TRUE, FALSE,
8828 EL_CHAR('T'), -1, -1
8831 Xalpha_u, TRUE, FALSE,
8832 EL_CHAR('U'), -1, -1
8835 Xalpha_v, TRUE, FALSE,
8836 EL_CHAR('V'), -1, -1
8839 Xalpha_w, TRUE, FALSE,
8840 EL_CHAR('W'), -1, -1
8843 Xalpha_x, TRUE, FALSE,
8844 EL_CHAR('X'), -1, -1
8847 Xalpha_y, TRUE, FALSE,
8848 EL_CHAR('Y'), -1, -1
8851 Xalpha_z, TRUE, FALSE,
8852 EL_CHAR('Z'), -1, -1
8855 Xalpha_arrow_e, TRUE, FALSE,
8856 EL_CHAR('>'), -1, -1
8859 Xalpha_arrow_w, TRUE, FALSE,
8860 EL_CHAR('<'), -1, -1
8863 Xalpha_copyr, TRUE, FALSE,
8864 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
8868 Ykey_1_blank, FALSE, FALSE,
8869 EL_EM_KEY_1, ACTION_COLLECTING, -1
8872 Ykey_2_blank, FALSE, FALSE,
8873 EL_EM_KEY_2, ACTION_COLLECTING, -1
8876 Ykey_3_blank, FALSE, FALSE,
8877 EL_EM_KEY_3, ACTION_COLLECTING, -1
8880 Ykey_4_blank, FALSE, FALSE,
8881 EL_EM_KEY_4, ACTION_COLLECTING, -1
8884 Ykey_5_blank, FALSE, FALSE,
8885 EL_EMC_KEY_5, ACTION_COLLECTING, -1
8888 Ykey_6_blank, FALSE, FALSE,
8889 EL_EMC_KEY_6, ACTION_COLLECTING, -1
8892 Ykey_7_blank, FALSE, FALSE,
8893 EL_EMC_KEY_7, ACTION_COLLECTING, -1
8896 Ykey_8_blank, FALSE, FALSE,
8897 EL_EMC_KEY_8, ACTION_COLLECTING, -1
8900 Ylenses_blank, FALSE, FALSE,
8901 EL_EMC_LENSES, ACTION_COLLECTING, -1
8904 Ymagnify_blank, FALSE, FALSE,
8905 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
8908 Ygrass_blank, FALSE, FALSE,
8909 EL_EMC_GRASS, ACTION_SNAPPING, -1
8912 Ydirt_blank, FALSE, FALSE,
8913 EL_SAND, ACTION_SNAPPING, -1
8922 static struct Mapping_EM_to_RND_player
8931 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
8935 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
8939 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
8943 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
8947 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
8951 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
8955 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
8959 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
8963 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
8967 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
8971 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
8975 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
8979 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
8983 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
8987 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
8991 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
8995 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
8999 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
9003 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
9007 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
9011 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
9015 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
9019 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
9023 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
9027 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
9031 EL_PLAYER_1, ACTION_DEFAULT, -1,
9035 EL_PLAYER_2, ACTION_DEFAULT, -1,
9039 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
9043 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
9047 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
9051 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
9055 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
9059 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
9063 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
9067 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
9071 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
9075 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
9079 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
9083 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
9087 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
9091 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
9095 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
9099 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
9103 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
9107 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
9111 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
9115 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
9119 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
9123 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
9127 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
9131 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
9135 EL_PLAYER_3, ACTION_DEFAULT, -1,
9139 EL_PLAYER_4, ACTION_DEFAULT, -1,
9148 int map_element_RND_to_EM_cave(int element_rnd)
9150 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
9151 static boolean mapping_initialized = FALSE;
9153 if (!mapping_initialized)
9157 // return "Xalpha_quest" for all undefined elements in mapping array
9158 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9159 mapping_RND_to_EM[i] = Xalpha_quest;
9161 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
9162 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
9163 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
9164 em_object_mapping_list[i].element_em;
9166 mapping_initialized = TRUE;
9169 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
9171 Warn("invalid RND level element %d", element_rnd);
9176 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
9179 int map_element_EM_to_RND_cave(int element_em_cave)
9181 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
9182 static boolean mapping_initialized = FALSE;
9184 if (!mapping_initialized)
9188 // return "EL_UNKNOWN" for all undefined elements in mapping array
9189 for (i = 0; i < GAME_TILE_MAX; i++)
9190 mapping_EM_to_RND[i] = EL_UNKNOWN;
9192 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
9193 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
9194 em_object_mapping_list[i].element_rnd;
9196 mapping_initialized = TRUE;
9199 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
9201 Warn("invalid EM cave element %d", element_em_cave);
9206 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
9209 int map_element_EM_to_RND_game(int element_em_game)
9211 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
9212 static boolean mapping_initialized = FALSE;
9214 if (!mapping_initialized)
9218 // return "EL_UNKNOWN" for all undefined elements in mapping array
9219 for (i = 0; i < GAME_TILE_MAX; i++)
9220 mapping_EM_to_RND[i] = EL_UNKNOWN;
9222 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
9223 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
9224 em_object_mapping_list[i].element_rnd;
9226 mapping_initialized = TRUE;
9229 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
9231 Warn("invalid EM game element %d", element_em_game);
9236 return mapping_EM_to_RND[element_em_game];
9239 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
9241 struct LevelInfo_EM *level_em = level->native_em_level;
9242 struct CAVE *cav = level_em->cav;
9245 for (i = 0; i < GAME_TILE_MAX; i++)
9246 cav->android_array[i] = Cblank;
9248 for (i = 0; i < level->num_android_clone_elements; i++)
9250 int element_rnd = level->android_clone_element[i];
9251 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
9253 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
9254 if (em_object_mapping_list[j].element_rnd == element_rnd)
9255 cav->android_array[em_object_mapping_list[j].element_em] =
9260 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
9262 struct LevelInfo_EM *level_em = level->native_em_level;
9263 struct CAVE *cav = level_em->cav;
9266 level->num_android_clone_elements = 0;
9268 for (i = 0; i < GAME_TILE_MAX; i++)
9270 int element_em_cave = cav->android_array[i];
9272 boolean element_found = FALSE;
9274 if (element_em_cave == Cblank)
9277 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
9279 for (j = 0; j < level->num_android_clone_elements; j++)
9280 if (level->android_clone_element[j] == element_rnd)
9281 element_found = TRUE;
9285 level->android_clone_element[level->num_android_clone_elements++] =
9288 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
9293 if (level->num_android_clone_elements == 0)
9295 level->num_android_clone_elements = 1;
9296 level->android_clone_element[0] = EL_EMPTY;
9300 int map_direction_RND_to_EM(int direction)
9302 return (direction == MV_UP ? 0 :
9303 direction == MV_RIGHT ? 1 :
9304 direction == MV_DOWN ? 2 :
9305 direction == MV_LEFT ? 3 :
9309 int map_direction_EM_to_RND(int direction)
9311 return (direction == 0 ? MV_UP :
9312 direction == 1 ? MV_RIGHT :
9313 direction == 2 ? MV_DOWN :
9314 direction == 3 ? MV_LEFT :
9318 int map_element_RND_to_SP(int element_rnd)
9320 int element_sp = 0x20; // map unknown elements to yellow "hardware"
9322 if (element_rnd >= EL_SP_START &&
9323 element_rnd <= EL_SP_END)
9324 element_sp = element_rnd - EL_SP_START;
9325 else if (element_rnd == EL_EMPTY_SPACE)
9327 else if (element_rnd == EL_INVISIBLE_WALL)
9333 int map_element_SP_to_RND(int element_sp)
9335 int element_rnd = EL_UNKNOWN;
9337 if (element_sp >= 0x00 &&
9339 element_rnd = EL_SP_START + element_sp;
9340 else if (element_sp == 0x28)
9341 element_rnd = EL_INVISIBLE_WALL;
9346 int map_action_SP_to_RND(int action_sp)
9350 case actActive: return ACTION_ACTIVE;
9351 case actImpact: return ACTION_IMPACT;
9352 case actExploding: return ACTION_EXPLODING;
9353 case actDigging: return ACTION_DIGGING;
9354 case actSnapping: return ACTION_SNAPPING;
9355 case actCollecting: return ACTION_COLLECTING;
9356 case actPassing: return ACTION_PASSING;
9357 case actPushing: return ACTION_PUSHING;
9358 case actDropping: return ACTION_DROPPING;
9360 default: return ACTION_DEFAULT;
9364 int map_element_RND_to_MM(int element_rnd)
9366 return (element_rnd >= EL_MM_START_1 &&
9367 element_rnd <= EL_MM_END_1 ?
9368 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
9370 element_rnd >= EL_MM_START_2 &&
9371 element_rnd <= EL_MM_END_2 ?
9372 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
9374 element_rnd >= EL_MM_START_3 &&
9375 element_rnd <= EL_MM_END_3 ?
9376 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
9378 element_rnd >= EL_CHAR_START &&
9379 element_rnd <= EL_CHAR_END ?
9380 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
9382 element_rnd >= EL_MM_RUNTIME_START &&
9383 element_rnd <= EL_MM_RUNTIME_END ?
9384 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
9386 EL_MM_EMPTY_NATIVE);
9389 int map_element_MM_to_RND(int element_mm)
9391 return (element_mm == EL_MM_EMPTY_NATIVE ||
9392 element_mm == EL_DF_EMPTY_NATIVE ?
9395 element_mm >= EL_MM_START_1_NATIVE &&
9396 element_mm <= EL_MM_END_1_NATIVE ?
9397 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
9399 element_mm >= EL_MM_START_2_NATIVE &&
9400 element_mm <= EL_MM_END_2_NATIVE ?
9401 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
9403 element_mm >= EL_MM_START_3_NATIVE &&
9404 element_mm <= EL_MM_END_3_NATIVE ?
9405 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
9407 element_mm >= EL_MM_CHAR_START_NATIVE &&
9408 element_mm <= EL_MM_CHAR_END_NATIVE ?
9409 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
9411 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
9412 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
9413 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
9418 int map_action_MM_to_RND(int action_mm)
9420 // all MM actions are defined to exactly match their RND counterparts
9424 int map_sound_MM_to_RND(int sound_mm)
9428 case SND_MM_GAME_LEVELTIME_CHARGING:
9429 return SND_GAME_LEVELTIME_CHARGING;
9431 case SND_MM_GAME_HEALTH_CHARGING:
9432 return SND_GAME_HEALTH_CHARGING;
9435 return SND_UNDEFINED;
9439 int map_mm_wall_element(int element)
9441 return (element >= EL_MM_STEEL_WALL_START &&
9442 element <= EL_MM_STEEL_WALL_END ?
9445 element >= EL_MM_WOODEN_WALL_START &&
9446 element <= EL_MM_WOODEN_WALL_END ?
9449 element >= EL_MM_ICE_WALL_START &&
9450 element <= EL_MM_ICE_WALL_END ?
9453 element >= EL_MM_AMOEBA_WALL_START &&
9454 element <= EL_MM_AMOEBA_WALL_END ?
9457 element >= EL_DF_STEEL_WALL_START &&
9458 element <= EL_DF_STEEL_WALL_END ?
9461 element >= EL_DF_WOODEN_WALL_START &&
9462 element <= EL_DF_WOODEN_WALL_END ?
9468 int map_mm_wall_element_editor(int element)
9472 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
9473 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
9474 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
9475 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
9476 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
9477 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
9479 default: return element;
9483 int get_next_element(int element)
9487 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
9488 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
9489 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
9490 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
9491 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
9492 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
9493 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
9494 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
9495 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
9496 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
9497 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
9499 default: return element;
9503 int el2img_mm(int element_mm)
9505 return el2img(map_element_MM_to_RND(element_mm));
9508 int el_act2img_mm(int element_mm, int action)
9510 return el_act2img(map_element_MM_to_RND(element_mm), action);
9513 int el_act_dir2img(int element, int action, int direction)
9515 element = GFX_ELEMENT(element);
9516 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
9518 // direction_graphic[][] == graphic[] for undefined direction graphics
9519 return element_info[element].direction_graphic[action][direction];
9522 static int el_act_dir2crm(int element, int action, int direction)
9524 element = GFX_ELEMENT(element);
9525 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
9527 // direction_graphic[][] == graphic[] for undefined direction graphics
9528 return element_info[element].direction_crumbled[action][direction];
9531 int el_act2img(int element, int action)
9533 element = GFX_ELEMENT(element);
9535 return element_info[element].graphic[action];
9538 int el_act2crm(int element, int action)
9540 element = GFX_ELEMENT(element);
9542 return element_info[element].crumbled[action];
9545 int el_dir2img(int element, int direction)
9547 element = GFX_ELEMENT(element);
9549 return el_act_dir2img(element, ACTION_DEFAULT, direction);
9552 int el2baseimg(int element)
9554 return element_info[element].graphic[ACTION_DEFAULT];
9557 int el2img(int element)
9559 element = GFX_ELEMENT(element);
9561 return element_info[element].graphic[ACTION_DEFAULT];
9564 int el2edimg(int element)
9566 element = GFX_ELEMENT(element);
9568 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
9571 int el2preimg(int element)
9573 element = GFX_ELEMENT(element);
9575 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
9578 int el2panelimg(int element)
9580 element = GFX_ELEMENT(element);
9582 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
9585 int font2baseimg(int font_nr)
9587 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
9590 int getBeltNrFromBeltElement(int element)
9592 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
9593 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
9594 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
9597 int getBeltNrFromBeltActiveElement(int element)
9599 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
9600 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
9601 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
9604 int getBeltNrFromBeltSwitchElement(int element)
9606 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
9607 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
9608 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
9611 int getBeltDirNrFromBeltElement(int element)
9613 static int belt_base_element[4] =
9615 EL_CONVEYOR_BELT_1_LEFT,
9616 EL_CONVEYOR_BELT_2_LEFT,
9617 EL_CONVEYOR_BELT_3_LEFT,
9618 EL_CONVEYOR_BELT_4_LEFT
9621 int belt_nr = getBeltNrFromBeltElement(element);
9622 int belt_dir_nr = element - belt_base_element[belt_nr];
9624 return (belt_dir_nr % 3);
9627 int getBeltDirNrFromBeltSwitchElement(int element)
9629 static int belt_base_element[4] =
9631 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
9632 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
9633 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
9634 EL_CONVEYOR_BELT_4_SWITCH_LEFT
9637 int belt_nr = getBeltNrFromBeltSwitchElement(element);
9638 int belt_dir_nr = element - belt_base_element[belt_nr];
9640 return (belt_dir_nr % 3);
9643 int getBeltDirFromBeltElement(int element)
9645 static int belt_move_dir[3] =
9652 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
9654 return belt_move_dir[belt_dir_nr];
9657 int getBeltDirFromBeltSwitchElement(int element)
9659 static int belt_move_dir[3] =
9666 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
9668 return belt_move_dir[belt_dir_nr];
9671 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
9673 static int belt_base_element[4] =
9675 EL_CONVEYOR_BELT_1_LEFT,
9676 EL_CONVEYOR_BELT_2_LEFT,
9677 EL_CONVEYOR_BELT_3_LEFT,
9678 EL_CONVEYOR_BELT_4_LEFT
9681 return belt_base_element[belt_nr] + belt_dir_nr;
9684 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
9686 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
9688 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
9691 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
9693 static int belt_base_element[4] =
9695 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
9696 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
9697 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
9698 EL_CONVEYOR_BELT_4_SWITCH_LEFT
9701 return belt_base_element[belt_nr] + belt_dir_nr;
9704 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
9706 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
9708 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
9711 boolean swapTiles_EM(boolean is_pre_emc_cave)
9713 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
9716 boolean getTeamMode_EM(void)
9718 return game.team_mode || network_playing;
9721 boolean isActivePlayer_EM(int player_nr)
9723 return stored_player[player_nr].active;
9726 unsigned int InitRND(int seed)
9728 if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
9729 return InitEngineRandom_BD(seed);
9730 else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9731 return InitEngineRandom_EM(seed);
9732 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
9733 return InitEngineRandom_SP(seed);
9734 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
9735 return InitEngineRandom_MM(seed);
9737 return InitEngineRandom_RND(seed);
9740 static struct Mapping_BD_to_RND_object bd_object_mapping[O_MAX_ALL];
9741 static struct Mapping_EM_to_RND_object em_object_mapping[GAME_TILE_MAX];
9742 static struct Mapping_EM_to_RND_player em_player_mapping[MAX_PLAYERS][PLY_MAX];
9744 static int get_effective_element_EM(int tile, int frame_em)
9746 int element = em_object_mapping[tile].element_rnd;
9747 int action = em_object_mapping[tile].action;
9748 boolean is_backside = em_object_mapping[tile].is_backside;
9749 boolean action_removing = (action == ACTION_DIGGING ||
9750 action == ACTION_SNAPPING ||
9751 action == ACTION_COLLECTING);
9759 return (frame_em > 5 ? EL_EMPTY : element);
9765 else // frame_em == 7
9776 case Ydiamond_stone:
9780 case Xdrip_stretchB:
9796 case Ymagnify_blank:
9799 case Xsand_stonein_1:
9800 case Xsand_stonein_2:
9801 case Xsand_stonein_3:
9802 case Xsand_stonein_4:
9806 return (is_backside || action_removing ? EL_EMPTY : element);
9811 static boolean check_linear_animation_EM(int tile)
9815 case Xsand_stonesand_1:
9816 case Xsand_stonesand_quickout_1:
9817 case Xsand_sandstone_1:
9818 case Xsand_stonein_1:
9819 case Xsand_stoneout_1:
9847 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
9848 boolean has_crumbled_graphics,
9849 int crumbled, int sync_frame)
9851 // if element can be crumbled, but certain action graphics are just empty
9852 // space (like instantly snapping sand to empty space in 1 frame), do not
9853 // treat these empty space graphics as crumbled graphics in EMC engine
9854 if (crumbled == IMG_EMPTY_SPACE)
9855 has_crumbled_graphics = FALSE;
9857 if (has_crumbled_graphics)
9859 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
9860 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
9861 g_crumbled->anim_delay,
9862 g_crumbled->anim_mode,
9863 g_crumbled->anim_start_frame,
9866 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
9867 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
9869 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
9870 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
9872 g_em->has_crumbled_graphics = TRUE;
9876 g_em->crumbled_bitmap = NULL;
9877 g_em->crumbled_src_x = 0;
9878 g_em->crumbled_src_y = 0;
9879 g_em->crumbled_border_size = 0;
9880 g_em->crumbled_tile_size = 0;
9882 g_em->has_crumbled_graphics = FALSE;
9887 void ResetGfxAnimation_EM(int x, int y, int tile)
9893 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
9894 int tile, int frame_em, int x, int y)
9896 int action = em_object_mapping[tile].action;
9897 int direction = em_object_mapping[tile].direction;
9898 int effective_element = get_effective_element_EM(tile, frame_em);
9899 int graphic = (direction == MV_NONE ?
9900 el_act2img(effective_element, action) :
9901 el_act_dir2img(effective_element, action, direction));
9902 struct GraphicInfo *g = &graphic_info[graphic];
9904 boolean action_removing = (action == ACTION_DIGGING ||
9905 action == ACTION_SNAPPING ||
9906 action == ACTION_COLLECTING);
9907 boolean action_moving = (action == ACTION_FALLING ||
9908 action == ACTION_MOVING ||
9909 action == ACTION_PUSHING ||
9910 action == ACTION_EATING ||
9911 action == ACTION_FILLING ||
9912 action == ACTION_EMPTYING);
9913 boolean action_falling = (action == ACTION_FALLING ||
9914 action == ACTION_FILLING ||
9915 action == ACTION_EMPTYING);
9917 // special case: graphic uses "2nd movement tile" and has defined
9918 // 7 frames for movement animation (or less) => use default graphic
9919 // for last (8th) frame which ends the movement animation
9920 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
9922 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
9923 graphic = (direction == MV_NONE ?
9924 el_act2img(effective_element, action) :
9925 el_act_dir2img(effective_element, action, direction));
9927 g = &graphic_info[graphic];
9930 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
9934 else if (action_moving)
9936 boolean is_backside = em_object_mapping[tile].is_backside;
9940 int direction = em_object_mapping[tile].direction;
9941 int move_dir = (action_falling ? MV_DOWN : direction);
9946 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
9947 if (g->double_movement && frame_em == 0)
9951 if (move_dir == MV_LEFT)
9952 GfxFrame[x - 1][y] = GfxFrame[x][y];
9953 else if (move_dir == MV_RIGHT)
9954 GfxFrame[x + 1][y] = GfxFrame[x][y];
9955 else if (move_dir == MV_UP)
9956 GfxFrame[x][y - 1] = GfxFrame[x][y];
9957 else if (move_dir == MV_DOWN)
9958 GfxFrame[x][y + 1] = GfxFrame[x][y];
9965 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
9966 if (tile == Xsand_stonesand_quickout_1 ||
9967 tile == Xsand_stonesand_quickout_2)
9971 if (graphic_info[graphic].anim_global_sync)
9972 sync_frame = FrameCounter;
9973 else if (graphic_info[graphic].anim_global_anim_sync)
9974 sync_frame = getGlobalAnimSyncFrame();
9975 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
9976 sync_frame = GfxFrame[x][y];
9978 sync_frame = 0; // playfield border (pseudo steel)
9980 SetRandomAnimationValue(x, y);
9982 int frame = getAnimationFrame(g->anim_frames,
9985 g->anim_start_frame,
9988 g_em->unique_identifier =
9989 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
9992 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
9993 int tile, int frame_em, int x, int y)
9995 int action = em_object_mapping[tile].action;
9996 int direction = em_object_mapping[tile].direction;
9997 boolean is_backside = em_object_mapping[tile].is_backside;
9998 int effective_element = get_effective_element_EM(tile, frame_em);
9999 int effective_action = action;
10000 int graphic = (direction == MV_NONE ?
10001 el_act2img(effective_element, effective_action) :
10002 el_act_dir2img(effective_element, effective_action,
10004 int crumbled = (direction == MV_NONE ?
10005 el_act2crm(effective_element, effective_action) :
10006 el_act_dir2crm(effective_element, effective_action,
10008 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
10009 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
10010 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
10011 struct GraphicInfo *g = &graphic_info[graphic];
10014 // special case: graphic uses "2nd movement tile" and has defined
10015 // 7 frames for movement animation (or less) => use default graphic
10016 // for last (8th) frame which ends the movement animation
10017 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
10019 effective_action = ACTION_DEFAULT;
10020 graphic = (direction == MV_NONE ?
10021 el_act2img(effective_element, effective_action) :
10022 el_act_dir2img(effective_element, effective_action,
10024 crumbled = (direction == MV_NONE ?
10025 el_act2crm(effective_element, effective_action) :
10026 el_act_dir2crm(effective_element, effective_action,
10029 g = &graphic_info[graphic];
10032 if (graphic_info[graphic].anim_global_sync)
10033 sync_frame = FrameCounter;
10034 else if (graphic_info[graphic].anim_global_anim_sync)
10035 sync_frame = getGlobalAnimSyncFrame();
10036 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
10037 sync_frame = GfxFrame[x][y];
10039 sync_frame = 0; // playfield border (pseudo steel)
10041 SetRandomAnimationValue(x, y);
10043 int frame = getAnimationFrame(g->anim_frames,
10046 g->anim_start_frame,
10049 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
10050 g->double_movement && is_backside);
10052 // (updating the "crumbled" graphic definitions is probably not really needed,
10053 // as animations for crumbled graphics can't be longer than one EMC cycle)
10054 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
10058 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
10059 int player_nr, int anim, int frame_em)
10061 int element = em_player_mapping[player_nr][anim].element_rnd;
10062 int action = em_player_mapping[player_nr][anim].action;
10063 int direction = em_player_mapping[player_nr][anim].direction;
10064 int graphic = (direction == MV_NONE ?
10065 el_act2img(element, action) :
10066 el_act_dir2img(element, action, direction));
10067 struct GraphicInfo *g = &graphic_info[graphic];
10070 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
10072 stored_player[player_nr].StepFrame = frame_em;
10074 sync_frame = stored_player[player_nr].Frame;
10076 int frame = getAnimationFrame(g->anim_frames,
10079 g->anim_start_frame,
10082 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
10083 &g_em->src_x, &g_em->src_y, FALSE);
10086 #define BD_GFX_RANGE(a, n, i) ((i) >= (a) && (i) < (a) + (n))
10087 #define BD_GFX_FRAME(b, i) (((i) - (b)) * 8)
10089 void InitGraphicInfo_BD(void)
10093 // always start with reliable default values
10094 for (i = 0; i < O_MAX_ALL; i++)
10096 bd_object_mapping[i].element_rnd = EL_UNKNOWN;
10097 bd_object_mapping[i].action = ACTION_DEFAULT;
10098 bd_object_mapping[i].direction = MV_NONE;
10101 for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
10103 int e = bd_object_mapping_list[i].element_bd;
10105 bd_object_mapping[e].element_rnd = bd_object_mapping_list[i].element_rnd;
10107 if (bd_object_mapping_list[i].action != -1)
10108 bd_object_mapping[e].action = bd_object_mapping_list[i].action;
10110 if (bd_object_mapping_list[i].direction != -1)
10111 bd_object_mapping[e].direction =
10112 MV_DIR_FROM_BIT(bd_object_mapping_list[i].direction);
10115 for (i = 0; i < O_MAX_ALL; i++)
10117 int element = bd_object_mapping[i].element_rnd;
10118 int action = bd_object_mapping[i].action;
10119 int direction = bd_object_mapping[i].direction;
10121 for (j = 0; j < 8; j++)
10123 int effective_element = element;
10124 int effective_action = action;
10125 int graphic = (el_act_dir2img(effective_element, effective_action,
10127 struct GraphicInfo *g = &graphic_info[graphic];
10128 struct GraphicInfo_BD *g_bd = &graphic_info_bd_object[i][j];
10129 Bitmap *src_bitmap;
10131 int sync_frame = (BD_GFX_RANGE(O_PRE_PL_1, 3, i) ? BD_GFX_FRAME(O_PRE_PL_1, i) :
10132 BD_GFX_RANGE(O_PRE_DIA_1, 5, i) ? BD_GFX_FRAME(O_PRE_DIA_1, i) :
10133 BD_GFX_RANGE(O_PRE_STONE_1, 4, i) ? BD_GFX_FRAME(O_PRE_STONE_1, i) :
10134 BD_GFX_RANGE(O_PRE_STEEL_1, 4, i) ? BD_GFX_FRAME(O_PRE_STEEL_1, i) :
10135 BD_GFX_RANGE(O_BOMB_TICK_1, 7, i) ? BD_GFX_FRAME(O_BOMB_TICK_1, i) :
10136 BD_GFX_RANGE(O_BOMB_EXPL_1, 4, i) ? BD_GFX_FRAME(O_BOMB_EXPL_1, i) :
10137 BD_GFX_RANGE(O_NUT_EXPL_1, 4, i) ? BD_GFX_FRAME(O_NUT_EXPL_1, i) :
10138 BD_GFX_RANGE(O_GHOST_EXPL_1, 4, i) ? BD_GFX_FRAME(O_GHOST_EXPL_1, i) :
10139 BD_GFX_RANGE(O_EXPLODE_1, 5, i) ? BD_GFX_FRAME(O_EXPLODE_1, i) :
10140 BD_GFX_RANGE(O_PRE_CLOCK_1, 4, i) ? BD_GFX_FRAME(O_PRE_CLOCK_1, i) :
10141 BD_GFX_RANGE(O_NITRO_EXPL_1, 4, i) ? BD_GFX_FRAME(O_NITRO_EXPL_1, i) :
10142 BD_GFX_RANGE(O_AMOEBA_2_EXPL_1, 4, i) ? BD_GFX_FRAME(O_AMOEBA_2_EXPL_1, i):
10143 i == O_INBOX_OPEN || i == O_OUTBOX_OPEN ? j :
10145 int frame = getAnimationFrame(g->anim_frames,
10148 g->anim_start_frame,
10151 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
10153 g_bd->bitmap = src_bitmap;
10154 g_bd->src_x = src_x;
10155 g_bd->src_y = src_y;
10156 g_bd->width = TILEX;
10157 g_bd->height = TILEY;
10161 // game graphics template for level-specific colors for native BD levels
10162 int graphic = IMG_BD_GAME_GRAPHICS_COLOR_TEMPLATE;
10163 struct GraphicInfo_BD *g_bd = &graphic_info_bd_color_template;
10164 Bitmap *src_bitmap;
10167 getGraphicSourceExt(graphic, 0, &src_bitmap, &src_x, &src_y, FALSE);
10169 g_bd->bitmap = src_bitmap;
10170 g_bd->src_x = src_x;
10171 g_bd->src_y = src_y;
10172 g_bd->width = TILEX;
10173 g_bd->height = TILEY;
10176 void InitGraphicInfo_EM(void)
10180 // always start with reliable default values
10181 for (i = 0; i < GAME_TILE_MAX; i++)
10183 em_object_mapping[i].element_rnd = EL_UNKNOWN;
10184 em_object_mapping[i].is_backside = FALSE;
10185 em_object_mapping[i].action = ACTION_DEFAULT;
10186 em_object_mapping[i].direction = MV_NONE;
10189 // always start with reliable default values
10190 for (p = 0; p < MAX_PLAYERS; p++)
10192 for (i = 0; i < PLY_MAX; i++)
10194 em_player_mapping[p][i].element_rnd = EL_UNKNOWN;
10195 em_player_mapping[p][i].action = ACTION_DEFAULT;
10196 em_player_mapping[p][i].direction = MV_NONE;
10200 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
10202 int e = em_object_mapping_list[i].element_em;
10204 em_object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
10205 em_object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
10207 if (em_object_mapping_list[i].action != -1)
10208 em_object_mapping[e].action = em_object_mapping_list[i].action;
10210 if (em_object_mapping_list[i].direction != -1)
10211 em_object_mapping[e].direction =
10212 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
10215 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
10217 int a = em_player_mapping_list[i].action_em;
10218 int p = em_player_mapping_list[i].player_nr;
10220 em_player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
10222 if (em_player_mapping_list[i].action != -1)
10223 em_player_mapping[p][a].action = em_player_mapping_list[i].action;
10225 if (em_player_mapping_list[i].direction != -1)
10226 em_player_mapping[p][a].direction =
10227 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
10230 for (i = 0; i < GAME_TILE_MAX; i++)
10232 int element = em_object_mapping[i].element_rnd;
10233 int action = em_object_mapping[i].action;
10234 int direction = em_object_mapping[i].direction;
10235 boolean is_backside = em_object_mapping[i].is_backside;
10236 boolean action_exploding = ((action == ACTION_EXPLODING ||
10237 action == ACTION_SMASHED_BY_ROCK ||
10238 action == ACTION_SMASHED_BY_SPRING) &&
10239 element != EL_DIAMOND);
10240 boolean action_active = (action == ACTION_ACTIVE);
10241 boolean action_other = (action == ACTION_OTHER);
10243 for (j = 0; j < 8; j++)
10245 int effective_element = get_effective_element_EM(i, j);
10246 int effective_action = (j < 7 ? action :
10247 i == Xdrip_stretch ? action :
10248 i == Xdrip_stretchB ? action :
10249 i == Ydrip_1_s ? action :
10250 i == Ydrip_1_sB ? action :
10251 i == Yball_1 ? action :
10252 i == Xball_2 ? action :
10253 i == Yball_2 ? action :
10254 i == Yball_blank ? action :
10255 i == Ykey_1_blank ? action :
10256 i == Ykey_2_blank ? action :
10257 i == Ykey_3_blank ? action :
10258 i == Ykey_4_blank ? action :
10259 i == Ykey_5_blank ? action :
10260 i == Ykey_6_blank ? action :
10261 i == Ykey_7_blank ? action :
10262 i == Ykey_8_blank ? action :
10263 i == Ylenses_blank ? action :
10264 i == Ymagnify_blank ? action :
10265 i == Ygrass_blank ? action :
10266 i == Ydirt_blank ? action :
10267 i == Xsand_stonein_1 ? action :
10268 i == Xsand_stonein_2 ? action :
10269 i == Xsand_stonein_3 ? action :
10270 i == Xsand_stonein_4 ? action :
10271 i == Xsand_stoneout_1 ? action :
10272 i == Xsand_stoneout_2 ? action :
10273 i == Xboom_android ? ACTION_EXPLODING :
10274 action_exploding ? ACTION_EXPLODING :
10275 action_active ? action :
10276 action_other ? action :
10278 int graphic = (el_act_dir2img(effective_element, effective_action,
10280 int crumbled = (el_act_dir2crm(effective_element, effective_action,
10282 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
10283 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
10284 boolean has_action_graphics = (graphic != base_graphic);
10285 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
10286 struct GraphicInfo *g = &graphic_info[graphic];
10287 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
10288 Bitmap *src_bitmap;
10290 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
10291 boolean special_animation = (action != ACTION_DEFAULT &&
10292 g->anim_frames == 3 &&
10293 g->anim_delay == 2 &&
10294 g->anim_mode & ANIM_LINEAR);
10295 int sync_frame = (i == Xdrip_stretch ? 7 :
10296 i == Xdrip_stretchB ? 7 :
10297 i == Ydrip_2_s ? j + 8 :
10298 i == Ydrip_2_sB ? j + 8 :
10300 i == Xacid_2 ? 10 :
10301 i == Xacid_3 ? 20 :
10302 i == Xacid_4 ? 30 :
10303 i == Xacid_5 ? 40 :
10304 i == Xacid_6 ? 50 :
10305 i == Xacid_7 ? 60 :
10306 i == Xacid_8 ? 70 :
10307 i == Xfake_acid_1 ? 0 :
10308 i == Xfake_acid_2 ? 10 :
10309 i == Xfake_acid_3 ? 20 :
10310 i == Xfake_acid_4 ? 30 :
10311 i == Xfake_acid_5 ? 40 :
10312 i == Xfake_acid_6 ? 50 :
10313 i == Xfake_acid_7 ? 60 :
10314 i == Xfake_acid_8 ? 70 :
10315 i == Xfake_acid_1_player ? 0 :
10316 i == Xfake_acid_2_player ? 10 :
10317 i == Xfake_acid_3_player ? 20 :
10318 i == Xfake_acid_4_player ? 30 :
10319 i == Xfake_acid_5_player ? 40 :
10320 i == Xfake_acid_6_player ? 50 :
10321 i == Xfake_acid_7_player ? 60 :
10322 i == Xfake_acid_8_player ? 70 :
10324 i == Yball_2 ? j + 8 :
10325 i == Yball_blank ? j + 1 :
10326 i == Ykey_1_blank ? j + 1 :
10327 i == Ykey_2_blank ? j + 1 :
10328 i == Ykey_3_blank ? j + 1 :
10329 i == Ykey_4_blank ? j + 1 :
10330 i == Ykey_5_blank ? j + 1 :
10331 i == Ykey_6_blank ? j + 1 :
10332 i == Ykey_7_blank ? j + 1 :
10333 i == Ykey_8_blank ? j + 1 :
10334 i == Ylenses_blank ? j + 1 :
10335 i == Ymagnify_blank ? j + 1 :
10336 i == Ygrass_blank ? j + 1 :
10337 i == Ydirt_blank ? j + 1 :
10338 i == Xamoeba_1 ? 0 :
10339 i == Xamoeba_2 ? 1 :
10340 i == Xamoeba_3 ? 2 :
10341 i == Xamoeba_4 ? 3 :
10342 i == Xamoeba_5 ? 0 :
10343 i == Xamoeba_6 ? 1 :
10344 i == Xamoeba_7 ? 2 :
10345 i == Xamoeba_8 ? 3 :
10346 i == Xexit_2 ? j + 8 :
10347 i == Xexit_3 ? j + 16 :
10348 i == Xdynamite_1 ? 0 :
10349 i == Xdynamite_2 ? 8 :
10350 i == Xdynamite_3 ? 16 :
10351 i == Xdynamite_4 ? 24 :
10352 i == Xsand_stonein_1 ? j + 1 :
10353 i == Xsand_stonein_2 ? j + 9 :
10354 i == Xsand_stonein_3 ? j + 17 :
10355 i == Xsand_stonein_4 ? j + 25 :
10356 i == Xsand_stoneout_1 && j == 0 ? 0 :
10357 i == Xsand_stoneout_1 && j == 1 ? 0 :
10358 i == Xsand_stoneout_1 && j == 2 ? 1 :
10359 i == Xsand_stoneout_1 && j == 3 ? 2 :
10360 i == Xsand_stoneout_1 && j == 4 ? 2 :
10361 i == Xsand_stoneout_1 && j == 5 ? 3 :
10362 i == Xsand_stoneout_1 && j == 6 ? 4 :
10363 i == Xsand_stoneout_1 && j == 7 ? 4 :
10364 i == Xsand_stoneout_2 && j == 0 ? 5 :
10365 i == Xsand_stoneout_2 && j == 1 ? 6 :
10366 i == Xsand_stoneout_2 && j == 2 ? 7 :
10367 i == Xsand_stoneout_2 && j == 3 ? 8 :
10368 i == Xsand_stoneout_2 && j == 4 ? 9 :
10369 i == Xsand_stoneout_2 && j == 5 ? 11 :
10370 i == Xsand_stoneout_2 && j == 6 ? 13 :
10371 i == Xsand_stoneout_2 && j == 7 ? 15 :
10372 i == Xboom_bug && j == 1 ? 2 :
10373 i == Xboom_bug && j == 2 ? 2 :
10374 i == Xboom_bug && j == 3 ? 4 :
10375 i == Xboom_bug && j == 4 ? 4 :
10376 i == Xboom_bug && j == 5 ? 2 :
10377 i == Xboom_bug && j == 6 ? 2 :
10378 i == Xboom_bug && j == 7 ? 0 :
10379 i == Xboom_tank && j == 1 ? 2 :
10380 i == Xboom_tank && j == 2 ? 2 :
10381 i == Xboom_tank && j == 3 ? 4 :
10382 i == Xboom_tank && j == 4 ? 4 :
10383 i == Xboom_tank && j == 5 ? 2 :
10384 i == Xboom_tank && j == 6 ? 2 :
10385 i == Xboom_tank && j == 7 ? 0 :
10386 i == Xboom_android && j == 7 ? 6 :
10387 i == Xboom_1 && j == 1 ? 2 :
10388 i == Xboom_1 && j == 2 ? 2 :
10389 i == Xboom_1 && j == 3 ? 4 :
10390 i == Xboom_1 && j == 4 ? 4 :
10391 i == Xboom_1 && j == 5 ? 6 :
10392 i == Xboom_1 && j == 6 ? 6 :
10393 i == Xboom_1 && j == 7 ? 8 :
10394 i == Xboom_2 && j == 0 ? 8 :
10395 i == Xboom_2 && j == 1 ? 8 :
10396 i == Xboom_2 && j == 2 ? 10 :
10397 i == Xboom_2 && j == 3 ? 10 :
10398 i == Xboom_2 && j == 4 ? 10 :
10399 i == Xboom_2 && j == 5 ? 12 :
10400 i == Xboom_2 && j == 6 ? 12 :
10401 i == Xboom_2 && j == 7 ? 12 :
10402 special_animation && j == 4 ? 3 :
10403 effective_action != action ? 0 :
10405 int frame = getAnimationFrame(g->anim_frames,
10408 g->anim_start_frame,
10411 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
10412 g->double_movement && is_backside);
10414 g_em->bitmap = src_bitmap;
10415 g_em->src_x = src_x;
10416 g_em->src_y = src_y;
10417 g_em->src_offset_x = 0;
10418 g_em->src_offset_y = 0;
10419 g_em->dst_offset_x = 0;
10420 g_em->dst_offset_y = 0;
10421 g_em->width = TILEX;
10422 g_em->height = TILEY;
10424 g_em->preserve_background = FALSE;
10426 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
10429 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
10430 effective_action == ACTION_MOVING ||
10431 effective_action == ACTION_PUSHING ||
10432 effective_action == ACTION_EATING)) ||
10433 (!has_action_graphics && (effective_action == ACTION_FILLING ||
10434 effective_action == ACTION_EMPTYING)))
10437 (effective_action == ACTION_FALLING ||
10438 effective_action == ACTION_FILLING ||
10439 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
10440 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
10441 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
10442 int num_steps = (i == Ydrip_1_s ? 16 :
10443 i == Ydrip_1_sB ? 16 :
10444 i == Ydrip_2_s ? 16 :
10445 i == Ydrip_2_sB ? 16 :
10446 i == Xsand_stonein_1 ? 32 :
10447 i == Xsand_stonein_2 ? 32 :
10448 i == Xsand_stonein_3 ? 32 :
10449 i == Xsand_stonein_4 ? 32 :
10450 i == Xsand_stoneout_1 ? 16 :
10451 i == Xsand_stoneout_2 ? 16 : 8);
10452 int cx = ABS(dx) * (TILEX / num_steps);
10453 int cy = ABS(dy) * (TILEY / num_steps);
10454 int step_frame = (i == Ydrip_2_s ? j + 8 :
10455 i == Ydrip_2_sB ? j + 8 :
10456 i == Xsand_stonein_2 ? j + 8 :
10457 i == Xsand_stonein_3 ? j + 16 :
10458 i == Xsand_stonein_4 ? j + 24 :
10459 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
10460 int step = (is_backside ? step_frame : num_steps - step_frame);
10462 if (is_backside) // tile where movement starts
10464 if (dx < 0 || dy < 0)
10466 g_em->src_offset_x = cx * step;
10467 g_em->src_offset_y = cy * step;
10471 g_em->dst_offset_x = cx * step;
10472 g_em->dst_offset_y = cy * step;
10475 else // tile where movement ends
10477 if (dx < 0 || dy < 0)
10479 g_em->dst_offset_x = cx * step;
10480 g_em->dst_offset_y = cy * step;
10484 g_em->src_offset_x = cx * step;
10485 g_em->src_offset_y = cy * step;
10489 g_em->width = TILEX - cx * step;
10490 g_em->height = TILEY - cy * step;
10493 // create unique graphic identifier to decide if tile must be redrawn
10494 /* bit 31 - 16 (16 bit): EM style graphic
10495 bit 15 - 12 ( 4 bit): EM style frame
10496 bit 11 - 6 ( 6 bit): graphic width
10497 bit 5 - 0 ( 6 bit): graphic height */
10498 g_em->unique_identifier =
10499 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
10503 for (i = 0; i < GAME_TILE_MAX; i++)
10505 for (j = 0; j < 8; j++)
10507 int element = em_object_mapping[i].element_rnd;
10508 int action = em_object_mapping[i].action;
10509 int direction = em_object_mapping[i].direction;
10510 boolean is_backside = em_object_mapping[i].is_backside;
10511 int graphic_action = el_act_dir2img(element, action, direction);
10512 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
10514 if ((action == ACTION_SMASHED_BY_ROCK ||
10515 action == ACTION_SMASHED_BY_SPRING ||
10516 action == ACTION_EATING) &&
10517 graphic_action == graphic_default)
10519 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
10520 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
10521 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
10522 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
10525 // no separate animation for "smashed by rock" -- use rock instead
10526 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
10527 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
10529 g_em->bitmap = g_xx->bitmap;
10530 g_em->src_x = g_xx->src_x;
10531 g_em->src_y = g_xx->src_y;
10532 g_em->src_offset_x = g_xx->src_offset_x;
10533 g_em->src_offset_y = g_xx->src_offset_y;
10534 g_em->dst_offset_x = g_xx->dst_offset_x;
10535 g_em->dst_offset_y = g_xx->dst_offset_y;
10536 g_em->width = g_xx->width;
10537 g_em->height = g_xx->height;
10538 g_em->unique_identifier = g_xx->unique_identifier;
10541 g_em->preserve_background = TRUE;
10546 for (p = 0; p < MAX_PLAYERS; p++)
10548 for (i = 0; i < PLY_MAX; i++)
10550 int element = em_player_mapping[p][i].element_rnd;
10551 int action = em_player_mapping[p][i].action;
10552 int direction = em_player_mapping[p][i].direction;
10554 for (j = 0; j < 8; j++)
10556 int effective_element = element;
10557 int effective_action = action;
10558 int graphic = (direction == MV_NONE ?
10559 el_act2img(effective_element, effective_action) :
10560 el_act_dir2img(effective_element, effective_action,
10562 struct GraphicInfo *g = &graphic_info[graphic];
10563 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
10564 Bitmap *src_bitmap;
10566 int sync_frame = j;
10567 int frame = getAnimationFrame(g->anim_frames,
10570 g->anim_start_frame,
10573 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
10575 g_em->bitmap = src_bitmap;
10576 g_em->src_x = src_x;
10577 g_em->src_y = src_y;
10578 g_em->src_offset_x = 0;
10579 g_em->src_offset_y = 0;
10580 g_em->dst_offset_x = 0;
10581 g_em->dst_offset_y = 0;
10582 g_em->width = TILEX;
10583 g_em->height = TILEY;
10589 static void CheckSaveEngineSnapshot_EM(int frame,
10590 boolean any_player_moving,
10591 boolean any_player_snapping,
10592 boolean any_player_dropping)
10594 if (frame == 7 && !any_player_dropping)
10596 if (!local_player->was_waiting)
10598 if (!CheckSaveEngineSnapshotToList())
10601 local_player->was_waiting = TRUE;
10604 else if (any_player_moving || any_player_snapping || any_player_dropping)
10606 local_player->was_waiting = FALSE;
10610 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
10611 boolean murphy_is_dropping)
10613 if (murphy_is_waiting)
10615 if (!local_player->was_waiting)
10617 if (!CheckSaveEngineSnapshotToList())
10620 local_player->was_waiting = TRUE;
10625 local_player->was_waiting = FALSE;
10629 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
10630 boolean button_released)
10632 if (button_released)
10634 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
10635 CheckSaveEngineSnapshotToList();
10637 else if (element_clicked)
10639 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
10640 CheckSaveEngineSnapshotToList();
10642 game.snapshot.changed_action = TRUE;
10646 boolean CheckSingleStepMode_EM(int frame,
10647 boolean any_player_moving,
10648 boolean any_player_snapping,
10649 boolean any_player_dropping)
10651 if (tape.single_step && tape.recording && !tape.pausing)
10652 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
10653 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10655 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
10656 any_player_snapping, any_player_dropping);
10658 return tape.pausing;
10661 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
10662 boolean murphy_is_dropping)
10664 boolean murphy_starts_dropping = FALSE;
10667 for (i = 0; i < MAX_PLAYERS; i++)
10668 if (stored_player[i].force_dropping)
10669 murphy_starts_dropping = TRUE;
10671 if (tape.single_step && tape.recording && !tape.pausing)
10672 if (murphy_is_waiting && !murphy_starts_dropping)
10673 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10675 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
10678 void CheckSingleStepMode_MM(boolean element_clicked,
10679 boolean button_released)
10681 if (tape.single_step && tape.recording && !tape.pausing)
10682 if (button_released)
10683 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10685 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
10688 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
10689 int graphic, int sync_frame)
10691 int frame = getGraphicAnimationFrame(graphic, sync_frame);
10693 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
10696 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
10698 return (IS_NEXT_FRAME(sync_frame, graphic));
10701 int getGraphicInfo_Delay(int graphic)
10703 return graphic_info[graphic].anim_delay;
10706 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
10708 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
10711 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
10717 void PlayMenuSoundExt(int sound)
10719 if (sound == SND_UNDEFINED)
10722 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
10723 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
10726 if (IS_LOOP_SOUND(sound))
10727 PlaySoundLoop(sound);
10732 void PlayMenuSound(void)
10734 PlayMenuSoundExt(menu.sound[game_status]);
10737 void PlayMenuSoundStereo(int sound, int stereo_position)
10739 if (sound == SND_UNDEFINED)
10742 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
10743 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
10746 if (IS_LOOP_SOUND(sound))
10747 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
10749 PlaySoundStereo(sound, stereo_position);
10752 void PlayMenuSoundIfLoopExt(int sound)
10754 if (sound == SND_UNDEFINED)
10757 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
10758 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
10761 if (IS_LOOP_SOUND(sound))
10762 PlaySoundLoop(sound);
10765 void PlayMenuSoundIfLoop(void)
10767 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
10770 void PlayMenuMusicExt(int music)
10772 if (music == MUS_UNDEFINED)
10775 if (!setup.sound_music)
10778 if (IS_LOOP_MUSIC(music))
10779 PlayMusicLoop(music);
10784 void PlayMenuMusic(void)
10786 char *curr_music = getCurrentlyPlayingMusicFilename();
10787 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
10789 if (!strEqual(curr_music, next_music))
10790 PlayMenuMusicExt(menu.music[game_status]);
10793 void PlayMenuSoundsAndMusic(void)
10799 static void FadeMenuSounds(void)
10804 static void FadeMenuMusic(void)
10806 char *curr_music = getCurrentlyPlayingMusicFilename();
10807 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
10809 if (!strEqual(curr_music, next_music))
10813 void FadeMenuSoundsAndMusic(void)
10819 void PlaySoundActivating(void)
10822 PlaySound(SND_MENU_ITEM_ACTIVATING);
10826 void PlaySoundSelecting(void)
10829 PlaySound(SND_MENU_ITEM_SELECTING);
10833 void ToggleFullscreenIfNeeded(void)
10835 // if setup and video fullscreen state are already matching, nothing do do
10836 if (setup.fullscreen == video.fullscreen_enabled ||
10837 !video.fullscreen_available)
10840 SDLSetWindowFullscreen(setup.fullscreen);
10842 // set setup value according to successfully changed fullscreen mode
10843 setup.fullscreen = video.fullscreen_enabled;
10846 void ChangeWindowScalingIfNeeded(void)
10848 // if setup and video window scaling are already matching, nothing do do
10849 if (setup.window_scaling_percent == video.window_scaling_percent ||
10850 video.fullscreen_enabled)
10853 SDLSetWindowScaling(setup.window_scaling_percent);
10855 // set setup value according to successfully changed window scaling
10856 setup.window_scaling_percent = video.window_scaling_percent;
10859 void ChangeVsyncModeIfNeeded(void)
10861 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
10862 int video_vsync_mode = video.vsync_mode;
10864 // if setup and video vsync mode are already matching, nothing do do
10865 if (setup_vsync_mode == video_vsync_mode)
10868 // if renderer is using OpenGL, vsync mode can directly be changed
10869 SDLSetScreenVsyncMode(setup.vsync_mode);
10871 // if vsync mode unchanged, try re-creating renderer to set vsync mode
10872 if (video.vsync_mode == video_vsync_mode)
10874 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
10876 // save backbuffer content which gets lost when re-creating screen
10877 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
10879 // force re-creating screen and renderer to set new vsync mode
10880 video.fullscreen_enabled = !setup.fullscreen;
10882 // when creating new renderer, destroy textures linked to old renderer
10883 FreeAllImageTextures(); // needs old renderer to free the textures
10885 // re-create screen and renderer (including change of vsync mode)
10886 ChangeVideoModeIfNeeded(setup.fullscreen);
10888 // set setup value according to successfully changed fullscreen mode
10889 setup.fullscreen = video.fullscreen_enabled;
10891 // restore backbuffer content from temporary backbuffer backup bitmap
10892 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
10893 FreeBitmap(tmp_backbuffer);
10895 // update visible window/screen
10896 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
10898 // when changing vsync mode, re-create textures for new renderer
10899 InitImageTextures();
10902 // set setup value according to successfully changed vsync mode
10903 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
10906 static void JoinRectangles(int *x, int *y, int *width, int *height,
10907 int x2, int y2, int width2, int height2)
10909 // do not join with "off-screen" rectangle
10910 if (x2 == -1 || y2 == -1)
10915 *width = MAX(*width, width2);
10916 *height = MAX(*height, height2);
10919 void SetAnimStatus(int anim_status_new)
10921 if (anim_status_new == GAME_MODE_MAIN)
10922 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
10923 else if (anim_status_new == GAME_MODE_NAMES)
10924 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
10925 else if (anim_status_new == GAME_MODE_SCORES)
10926 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
10928 global.anim_status_next = anim_status_new;
10930 // directly set screen modes that are entered without fading
10931 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
10932 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
10933 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
10934 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
10935 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
10936 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
10937 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
10938 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
10939 global.anim_status = global.anim_status_next;
10942 void SetGameStatus(int game_status_new)
10944 if (game_status_new != game_status)
10945 game_status_last_screen = game_status;
10947 game_status = game_status_new;
10949 SetAnimStatus(game_status_new);
10952 void SetFontStatus(int game_status_new)
10954 static int last_game_status = -1;
10956 if (game_status_new != -1)
10958 // set game status for font use after storing last game status
10959 last_game_status = game_status;
10960 game_status = game_status_new;
10964 // reset game status after font use from last stored game status
10965 game_status = last_game_status;
10969 void ResetFontStatus(void)
10974 void SetLevelSetInfo(char *identifier, int level_nr)
10976 setString(&levelset.identifier, identifier);
10978 levelset.level_nr = level_nr;
10981 boolean CheckIfAllViewportsHaveChanged(void)
10983 // if game status has not changed, viewports have not changed either
10984 if (game_status == game_status_last)
10987 // check if all viewports have changed with current game status
10989 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
10990 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
10991 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
10992 int new_real_sx = vp_playfield->x;
10993 int new_real_sy = vp_playfield->y;
10994 int new_full_sxsize = vp_playfield->width;
10995 int new_full_sysize = vp_playfield->height;
10996 int new_dx = vp_door_1->x;
10997 int new_dy = vp_door_1->y;
10998 int new_dxsize = vp_door_1->width;
10999 int new_dysize = vp_door_1->height;
11000 int new_vx = vp_door_2->x;
11001 int new_vy = vp_door_2->y;
11002 int new_vxsize = vp_door_2->width;
11003 int new_vysize = vp_door_2->height;
11005 boolean playfield_viewport_has_changed =
11006 (new_real_sx != REAL_SX ||
11007 new_real_sy != REAL_SY ||
11008 new_full_sxsize != FULL_SXSIZE ||
11009 new_full_sysize != FULL_SYSIZE);
11011 boolean door_1_viewport_has_changed =
11014 new_dxsize != DXSIZE ||
11015 new_dysize != DYSIZE);
11017 boolean door_2_viewport_has_changed =
11020 new_vxsize != VXSIZE ||
11021 new_vysize != VYSIZE ||
11022 game_status_last == GAME_MODE_EDITOR);
11024 return (playfield_viewport_has_changed &&
11025 door_1_viewport_has_changed &&
11026 door_2_viewport_has_changed);
11029 boolean CheckFadeAll(void)
11031 return (CheckIfGlobalBorderHasChanged() ||
11032 CheckIfAllViewportsHaveChanged());
11035 void ChangeViewportPropertiesIfNeeded(void)
11037 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
11038 FALSE : setup.small_game_graphics);
11039 int gfx_game_mode = getGlobalGameStatus(game_status);
11040 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
11042 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
11043 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
11044 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
11045 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
11046 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
11047 int new_win_xsize = vp_window->width;
11048 int new_win_ysize = vp_window->height;
11049 int border_left = vp_playfield->border_left;
11050 int border_right = vp_playfield->border_right;
11051 int border_top = vp_playfield->border_top;
11052 int border_bottom = vp_playfield->border_bottom;
11053 int new_sx = vp_playfield->x + border_left;
11054 int new_sy = vp_playfield->y + border_top;
11055 int new_sxsize = vp_playfield->width - border_left - border_right;
11056 int new_sysize = vp_playfield->height - border_top - border_bottom;
11057 int new_real_sx = vp_playfield->x;
11058 int new_real_sy = vp_playfield->y;
11059 int new_full_sxsize = vp_playfield->width;
11060 int new_full_sysize = vp_playfield->height;
11061 int new_dx = vp_door_1->x;
11062 int new_dy = vp_door_1->y;
11063 int new_dxsize = vp_door_1->width;
11064 int new_dysize = vp_door_1->height;
11065 int new_vx = vp_door_2->x;
11066 int new_vy = vp_door_2->y;
11067 int new_vxsize = vp_door_2->width;
11068 int new_vysize = vp_door_2->height;
11069 int new_ex = vp_door_3->x;
11070 int new_ey = vp_door_3->y;
11071 int new_exsize = vp_door_3->width;
11072 int new_eysize = vp_door_3->height;
11073 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
11074 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
11075 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
11076 int new_scr_fieldx = new_sxsize / tilesize;
11077 int new_scr_fieldy = new_sysize / tilesize;
11078 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
11079 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
11080 boolean init_gfx_buffers = FALSE;
11081 boolean init_video_buffer = FALSE;
11082 boolean init_gadgets_and_anims = FALSE;
11083 boolean init_bd_graphics = FALSE;
11084 boolean init_em_graphics = FALSE;
11086 if (new_win_xsize != WIN_XSIZE ||
11087 new_win_ysize != WIN_YSIZE)
11089 WIN_XSIZE = new_win_xsize;
11090 WIN_YSIZE = new_win_ysize;
11092 init_video_buffer = TRUE;
11093 init_gfx_buffers = TRUE;
11094 init_gadgets_and_anims = TRUE;
11096 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
11099 if (new_scr_fieldx != SCR_FIELDX ||
11100 new_scr_fieldy != SCR_FIELDY)
11102 // this always toggles between MAIN and GAME when using small tile size
11104 SCR_FIELDX = new_scr_fieldx;
11105 SCR_FIELDY = new_scr_fieldy;
11107 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
11110 if (new_sx != SX ||
11118 new_sxsize != SXSIZE ||
11119 new_sysize != SYSIZE ||
11120 new_dxsize != DXSIZE ||
11121 new_dysize != DYSIZE ||
11122 new_vxsize != VXSIZE ||
11123 new_vysize != VYSIZE ||
11124 new_exsize != EXSIZE ||
11125 new_eysize != EYSIZE ||
11126 new_real_sx != REAL_SX ||
11127 new_real_sy != REAL_SY ||
11128 new_full_sxsize != FULL_SXSIZE ||
11129 new_full_sysize != FULL_SYSIZE ||
11130 new_tilesize_var != TILESIZE_VAR
11133 // ------------------------------------------------------------------------
11134 // determine next fading area for changed viewport definitions
11135 // ------------------------------------------------------------------------
11137 // start with current playfield area (default fading area)
11140 FADE_SXSIZE = FULL_SXSIZE;
11141 FADE_SYSIZE = FULL_SYSIZE;
11143 // add new playfield area if position or size has changed
11144 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
11145 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
11147 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11148 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
11151 // add current and new door 1 area if position or size has changed
11152 if (new_dx != DX || new_dy != DY ||
11153 new_dxsize != DXSIZE || new_dysize != DYSIZE)
11155 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11156 DX, DY, DXSIZE, DYSIZE);
11157 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11158 new_dx, new_dy, new_dxsize, new_dysize);
11161 // add current and new door 2 area if position or size has changed
11162 if (new_vx != VX || new_vy != VY ||
11163 new_vxsize != VXSIZE || new_vysize != VYSIZE)
11165 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11166 VX, VY, VXSIZE, VYSIZE);
11167 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11168 new_vx, new_vy, new_vxsize, new_vysize);
11171 // ------------------------------------------------------------------------
11172 // handle changed tile size
11173 // ------------------------------------------------------------------------
11175 if (new_tilesize_var != TILESIZE_VAR)
11177 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
11179 // changing tile size invalidates scroll values of engine snapshots
11180 FreeEngineSnapshotSingle();
11182 // changing tile size requires update of graphic mapping for BD/EM engine
11183 init_bd_graphics = TRUE;
11184 init_em_graphics = TRUE;
11195 SXSIZE = new_sxsize;
11196 SYSIZE = new_sysize;
11197 DXSIZE = new_dxsize;
11198 DYSIZE = new_dysize;
11199 VXSIZE = new_vxsize;
11200 VYSIZE = new_vysize;
11201 EXSIZE = new_exsize;
11202 EYSIZE = new_eysize;
11203 REAL_SX = new_real_sx;
11204 REAL_SY = new_real_sy;
11205 FULL_SXSIZE = new_full_sxsize;
11206 FULL_SYSIZE = new_full_sysize;
11207 TILESIZE_VAR = new_tilesize_var;
11209 init_gfx_buffers = TRUE;
11210 init_gadgets_and_anims = TRUE;
11212 // Debug("tools:viewport", "viewports: init_gfx_buffers");
11213 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
11216 if (init_gfx_buffers)
11218 // Debug("tools:viewport", "init_gfx_buffers");
11220 SCR_FIELDX = new_scr_fieldx_buffers;
11221 SCR_FIELDY = new_scr_fieldy_buffers;
11225 SCR_FIELDX = new_scr_fieldx;
11226 SCR_FIELDY = new_scr_fieldy;
11228 SetDrawDeactivationMask(REDRAW_NONE);
11229 SetDrawBackgroundMask(REDRAW_FIELD);
11232 if (init_video_buffer)
11234 // Debug("tools:viewport", "init_video_buffer");
11236 FreeAllImageTextures(); // needs old renderer to free the textures
11238 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
11239 InitImageTextures();
11242 if (init_gadgets_and_anims)
11244 // Debug("tools:viewport", "init_gadgets_and_anims");
11247 InitGlobalAnimations();
11250 if (init_bd_graphics)
11252 InitGraphicInfo_BD();
11255 if (init_em_graphics)
11257 InitGraphicInfo_EM();
11261 void OpenURL(char *url)
11263 #if SDL_VERSION_ATLEAST(2,0,14)
11266 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
11267 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
11268 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
11272 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
11274 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
11277 char *getCurrentLevelsetName(void)
11279 return leveldir_current->name;
11283 // ============================================================================
11285 // ============================================================================
11287 #if defined(PLATFORM_WINDOWS)
11288 /* FILETIME of Jan 1 1970 00:00:00. */
11289 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
11292 * timezone information is stored outside the kernel so tzp isn't used anymore.
11294 * Note: this function is not for Win32 high precision timing purpose. See
11297 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
11299 FILETIME file_time;
11300 SYSTEMTIME system_time;
11301 ULARGE_INTEGER ularge;
11303 GetSystemTime(&system_time);
11304 SystemTimeToFileTime(&system_time, &file_time);
11305 ularge.LowPart = file_time.dwLowDateTime;
11306 ularge.HighPart = file_time.dwHighDateTime;
11308 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
11309 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
11315 static char *test_init_uuid_random_function_simple(void)
11317 static char seed_text[100];
11318 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
11320 sprintf(seed_text, "%d", seed);
11325 static char *test_init_uuid_random_function_better(void)
11327 static char seed_text[100];
11328 struct timeval current_time;
11330 gettimeofday(¤t_time, NULL);
11332 prng_seed_bytes(¤t_time, sizeof(current_time));
11334 sprintf(seed_text, "%ld.%ld",
11335 (long)current_time.tv_sec,
11336 (long)current_time.tv_usec);
11341 #if defined(PLATFORM_WINDOWS)
11342 static char *test_init_uuid_random_function_better_windows(void)
11344 static char seed_text[100];
11345 struct timeval current_time;
11347 gettimeofday_windows(¤t_time, NULL);
11349 prng_seed_bytes(¤t_time, sizeof(current_time));
11351 sprintf(seed_text, "%ld.%ld",
11352 (long)current_time.tv_sec,
11353 (long)current_time.tv_usec);
11359 static unsigned int test_uuid_random_function_simple(int max)
11361 return GetSimpleRandom(max);
11364 static unsigned int test_uuid_random_function_better(int max)
11366 return (max > 0 ? prng_get_uint() % max : 0);
11369 #if defined(PLATFORM_WINDOWS)
11370 #define NUM_UUID_TESTS 3
11372 #define NUM_UUID_TESTS 2
11375 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
11377 HashTable *hash_seeds =
11378 create_hashtable(get_hash_from_string, hash_key_strings_are_equal, free, NULL);
11379 HashTable *hash_uuids =
11380 create_hashtable(get_hash_from_string, hash_key_strings_are_equal, free, NULL);
11381 static char message[100];
11384 char *random_name = (nr == 0 ? "simple" : "better");
11385 char *random_type = (always_seed ? "always" : "only once");
11386 char *(*init_random_function)(void) =
11388 test_init_uuid_random_function_simple :
11389 test_init_uuid_random_function_better);
11390 unsigned int (*random_function)(int) =
11392 test_uuid_random_function_simple :
11393 test_uuid_random_function_better);
11396 #if defined(PLATFORM_WINDOWS)
11399 random_name = "windows";
11400 init_random_function = test_init_uuid_random_function_better_windows;
11406 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
11407 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
11409 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
11410 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
11411 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
11413 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
11417 // always initialize random number generator at least once
11418 init_random_function();
11420 unsigned int time_start = SDL_GetTicks();
11422 for (i = 0; i < num_uuids; i++)
11426 char *seed = getStringCopy(init_random_function());
11428 hashtable_remove(hash_seeds, seed);
11429 hashtable_insert(hash_seeds, seed, "1");
11432 char *uuid = getStringCopy(getUUIDExt(random_function));
11434 hashtable_remove(hash_uuids, uuid);
11435 hashtable_insert(hash_uuids, uuid, "1");
11438 int num_unique_seeds = hashtable_count(hash_seeds);
11439 int num_unique_uuids = hashtable_count(hash_uuids);
11441 unsigned int time_needed = SDL_GetTicks() - time_start;
11443 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
11445 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
11448 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
11450 if (nr == NUM_UUID_TESTS - 1 && always_seed)
11451 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
11453 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
11455 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
11457 Request(message, REQ_CONFIRM);
11459 hashtable_destroy(hash_seeds);
11460 hashtable_destroy(hash_uuids);
11463 void TestGeneratingUUIDs(void)
11465 int num_uuids = 1000000;
11468 for (i = 0; i < NUM_UUID_TESTS; i++)
11469 for (j = 0; j < 2; j++)
11470 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
11472 CloseAllAndExit(0);