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 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3401 if (IS_MM_WALL(element))
3403 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3409 int graphic = el2preimg(element);
3411 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3412 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3417 void DrawLevel(int draw_background_mask)
3421 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3422 SetDrawBackgroundMask(draw_background_mask);
3426 for (x = BX1; x <= BX2; x++)
3427 for (y = BY1; y <= BY2; y++)
3428 DrawScreenField(x, y);
3430 redraw_mask |= REDRAW_FIELD;
3433 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3438 for (x = 0; x < size_x; x++)
3439 for (y = 0; y < size_y; y++)
3440 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3442 redraw_mask |= REDRAW_FIELD;
3445 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3449 for (x = 0; x < size_x; x++)
3450 for (y = 0; y < size_y; y++)
3451 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3453 redraw_mask |= REDRAW_FIELD;
3456 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3458 boolean show_level_border = (BorderElement != EL_EMPTY);
3459 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3460 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3461 int tile_size = preview.tile_size;
3462 int preview_width = preview.xsize * tile_size;
3463 int preview_height = preview.ysize * tile_size;
3464 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3465 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3466 int real_preview_width = real_preview_xsize * tile_size;
3467 int real_preview_height = real_preview_ysize * tile_size;
3468 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3469 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3472 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3475 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3477 dst_x += (preview_width - real_preview_width) / 2;
3478 dst_y += (preview_height - real_preview_height) / 2;
3480 for (x = 0; x < real_preview_xsize; x++)
3482 for (y = 0; y < real_preview_ysize; y++)
3484 int lx = from_x + x + (show_level_border ? -1 : 0);
3485 int ly = from_y + y + (show_level_border ? -1 : 0);
3486 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3487 getBorderElement(lx, ly));
3489 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3490 element, tile_size);
3494 redraw_mask |= REDRAW_FIELD;
3497 #define MICROLABEL_EMPTY 0
3498 #define MICROLABEL_LEVEL_NAME 1
3499 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3500 #define MICROLABEL_LEVEL_AUTHOR 3
3501 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3502 #define MICROLABEL_IMPORTED_FROM 5
3503 #define MICROLABEL_IMPORTED_BY_HEAD 6
3504 #define MICROLABEL_IMPORTED_BY 7
3506 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3508 int max_text_width = SXSIZE;
3509 int font_width = getFontWidth(font_nr);
3511 if (pos->align == ALIGN_CENTER)
3512 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3513 else if (pos->align == ALIGN_RIGHT)
3514 max_text_width = pos->x;
3516 max_text_width = SXSIZE - pos->x;
3518 return max_text_width / font_width;
3521 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3523 char label_text[MAX_OUTPUT_LINESIZE + 1];
3524 int max_len_label_text;
3525 int font_nr = pos->font;
3528 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3531 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3532 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3533 mode == MICROLABEL_IMPORTED_BY_HEAD)
3534 font_nr = pos->font_alt;
3536 max_len_label_text = getMaxTextLength(pos, font_nr);
3538 if (pos->size != -1)
3539 max_len_label_text = pos->size;
3541 for (i = 0; i < max_len_label_text; i++)
3542 label_text[i] = ' ';
3543 label_text[max_len_label_text] = '\0';
3545 if (strlen(label_text) > 0)
3546 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3549 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3550 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3551 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3552 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3553 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3554 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3555 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3556 max_len_label_text);
3557 label_text[max_len_label_text] = '\0';
3559 if (strlen(label_text) > 0)
3560 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3562 redraw_mask |= REDRAW_FIELD;
3565 static void DrawPreviewLevelLabel(int mode)
3567 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3570 static void DrawPreviewLevelInfo(int mode)
3572 if (mode == MICROLABEL_LEVEL_NAME)
3573 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3574 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3575 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3578 static void DrawPreviewLevelExt(boolean restart)
3580 static DelayCounter scroll_delay = { 0 };
3581 static DelayCounter label_delay = { 0 };
3582 static int from_x, from_y, scroll_direction;
3583 static int label_state, label_counter;
3584 boolean show_level_border = (BorderElement != EL_EMPTY);
3585 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3586 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3588 scroll_delay.value = preview.step_delay;
3589 label_delay.value = MICROLEVEL_LABEL_DELAY;
3596 if (preview.anim_mode == ANIM_CENTERED)
3598 if (level_xsize > preview.xsize)
3599 from_x = (level_xsize - preview.xsize) / 2;
3600 if (level_ysize > preview.ysize)
3601 from_y = (level_ysize - preview.ysize) / 2;
3604 from_x += preview.xoffset;
3605 from_y += preview.yoffset;
3607 scroll_direction = MV_RIGHT;
3611 DrawPreviewLevelPlayfield(from_x, from_y);
3612 DrawPreviewLevelLabel(label_state);
3614 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3615 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3617 // initialize delay counters
3618 ResetDelayCounter(&scroll_delay);
3619 ResetDelayCounter(&label_delay);
3621 if (leveldir_current->name)
3623 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3624 char label_text[MAX_OUTPUT_LINESIZE + 1];
3625 int font_nr = pos->font;
3626 int max_len_label_text = getMaxTextLength(pos, font_nr);
3628 if (pos->size != -1)
3629 max_len_label_text = pos->size;
3631 strncpy(label_text, leveldir_current->name, max_len_label_text);
3632 label_text[max_len_label_text] = '\0';
3634 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3635 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3641 // scroll preview level, if needed
3642 if (preview.anim_mode != ANIM_NONE &&
3643 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3644 DelayReached(&scroll_delay))
3646 switch (scroll_direction)
3651 from_x -= preview.step_offset;
3652 from_x = (from_x < 0 ? 0 : from_x);
3655 scroll_direction = MV_UP;
3659 if (from_x < level_xsize - preview.xsize)
3661 from_x += preview.step_offset;
3662 from_x = (from_x > level_xsize - preview.xsize ?
3663 level_xsize - preview.xsize : from_x);
3666 scroll_direction = MV_DOWN;
3672 from_y -= preview.step_offset;
3673 from_y = (from_y < 0 ? 0 : from_y);
3676 scroll_direction = MV_RIGHT;
3680 if (from_y < level_ysize - preview.ysize)
3682 from_y += preview.step_offset;
3683 from_y = (from_y > level_ysize - preview.ysize ?
3684 level_ysize - preview.ysize : from_y);
3687 scroll_direction = MV_LEFT;
3694 DrawPreviewLevelPlayfield(from_x, from_y);
3697 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3698 // redraw micro level label, if needed
3699 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3700 !strEqual(level.author, ANONYMOUS_NAME) &&
3701 !strEqual(level.author, leveldir_current->name) &&
3702 DelayReached(&label_delay))
3704 int max_label_counter = 23;
3706 if (leveldir_current->imported_from != NULL &&
3707 strlen(leveldir_current->imported_from) > 0)
3708 max_label_counter += 14;
3709 if (leveldir_current->imported_by != NULL &&
3710 strlen(leveldir_current->imported_by) > 0)
3711 max_label_counter += 14;
3713 label_counter = (label_counter + 1) % max_label_counter;
3714 label_state = (label_counter >= 0 && label_counter <= 7 ?
3715 MICROLABEL_LEVEL_NAME :
3716 label_counter >= 9 && label_counter <= 12 ?
3717 MICROLABEL_LEVEL_AUTHOR_HEAD :
3718 label_counter >= 14 && label_counter <= 21 ?
3719 MICROLABEL_LEVEL_AUTHOR :
3720 label_counter >= 23 && label_counter <= 26 ?
3721 MICROLABEL_IMPORTED_FROM_HEAD :
3722 label_counter >= 28 && label_counter <= 35 ?
3723 MICROLABEL_IMPORTED_FROM :
3724 label_counter >= 37 && label_counter <= 40 ?
3725 MICROLABEL_IMPORTED_BY_HEAD :
3726 label_counter >= 42 && label_counter <= 49 ?
3727 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3729 if (leveldir_current->imported_from == NULL &&
3730 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3731 label_state == MICROLABEL_IMPORTED_FROM))
3732 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3733 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3735 DrawPreviewLevelLabel(label_state);
3739 void DrawPreviewPlayers(void)
3741 if (game_status != GAME_MODE_MAIN)
3744 // do not draw preview players if level preview redefined, but players aren't
3745 if (preview.redefined && !menu.main.preview_players.redefined)
3748 boolean player_found[MAX_PLAYERS];
3749 int num_players = 0;
3752 for (i = 0; i < MAX_PLAYERS; i++)
3753 player_found[i] = FALSE;
3755 // check which players can be found in the level (simple approach)
3756 for (x = 0; x < lev_fieldx; x++)
3758 for (y = 0; y < lev_fieldy; y++)
3760 int element = level.field[x][y];
3762 if (IS_PLAYER_ELEMENT(element))
3764 int player_nr = GET_PLAYER_NR(element);
3766 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3768 if (!player_found[player_nr])
3771 player_found[player_nr] = TRUE;
3776 struct TextPosInfo *pos = &menu.main.preview_players;
3777 int tile_size = pos->tile_size;
3778 int border_size = pos->border_size;
3779 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3780 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3781 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3782 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3783 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3784 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3785 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3786 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3787 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3788 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3789 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3790 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3792 // clear area in which the players will be drawn
3793 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3794 max_players_width, max_players_height);
3796 if (!network.enabled && !setup.team_mode)
3799 // only draw players if level is suited for team mode
3800 if (num_players < 2)
3803 // draw all players that were found in the level
3804 for (i = 0; i < MAX_PLAYERS; i++)
3806 if (player_found[i])
3808 int graphic = el2img(EL_PLAYER_1 + i);
3810 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3812 xpos += player_xoffset;
3813 ypos += player_yoffset;
3818 void DrawPreviewLevelInitial(void)
3820 DrawPreviewLevelExt(TRUE);
3821 DrawPreviewPlayers();
3824 void DrawPreviewLevelAnimation(void)
3826 DrawPreviewLevelExt(FALSE);
3829 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3830 int border_size, int font_nr)
3832 int graphic = el2img(EL_PLAYER_1 + player_nr);
3833 int font_height = getFontHeight(font_nr);
3834 int player_height = MAX(tile_size, font_height);
3835 int xoffset_text = tile_size + border_size;
3836 int yoffset_text = (player_height - font_height) / 2;
3837 int yoffset_graphic = (player_height - tile_size) / 2;
3838 char *player_name = getNetworkPlayerName(player_nr + 1);
3840 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3842 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3845 static void DrawNetworkPlayersExt(boolean force)
3847 if (game_status != GAME_MODE_MAIN)
3850 if (!network.connected && !force)
3853 // do not draw network players if level preview redefined, but players aren't
3854 if (preview.redefined && !menu.main.network_players.redefined)
3857 int num_players = 0;
3860 for (i = 0; i < MAX_PLAYERS; i++)
3861 if (stored_player[i].connected_network)
3864 struct TextPosInfo *pos = &menu.main.network_players;
3865 int tile_size = pos->tile_size;
3866 int border_size = pos->border_size;
3867 int xoffset_text = tile_size + border_size;
3868 int font_nr = pos->font;
3869 int font_width = getFontWidth(font_nr);
3870 int font_height = getFontHeight(font_nr);
3871 int player_height = MAX(tile_size, font_height);
3872 int player_yoffset = player_height + border_size;
3873 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3874 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3875 int all_players_height = num_players * player_yoffset - border_size;
3876 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3877 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3878 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3880 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3881 max_players_width, max_players_height);
3883 // first draw local network player ...
3884 for (i = 0; i < MAX_PLAYERS; i++)
3886 if (stored_player[i].connected_network &&
3887 stored_player[i].connected_locally)
3889 char *player_name = getNetworkPlayerName(i + 1);
3890 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3891 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3893 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3895 ypos += player_yoffset;
3899 // ... then draw all other network players
3900 for (i = 0; i < MAX_PLAYERS; i++)
3902 if (stored_player[i].connected_network &&
3903 !stored_player[i].connected_locally)
3905 char *player_name = getNetworkPlayerName(i + 1);
3906 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3907 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3909 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3911 ypos += player_yoffset;
3916 void DrawNetworkPlayers(void)
3918 DrawNetworkPlayersExt(FALSE);
3921 void ClearNetworkPlayers(void)
3923 DrawNetworkPlayersExt(TRUE);
3926 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3927 int graphic, int lx, int ly,
3930 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3932 if (mask_mode == USE_MASKING)
3933 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3935 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3938 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3939 int graphic, int sync_frame, int mask_mode)
3941 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3943 if (mask_mode == USE_MASKING)
3944 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3946 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3949 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3950 int graphic, int sync_frame, int tilesize,
3953 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3955 if (mask_mode == USE_MASKING)
3956 DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
3958 DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
3961 static void DrawGraphicAnimation(int x, int y, int graphic)
3963 int lx = LEVELX(x), ly = LEVELY(y);
3964 int mask_mode = NO_MASKING;
3966 if (!IN_SCR_FIELD(x, y))
3969 if (game.use_masked_elements)
3971 if (Tile[lx][ly] != EL_EMPTY)
3973 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3975 mask_mode = USE_MASKING;
3979 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3980 graphic, lx, ly, mask_mode);
3982 MarkTileDirty(x, y);
3985 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3987 int lx = LEVELX(x), ly = LEVELY(y);
3988 int mask_mode = NO_MASKING;
3990 if (!IN_SCR_FIELD(x, y))
3993 if (game.use_masked_elements)
3995 if (Tile[lx][ly] != EL_EMPTY)
3997 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3999 mask_mode = USE_MASKING;
4003 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
4004 graphic, lx, ly, mask_mode);
4006 MarkTileDirty(x, y);
4009 void DrawLevelGraphicAnimation(int x, int y, int graphic)
4011 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4014 void DrawLevelElementAnimation(int x, int y, int element)
4016 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4018 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4021 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
4023 int sx = SCREENX(x), sy = SCREENY(y);
4025 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4028 if (Tile[x][y] == EL_EMPTY)
4029 graphic = el2img(GfxElementEmpty[x][y]);
4031 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4034 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4037 DrawGraphicAnimation(sx, sy, graphic);
4040 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4041 DrawLevelFieldCrumbled(x, y);
4043 if (GFX_CRUMBLED(Tile[x][y]))
4044 DrawLevelFieldCrumbled(x, y);
4048 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4050 int sx = SCREENX(x), sy = SCREENY(y);
4053 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4056 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4058 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4061 DrawGraphicAnimation(sx, sy, graphic);
4063 if (GFX_CRUMBLED(element))
4064 DrawLevelFieldCrumbled(x, y);
4067 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4069 if (player->use_murphy)
4071 // this works only because currently only one player can be "murphy" ...
4072 static int last_horizontal_dir = MV_LEFT;
4073 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4075 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4076 last_horizontal_dir = move_dir;
4078 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4080 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4082 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4088 return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4091 static boolean equalGraphics(int graphic1, int graphic2)
4093 struct GraphicInfo *g1 = &graphic_info[graphic1];
4094 struct GraphicInfo *g2 = &graphic_info[graphic2];
4096 return (g1->bitmap == g2->bitmap &&
4097 g1->src_x == g2->src_x &&
4098 g1->src_y == g2->src_y &&
4099 g1->anim_frames == g2->anim_frames &&
4100 g1->anim_delay == g2->anim_delay &&
4101 g1->anim_mode == g2->anim_mode);
4104 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4108 DRAW_PLAYER_STAGE_INIT = 0,
4109 DRAW_PLAYER_STAGE_LAST_FIELD,
4110 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4111 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4112 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4113 DRAW_PLAYER_STAGE_PLAYER,
4115 DRAW_PLAYER_STAGE_PLAYER,
4116 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4118 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4119 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4121 NUM_DRAW_PLAYER_STAGES
4124 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4126 static int static_last_player_graphic[MAX_PLAYERS];
4127 static int static_last_player_frame[MAX_PLAYERS];
4128 static boolean static_player_is_opaque[MAX_PLAYERS];
4129 static boolean draw_player[MAX_PLAYERS];
4130 int pnr = player->index_nr;
4132 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4134 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4135 static_last_player_frame[pnr] = player->Frame;
4136 static_player_is_opaque[pnr] = FALSE;
4138 draw_player[pnr] = TRUE;
4141 if (!draw_player[pnr])
4145 if (!IN_LEV_FIELD(player->jx, player->jy))
4147 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4148 Debug("draw:DrawPlayerExt", "This should never happen!");
4150 draw_player[pnr] = FALSE;
4156 int last_player_graphic = static_last_player_graphic[pnr];
4157 int last_player_frame = static_last_player_frame[pnr];
4158 boolean player_is_opaque = static_player_is_opaque[pnr];
4160 int jx = player->jx;
4161 int jy = player->jy;
4162 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4163 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4164 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4165 int last_jx = (player->is_moving ? jx - dx : jx);
4166 int last_jy = (player->is_moving ? jy - dy : jy);
4167 int next_jx = jx + dx;
4168 int next_jy = jy + dy;
4169 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4170 int sx = SCREENX(jx);
4171 int sy = SCREENY(jy);
4172 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4173 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4174 int element = Tile[jx][jy];
4175 int last_element = Tile[last_jx][last_jy];
4176 int action = (player->is_pushing ? ACTION_PUSHING :
4177 player->is_digging ? ACTION_DIGGING :
4178 player->is_collecting ? ACTION_COLLECTING :
4179 player->is_moving ? ACTION_MOVING :
4180 player->is_snapping ? ACTION_SNAPPING :
4181 player->is_dropping ? ACTION_DROPPING :
4182 player->is_waiting ? player->action_waiting :
4185 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4187 // ------------------------------------------------------------------------
4188 // initialize drawing the player
4189 // ------------------------------------------------------------------------
4191 draw_player[pnr] = FALSE;
4193 // GfxElement[][] is set to the element the player is digging or collecting;
4194 // remove also for off-screen player if the player is not moving anymore
4195 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4196 GfxElement[jx][jy] = EL_UNDEFINED;
4198 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4201 if (element == EL_EXPLOSION)
4204 InitPlayerGfxAnimation(player, action, move_dir);
4206 draw_player[pnr] = TRUE;
4208 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4210 // ------------------------------------------------------------------------
4211 // draw things in the field the player is leaving, if needed
4212 // ------------------------------------------------------------------------
4214 if (!IN_SCR_FIELD(sx, sy))
4215 draw_player[pnr] = FALSE;
4217 if (!player->is_moving)
4220 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4222 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4224 if (last_element == EL_DYNAMITE_ACTIVE ||
4225 last_element == EL_EM_DYNAMITE_ACTIVE ||
4226 last_element == EL_SP_DISK_RED_ACTIVE)
4227 DrawDynamite(last_jx, last_jy);
4229 DrawLevelFieldThruMask(last_jx, last_jy);
4231 else if (last_element == EL_DYNAMITE_ACTIVE ||
4232 last_element == EL_EM_DYNAMITE_ACTIVE ||
4233 last_element == EL_SP_DISK_RED_ACTIVE)
4234 DrawDynamite(last_jx, last_jy);
4236 DrawLevelField(last_jx, last_jy);
4238 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4240 // ------------------------------------------------------------------------
4241 // draw things behind the player, if needed
4242 // ------------------------------------------------------------------------
4246 DrawLevelElement(jx, jy, Back[jx][jy]);
4251 if (IS_ACTIVE_BOMB(element))
4253 DrawLevelElement(jx, jy, EL_EMPTY);
4258 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4260 int old_element = GfxElement[jx][jy];
4261 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4262 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4264 if (GFX_CRUMBLED(old_element))
4265 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4267 DrawScreenGraphic(sx, sy, old_graphic, frame);
4269 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4270 static_player_is_opaque[pnr] = TRUE;
4274 GfxElement[jx][jy] = EL_UNDEFINED;
4276 // make sure that pushed elements are drawn with correct frame rate
4277 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4279 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4280 GfxFrame[jx][jy] = player->StepFrame;
4282 DrawLevelField(jx, jy);
4285 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4287 // ------------------------------------------------------------------------
4288 // draw things the player is pushing, if needed
4289 // ------------------------------------------------------------------------
4291 if (!player->is_pushing || !player->is_moving)
4294 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4297 int gfx_frame = GfxFrame[jx][jy];
4299 if (!IS_MOVING(jx, jy)) // push movement already finished
4301 element = Tile[next_jx][next_jy];
4302 gfx_frame = GfxFrame[next_jx][next_jy];
4305 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4306 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4307 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4309 // draw background element under pushed element (like the Sokoban field)
4310 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4312 // this allows transparent pushing animation over non-black background
4315 DrawLevelElement(jx, jy, Back[jx][jy]);
4317 DrawLevelElement(jx, jy, EL_EMPTY);
4320 if (Back[next_jx][next_jy])
4321 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4323 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4325 int px = SCREENX(jx), py = SCREENY(jy);
4326 int pxx = (TILEX - ABS(sxx)) * dx;
4327 int pyy = (TILEY - ABS(syy)) * dy;
4330 // do not draw (EM style) pushing animation when pushing is finished
4331 // (two-tile animations usually do not contain start and end frame)
4332 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4333 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4335 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4337 // masked drawing is needed for EMC style (double) movement graphics
4338 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4339 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4342 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4344 // ------------------------------------------------------------------------
4345 // draw player himself
4346 // ------------------------------------------------------------------------
4348 int graphic = getPlayerGraphic(player, move_dir);
4350 // in the case of changed player action or direction, prevent the current
4351 // animation frame from being restarted for identical animations
4352 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4353 player->Frame = last_player_frame;
4355 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4357 if (player_is_opaque)
4358 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4360 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4362 if (SHIELD_ON(player))
4364 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4365 IMG_SHIELD_NORMAL_ACTIVE);
4366 frame = getGraphicAnimationFrame(graphic, -1);
4368 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4371 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4373 // ------------------------------------------------------------------------
4374 // draw things in front of player (active dynamite or dynabombs)
4375 // ------------------------------------------------------------------------
4377 if (IS_ACTIVE_BOMB(element))
4379 int graphic = el2img(element);
4380 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4382 if (game.emulation == EMU_SUPAPLEX)
4383 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4385 DrawGraphicThruMask(sx, sy, graphic, frame);
4388 if (player_is_moving && last_element == EL_EXPLOSION)
4390 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4391 GfxElement[last_jx][last_jy] : EL_EMPTY);
4392 int graphic = el_act2img(element, ACTION_EXPLODING);
4393 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4394 int phase = ExplodePhase[last_jx][last_jy] - 1;
4395 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4398 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4401 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4403 // ------------------------------------------------------------------------
4404 // draw elements the player is just walking/passing through/under
4405 // ------------------------------------------------------------------------
4407 if (player_is_moving)
4409 // handle the field the player is leaving ...
4410 if (IS_ACCESSIBLE_INSIDE(last_element))
4411 DrawLevelField(last_jx, last_jy);
4412 else if (IS_ACCESSIBLE_UNDER(last_element))
4413 DrawLevelFieldThruMask(last_jx, last_jy);
4416 // do not redraw accessible elements if the player is just pushing them
4417 if (!player_is_moving || !player->is_pushing)
4419 // ... and the field the player is entering
4420 if (IS_ACCESSIBLE_INSIDE(element))
4421 DrawLevelField(jx, jy);
4422 else if (IS_ACCESSIBLE_UNDER(element))
4423 DrawLevelFieldThruMask(jx, jy);
4426 MarkTileDirty(sx, sy);
4430 void DrawPlayer(struct PlayerInfo *player)
4434 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4435 DrawPlayerExt(player, i);
4438 void DrawAllPlayers(void)
4442 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4443 for (j = 0; j < MAX_PLAYERS; j++)
4444 if (stored_player[j].active)
4445 DrawPlayerExt(&stored_player[j], i);
4448 void DrawPlayerField(int x, int y)
4450 if (!IS_PLAYER(x, y))
4453 DrawPlayer(PLAYERINFO(x, y));
4456 // ----------------------------------------------------------------------------
4458 void WaitForEventToContinue(void)
4460 boolean first_wait = TRUE;
4461 boolean still_wait = TRUE;
4463 if (program.headless)
4466 // simulate releasing mouse button over last gadget, if still pressed
4468 HandleGadgets(-1, -1, 0);
4470 button_status = MB_RELEASED;
4473 ClearPlayerAction();
4479 if (NextValidEvent(&event))
4483 case EVENT_BUTTONPRESS:
4484 case EVENT_FINGERPRESS:
4488 case EVENT_BUTTONRELEASE:
4489 case EVENT_FINGERRELEASE:
4490 still_wait = first_wait;
4493 case EVENT_KEYPRESS:
4494 case SDL_CONTROLLERBUTTONDOWN:
4495 case SDL_JOYBUTTONDOWN:
4500 HandleOtherEvents(&event);
4504 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4509 if (!PendingEvent())
4514 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4516 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4517 int draw_buffer_last = GetDrawtoField();
4518 int width = request.width;
4519 int height = request.height;
4523 setRequestPosition(&sx, &sy, FALSE);
4525 button_status = MB_RELEASED;
4527 request_gadget_id = -1;
4534 SetDrawtoField(draw_buffer_game);
4536 HandleGameActions();
4538 SetDrawtoField(DRAW_TO_BACKBUFFER);
4545 while (NextValidEvent(&event))
4549 case EVENT_BUTTONPRESS:
4550 case EVENT_BUTTONRELEASE:
4551 case EVENT_MOTIONNOTIFY:
4553 DrawBuffer *drawto_last = drawto;
4556 if (event.type == EVENT_MOTIONNOTIFY)
4561 motion_status = TRUE;
4562 mx = ((MotionEvent *) &event)->x;
4563 my = ((MotionEvent *) &event)->y;
4567 motion_status = FALSE;
4568 mx = ((ButtonEvent *) &event)->x;
4569 my = ((ButtonEvent *) &event)->y;
4570 if (event.type == EVENT_BUTTONPRESS)
4571 button_status = ((ButtonEvent *) &event)->button;
4573 button_status = MB_RELEASED;
4576 if (global.use_envelope_request)
4578 // draw changed button states to temporary bitmap
4579 drawto = bitmap_db_store_1;
4582 // this sets 'request_gadget_id'
4583 HandleGadgets(mx, my, button_status);
4585 if (global.use_envelope_request)
4587 // restore pointer to drawing buffer
4588 drawto = drawto_last;
4590 // prepare complete envelope request from temporary bitmap
4591 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy,
4595 switch (request_gadget_id)
4597 case TOOL_CTRL_ID_YES:
4598 case TOOL_CTRL_ID_TOUCH_YES:
4601 case TOOL_CTRL_ID_NO:
4602 case TOOL_CTRL_ID_TOUCH_NO:
4605 case TOOL_CTRL_ID_CONFIRM:
4606 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4607 result = TRUE | FALSE;
4610 case TOOL_CTRL_ID_PLAYER_1:
4613 case TOOL_CTRL_ID_PLAYER_2:
4616 case TOOL_CTRL_ID_PLAYER_3:
4619 case TOOL_CTRL_ID_PLAYER_4:
4627 // only needed to handle clickable pointer animations here
4628 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4633 case SDL_WINDOWEVENT:
4634 HandleWindowEvent((WindowEvent *) &event);
4637 case SDL_APP_WILLENTERBACKGROUND:
4638 case SDL_APP_DIDENTERBACKGROUND:
4639 case SDL_APP_WILLENTERFOREGROUND:
4640 case SDL_APP_DIDENTERFOREGROUND:
4641 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4644 case EVENT_KEYPRESS:
4646 Key key = GetEventKey((KeyEvent *)&event);
4651 if (req_state & REQ_CONFIRM)
4660 #if defined(KSYM_Rewind)
4661 case KSYM_Rewind: // for Amazon Fire TV remote
4670 #if defined(KSYM_FastForward)
4671 case KSYM_FastForward: // for Amazon Fire TV remote
4677 HandleKeysDebug(key, KEY_PRESSED);
4681 if (req_state & REQ_PLAYER)
4683 int old_player_nr = setup.network_player_nr;
4686 result = old_player_nr + 1;
4691 result = old_player_nr + 1;
4722 case EVENT_FINGERRELEASE:
4723 case EVENT_KEYRELEASE:
4724 ClearPlayerAction();
4727 case SDL_CONTROLLERBUTTONDOWN:
4728 switch (event.cbutton.button)
4730 case SDL_CONTROLLER_BUTTON_A:
4731 case SDL_CONTROLLER_BUTTON_X:
4732 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4733 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4737 case SDL_CONTROLLER_BUTTON_B:
4738 case SDL_CONTROLLER_BUTTON_Y:
4739 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4740 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4741 case SDL_CONTROLLER_BUTTON_BACK:
4746 if (req_state & REQ_PLAYER)
4748 int old_player_nr = setup.network_player_nr;
4751 result = old_player_nr + 1;
4753 switch (event.cbutton.button)
4755 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4756 case SDL_CONTROLLER_BUTTON_Y:
4760 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4761 case SDL_CONTROLLER_BUTTON_B:
4765 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4766 case SDL_CONTROLLER_BUTTON_A:
4770 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4771 case SDL_CONTROLLER_BUTTON_X:
4782 case SDL_CONTROLLERBUTTONUP:
4783 HandleJoystickEvent(&event);
4784 ClearPlayerAction();
4788 HandleOtherEvents(&event);
4793 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4795 int joy = AnyJoystick();
4797 if (joy & JOY_BUTTON_1)
4799 else if (joy & JOY_BUTTON_2)
4802 else if (AnyJoystick())
4804 int joy = AnyJoystick();
4806 if (req_state & REQ_PLAYER)
4810 else if (joy & JOY_RIGHT)
4812 else if (joy & JOY_DOWN)
4814 else if (joy & JOY_LEFT)
4822 SetDrawtoField(draw_buffer_last);
4827 static void DoRequestBefore(void)
4829 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4831 // when showing request dialog after game ended, deactivate game panel
4833 game.panel.active = FALSE;
4835 if (game_status == GAME_MODE_PLAYING)
4836 BlitScreenToBitmap(backbuffer);
4838 // disable deactivated drawing when quick-loading level tape recording
4839 if (tape.playing && tape.deactivate_display)
4840 TapeDeactivateDisplayOff(TRUE);
4842 SetMouseCursor(CURSOR_DEFAULT);
4844 // pause network game while waiting for request to answer
4845 if (network.enabled &&
4846 game_status == GAME_MODE_PLAYING &&
4847 !game.all_players_gone)
4848 SendToServer_PausePlaying();
4850 // simulate releasing mouse button over last gadget, if still pressed
4852 HandleGadgets(-1, -1, 0);
4857 static void DoRequestAfter(void)
4861 if (game_status == GAME_MODE_PLAYING)
4863 SetPanelBackground();
4864 SetDrawBackgroundMask(REDRAW_DOOR_1);
4868 SetDrawBackgroundMask(REDRAW_FIELD);
4871 // continue network game after request
4872 if (network.enabled &&
4873 game_status == GAME_MODE_PLAYING &&
4874 !game.all_players_gone)
4875 SendToServer_ContinuePlaying();
4877 // restore deactivated drawing when quick-loading level tape recording
4878 if (tape.playing && tape.deactivate_display)
4879 TapeDeactivateDisplayOn();
4882 static void setRequestDoorTextProperties(char *text,
4887 int *set_max_line_length)
4889 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
4890 struct TextPosInfo *pos = &request.button.confirm;
4891 int button_ypos = pos->y;
4892 int font_nr = FONT_TEXT_2;
4893 int font_width = getFontWidth(font_nr);
4894 int font_height = getFontHeight(font_nr);
4895 int line_height = font_height + line_spacing;
4896 int max_text_width = vp_door_1->width;
4897 int max_text_height = button_ypos - 2 * text_spacing;
4898 int max_line_length = max_text_width / font_width;
4899 int max_lines = max_text_height / line_height;
4901 if (maxWordLengthInRequestString(text) > max_line_length)
4903 font_nr = FONT_TEXT_1;
4904 font_width = getFontWidth(font_nr);
4905 max_line_length = max_text_width / font_width;
4908 *set_font_nr = font_nr;
4909 *set_max_lines = max_lines;
4910 *set_max_line_length = max_line_length;
4913 static void DrawRequestDoorText(char *text)
4915 char *text_ptr = text;
4916 int text_spacing = 8;
4917 int line_spacing = 2;
4918 int max_request_lines;
4919 int max_request_line_len;
4923 // force DOOR font inside door area
4924 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4926 setRequestDoorTextProperties(text, text_spacing, line_spacing, &font_nr,
4927 &max_request_lines, &max_request_line_len);
4929 for (text_ptr = text, ty = 0; ty < max_request_lines; ty++)
4931 char text_line[max_request_line_len + 1];
4937 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4939 tc = *(text_ptr + tx);
4940 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4944 if ((tc == '?' || tc == '!') && tl == 0)
4954 strncpy(text_line, text_ptr, tl);
4957 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4958 DY + text_spacing + ty * (getFontHeight(font_nr) + line_spacing),
4959 text_line, font_nr);
4961 text_ptr += tl + (tc == ' ' ? 1 : 0);
4967 static int RequestDoor(char *text, unsigned int req_state)
4969 unsigned int old_door_state = GetDoorState();
4970 int draw_buffer_last = GetDrawtoField();
4973 if (old_door_state & DOOR_OPEN_1)
4975 CloseDoor(DOOR_CLOSE_1);
4977 // save old door content
4978 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4979 0, 0, DXSIZE, DYSIZE, DXSIZE, 0);
4982 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4983 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4985 // clear door drawing field
4986 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4988 // write text for request
4989 DrawRequestDoorText(text);
4991 MapToolButtons(req_state);
4993 // copy request gadgets to door backbuffer
4994 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4996 OpenDoor(DOOR_OPEN_1);
4998 // ---------- handle request buttons ----------
4999 result = RequestHandleEvents(req_state, draw_buffer_last);
5003 if (!(req_state & REQ_STAY_OPEN))
5005 CloseDoor(DOOR_CLOSE_1);
5007 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
5008 (req_state & REQ_REOPEN))
5009 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
5015 static int RequestEnvelope(char *text, unsigned int req_state)
5017 int draw_buffer_last = GetDrawtoField();
5020 DrawEnvelopeRequest(text, req_state);
5021 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5023 // ---------- handle request buttons ----------
5024 result = RequestHandleEvents(req_state, draw_buffer_last);
5028 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5033 int Request(char *text, unsigned int req_state)
5035 boolean overlay_enabled = GetOverlayEnabled();
5038 game.request_active = TRUE;
5040 SetOverlayEnabled(FALSE);
5044 if (global.use_envelope_request)
5045 result = RequestEnvelope(text, req_state);
5047 result = RequestDoor(text, req_state);
5051 SetOverlayEnabled(overlay_enabled);
5053 game.request_active = FALSE;
5058 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5060 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5061 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5064 if (dpo1->sort_priority != dpo2->sort_priority)
5065 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5067 compare_result = dpo1->nr - dpo2->nr;
5069 return compare_result;
5072 void InitGraphicCompatibilityInfo_Doors(void)
5078 struct DoorInfo *door;
5082 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5083 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5085 { -1, -1, -1, NULL }
5087 struct Rect door_rect_list[] =
5089 { DX, DY, DXSIZE, DYSIZE },
5090 { VX, VY, VXSIZE, VYSIZE }
5094 for (i = 0; doors[i].door_token != -1; i++)
5096 int door_token = doors[i].door_token;
5097 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5098 int part_1 = doors[i].part_1;
5099 int part_8 = doors[i].part_8;
5100 int part_2 = part_1 + 1;
5101 int part_3 = part_1 + 2;
5102 struct DoorInfo *door = doors[i].door;
5103 struct Rect *door_rect = &door_rect_list[door_index];
5104 boolean door_gfx_redefined = FALSE;
5106 // check if any door part graphic definitions have been redefined
5108 for (j = 0; door_part_controls[j].door_token != -1; j++)
5110 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5111 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5113 if (dpc->door_token == door_token && fi->redefined)
5114 door_gfx_redefined = TRUE;
5117 // check for old-style door graphic/animation modifications
5119 if (!door_gfx_redefined)
5121 if (door->anim_mode & ANIM_STATIC_PANEL)
5123 door->panel.step_xoffset = 0;
5124 door->panel.step_yoffset = 0;
5127 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5129 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5130 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5131 int num_door_steps, num_panel_steps;
5133 // remove door part graphics other than the two default wings
5135 for (j = 0; door_part_controls[j].door_token != -1; j++)
5137 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5138 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5140 if (dpc->graphic >= part_3 &&
5141 dpc->graphic <= part_8)
5145 // set graphics and screen positions of the default wings
5147 g_part_1->width = door_rect->width;
5148 g_part_1->height = door_rect->height;
5149 g_part_2->width = door_rect->width;
5150 g_part_2->height = door_rect->height;
5151 g_part_2->src_x = door_rect->width;
5152 g_part_2->src_y = g_part_1->src_y;
5154 door->part_2.x = door->part_1.x;
5155 door->part_2.y = door->part_1.y;
5157 if (door->width != -1)
5159 g_part_1->width = door->width;
5160 g_part_2->width = door->width;
5162 // special treatment for graphics and screen position of right wing
5163 g_part_2->src_x += door_rect->width - door->width;
5164 door->part_2.x += door_rect->width - door->width;
5167 if (door->height != -1)
5169 g_part_1->height = door->height;
5170 g_part_2->height = door->height;
5172 // special treatment for graphics and screen position of bottom wing
5173 g_part_2->src_y += door_rect->height - door->height;
5174 door->part_2.y += door_rect->height - door->height;
5177 // set animation delays for the default wings and panels
5179 door->part_1.step_delay = door->step_delay;
5180 door->part_2.step_delay = door->step_delay;
5181 door->panel.step_delay = door->step_delay;
5183 // set animation draw order for the default wings
5185 door->part_1.sort_priority = 2; // draw left wing over ...
5186 door->part_2.sort_priority = 1; // ... right wing
5188 // set animation draw offset for the default wings
5190 if (door->anim_mode & ANIM_HORIZONTAL)
5192 door->part_1.step_xoffset = door->step_offset;
5193 door->part_1.step_yoffset = 0;
5194 door->part_2.step_xoffset = door->step_offset * -1;
5195 door->part_2.step_yoffset = 0;
5197 num_door_steps = g_part_1->width / door->step_offset;
5199 else // ANIM_VERTICAL
5201 door->part_1.step_xoffset = 0;
5202 door->part_1.step_yoffset = door->step_offset;
5203 door->part_2.step_xoffset = 0;
5204 door->part_2.step_yoffset = door->step_offset * -1;
5206 num_door_steps = g_part_1->height / door->step_offset;
5209 // set animation draw offset for the default panels
5211 if (door->step_offset > 1)
5213 num_panel_steps = 2 * door_rect->height / door->step_offset;
5214 door->panel.start_step = num_panel_steps - num_door_steps;
5215 door->panel.start_step_closing = door->panel.start_step;
5219 num_panel_steps = door_rect->height / door->step_offset;
5220 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5221 door->panel.start_step_closing = door->panel.start_step;
5222 door->panel.step_delay *= 2;
5229 void InitDoors(void)
5233 for (i = 0; door_part_controls[i].door_token != -1; i++)
5235 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5236 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5238 // initialize "start_step_opening" and "start_step_closing", if needed
5239 if (dpc->pos->start_step_opening == 0 &&
5240 dpc->pos->start_step_closing == 0)
5242 // dpc->pos->start_step_opening = dpc->pos->start_step;
5243 dpc->pos->start_step_closing = dpc->pos->start_step;
5246 // fill structure for door part draw order (sorted below)
5248 dpo->sort_priority = dpc->pos->sort_priority;
5251 // sort door part controls according to sort_priority and graphic number
5252 qsort(door_part_order, MAX_DOOR_PARTS,
5253 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5256 unsigned int OpenDoor(unsigned int door_state)
5258 if (door_state & DOOR_COPY_BACK)
5260 if (door_state & DOOR_OPEN_1)
5261 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5262 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5264 if (door_state & DOOR_OPEN_2)
5265 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5266 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5268 door_state &= ~DOOR_COPY_BACK;
5271 return MoveDoor(door_state);
5274 unsigned int CloseDoor(unsigned int door_state)
5276 unsigned int old_door_state = GetDoorState();
5278 if (!(door_state & DOOR_NO_COPY_BACK))
5280 if (old_door_state & DOOR_OPEN_1)
5281 BlitBitmap(backbuffer, bitmap_db_door_1,
5282 DX, DY, DXSIZE, DYSIZE, 0, 0);
5284 if (old_door_state & DOOR_OPEN_2)
5285 BlitBitmap(backbuffer, bitmap_db_door_2,
5286 VX, VY, VXSIZE, VYSIZE, 0, 0);
5288 door_state &= ~DOOR_NO_COPY_BACK;
5291 return MoveDoor(door_state);
5294 unsigned int GetDoorState(void)
5296 return MoveDoor(DOOR_GET_STATE);
5299 unsigned int SetDoorState(unsigned int door_state)
5301 return MoveDoor(door_state | DOOR_SET_STATE);
5304 static int euclid(int a, int b)
5306 return (b ? euclid(b, a % b) : a);
5309 unsigned int MoveDoor(unsigned int door_state)
5311 struct Rect door_rect_list[] =
5313 { DX, DY, DXSIZE, DYSIZE },
5314 { VX, VY, VXSIZE, VYSIZE }
5316 static int door1 = DOOR_CLOSE_1;
5317 static int door2 = DOOR_CLOSE_2;
5318 DelayCounter door_delay = { 0 };
5321 if (door_state == DOOR_GET_STATE)
5322 return (door1 | door2);
5324 if (door_state & DOOR_SET_STATE)
5326 if (door_state & DOOR_ACTION_1)
5327 door1 = door_state & DOOR_ACTION_1;
5328 if (door_state & DOOR_ACTION_2)
5329 door2 = door_state & DOOR_ACTION_2;
5331 return (door1 | door2);
5334 if (!(door_state & DOOR_FORCE_REDRAW))
5336 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5337 door_state &= ~DOOR_OPEN_1;
5338 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5339 door_state &= ~DOOR_CLOSE_1;
5340 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5341 door_state &= ~DOOR_OPEN_2;
5342 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5343 door_state &= ~DOOR_CLOSE_2;
5346 if (global.autoplay_leveldir)
5348 door_state |= DOOR_NO_DELAY;
5349 door_state &= ~DOOR_CLOSE_ALL;
5352 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5353 door_state |= DOOR_NO_DELAY;
5355 if (door_state & DOOR_ACTION)
5357 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
5358 boolean door_panel_drawn[NUM_DOORS];
5359 boolean panel_has_doors[NUM_DOORS];
5360 boolean door_part_skip[MAX_DOOR_PARTS];
5361 boolean door_part_done[MAX_DOOR_PARTS];
5362 boolean door_part_done_all;
5363 int num_steps[MAX_DOOR_PARTS];
5364 int max_move_delay = 0; // delay for complete animations of all doors
5365 int max_step_delay = 0; // delay (ms) between two animation frames
5366 int num_move_steps = 0; // number of animation steps for all doors
5367 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5368 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5372 for (i = 0; i < NUM_DOORS; i++)
5373 panel_has_doors[i] = FALSE;
5375 for (i = 0; i < MAX_DOOR_PARTS; i++)
5377 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5378 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5379 int door_token = dpc->door_token;
5381 door_part_done[i] = FALSE;
5382 door_part_skip[i] = (!(door_state & door_token) ||
5386 for (i = 0; i < MAX_DOOR_PARTS; i++)
5388 int nr = door_part_order[i].nr;
5389 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5390 struct DoorPartPosInfo *pos = dpc->pos;
5391 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5392 int door_token = dpc->door_token;
5393 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5394 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5395 int step_xoffset = ABS(pos->step_xoffset);
5396 int step_yoffset = ABS(pos->step_yoffset);
5397 int step_delay = pos->step_delay;
5398 int current_door_state = door_state & door_token;
5399 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5400 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5401 boolean part_opening = (is_panel ? door_closing : door_opening);
5402 int start_step = (part_opening ? pos->start_step_opening :
5403 pos->start_step_closing);
5404 float move_xsize = (step_xoffset ? g->width : 0);
5405 float move_ysize = (step_yoffset ? g->height : 0);
5406 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5407 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5408 int move_steps = (move_xsteps && move_ysteps ?
5409 MIN(move_xsteps, move_ysteps) :
5410 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5411 int move_delay = move_steps * step_delay;
5413 if (door_part_skip[nr])
5416 max_move_delay = MAX(max_move_delay, move_delay);
5417 max_step_delay = (max_step_delay == 0 ? step_delay :
5418 euclid(max_step_delay, step_delay));
5419 num_steps[nr] = move_steps;
5423 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5425 panel_has_doors[door_index] = TRUE;
5429 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5431 num_move_steps = max_move_delay / max_step_delay;
5432 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5434 door_delay.value = max_step_delay;
5436 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5438 start = num_move_steps - 1;
5442 // opening door sound has priority over simultaneously closing door
5443 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5445 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5447 if (door_state & DOOR_OPEN_1)
5448 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5449 if (door_state & DOOR_OPEN_2)
5450 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5452 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5454 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5456 if (door_state & DOOR_CLOSE_1)
5457 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5458 if (door_state & DOOR_CLOSE_2)
5459 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5463 for (k = start; k < num_move_steps; k++)
5465 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5467 door_part_done_all = TRUE;
5469 for (i = 0; i < NUM_DOORS; i++)
5470 door_panel_drawn[i] = FALSE;
5472 for (i = 0; i < MAX_DOOR_PARTS; i++)
5474 int nr = door_part_order[i].nr;
5475 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5476 struct DoorPartPosInfo *pos = dpc->pos;
5477 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5478 int door_token = dpc->door_token;
5479 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5480 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5481 boolean is_panel_and_door_has_closed = FALSE;
5482 struct Rect *door_rect = &door_rect_list[door_index];
5483 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5485 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5486 int current_door_state = door_state & door_token;
5487 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5488 boolean door_closing = !door_opening;
5489 boolean part_opening = (is_panel ? door_closing : door_opening);
5490 boolean part_closing = !part_opening;
5491 int start_step = (part_opening ? pos->start_step_opening :
5492 pos->start_step_closing);
5493 int step_delay = pos->step_delay;
5494 int step_factor = step_delay / max_step_delay;
5495 int k1 = (step_factor ? k / step_factor + 1 : k);
5496 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5497 int kk = MAX(0, k2);
5500 int src_x, src_y, src_xx, src_yy;
5501 int dst_x, dst_y, dst_xx, dst_yy;
5504 if (door_part_skip[nr])
5507 if (!(door_state & door_token))
5515 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5516 int kk_door = MAX(0, k2_door);
5517 int sync_frame = kk_door * door_delay.value;
5518 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5520 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5521 &g_src_x, &g_src_y);
5526 if (!door_panel_drawn[door_index])
5528 ClearRectangle(drawto, door_rect->x, door_rect->y,
5529 door_rect->width, door_rect->height);
5531 door_panel_drawn[door_index] = TRUE;
5534 // draw opening or closing door parts
5536 if (pos->step_xoffset < 0) // door part on right side
5539 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5542 if (dst_xx + width > door_rect->width)
5543 width = door_rect->width - dst_xx;
5545 else // door part on left side
5548 dst_xx = pos->x - kk * pos->step_xoffset;
5552 src_xx = ABS(dst_xx);
5556 width = g->width - src_xx;
5558 if (width > door_rect->width)
5559 width = door_rect->width;
5561 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5564 if (pos->step_yoffset < 0) // door part on bottom side
5567 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5570 if (dst_yy + height > door_rect->height)
5571 height = door_rect->height - dst_yy;
5573 else // door part on top side
5576 dst_yy = pos->y - kk * pos->step_yoffset;
5580 src_yy = ABS(dst_yy);
5584 height = g->height - src_yy;
5587 src_x = g_src_x + src_xx;
5588 src_y = g_src_y + src_yy;
5590 dst_x = door_rect->x + dst_xx;
5591 dst_y = door_rect->y + dst_yy;
5593 is_panel_and_door_has_closed =
5596 panel_has_doors[door_index] &&
5597 k >= num_move_steps_doors_only - 1);
5599 if (width >= 0 && width <= g->width &&
5600 height >= 0 && height <= g->height &&
5601 !is_panel_and_door_has_closed)
5603 if (is_panel || !pos->draw_masked)
5604 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5607 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5611 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5613 if ((part_opening && (width < 0 || height < 0)) ||
5614 (part_closing && (width >= g->width && height >= g->height)))
5615 door_part_done[nr] = TRUE;
5617 // continue door part animations, but not panel after door has closed
5618 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5619 door_part_done_all = FALSE;
5622 if (!(door_state & DOOR_NO_DELAY))
5625 HandleGameActions();
5629 SkipUntilDelayReached(&door_delay, &k, last_frame);
5631 // prevent OS (Windows) from complaining about program not responding
5635 if (door_part_done_all)
5639 if (!(door_state & DOOR_NO_DELAY))
5641 // wait for specified door action post delay
5642 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5643 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5644 else if (door_state & DOOR_ACTION_1)
5645 door_delay.value = door_1.post_delay;
5646 else if (door_state & DOOR_ACTION_2)
5647 door_delay.value = door_2.post_delay;
5649 while (!DelayReached(&door_delay))
5652 HandleGameActions();
5659 if (door_state & DOOR_ACTION_1)
5660 door1 = door_state & DOOR_ACTION_1;
5661 if (door_state & DOOR_ACTION_2)
5662 door2 = door_state & DOOR_ACTION_2;
5664 // draw masked border over door area
5665 DrawMaskedBorder(REDRAW_DOOR_1);
5666 DrawMaskedBorder(REDRAW_DOOR_2);
5668 ClearAutoRepeatKeyEvents();
5670 return (door1 | door2);
5673 static boolean useSpecialEditorDoor(void)
5675 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5676 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5678 // do not draw special editor door if editor border defined or redefined
5679 if (graphic_info[graphic].bitmap != NULL || redefined)
5682 // do not draw special editor door if global border defined to be empty
5683 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5686 // do not draw special editor door if viewport definitions do not match
5690 EY + EYSIZE != VY + VYSIZE)
5696 void DrawSpecialEditorDoor(void)
5698 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5699 int top_border_width = gfx1->width;
5700 int top_border_height = gfx1->height;
5701 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5702 int ex = EX - outer_border;
5703 int ey = EY - outer_border;
5704 int vy = VY - outer_border;
5705 int exsize = EXSIZE + 2 * outer_border;
5707 if (!useSpecialEditorDoor())
5710 // draw bigger level editor toolbox window
5711 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5712 top_border_width, top_border_height, ex, ey - top_border_height);
5713 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5714 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5716 redraw_mask |= REDRAW_ALL;
5719 void UndrawSpecialEditorDoor(void)
5721 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5722 int top_border_width = gfx1->width;
5723 int top_border_height = gfx1->height;
5724 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5725 int ex = EX - outer_border;
5726 int ey = EY - outer_border;
5727 int ey_top = ey - top_border_height;
5728 int exsize = EXSIZE + 2 * outer_border;
5729 int eysize = EYSIZE + 2 * outer_border;
5731 if (!useSpecialEditorDoor())
5734 // draw normal tape recorder window
5735 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5737 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5738 ex, ey_top, top_border_width, top_border_height,
5740 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5741 ex, ey, exsize, eysize, ex, ey);
5745 // if screen background is set to "[NONE]", clear editor toolbox window
5746 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5747 ClearRectangle(drawto, ex, ey, exsize, eysize);
5750 redraw_mask |= REDRAW_ALL;
5754 // ---------- new tool button stuff -------------------------------------------
5759 struct TextPosInfo *pos;
5761 boolean is_touch_button;
5763 } toolbutton_info[NUM_TOOL_BUTTONS] =
5766 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5767 TOOL_CTRL_ID_YES, FALSE, "yes"
5770 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5771 TOOL_CTRL_ID_NO, FALSE, "no"
5774 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5775 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5778 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5779 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5782 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5783 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5786 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5787 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5790 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5791 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5794 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5795 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5798 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5799 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5802 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5803 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5807 void CreateToolButtons(void)
5811 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5813 int graphic = toolbutton_info[i].graphic;
5814 struct GraphicInfo *gfx = &graphic_info[graphic];
5815 struct TextPosInfo *pos = toolbutton_info[i].pos;
5816 struct GadgetInfo *gi;
5817 Bitmap *deco_bitmap = None;
5818 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5819 unsigned int event_mask = GD_EVENT_RELEASED;
5820 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5821 int base_x = (is_touch_button ? 0 : DX);
5822 int base_y = (is_touch_button ? 0 : DY);
5823 int gd_x = gfx->src_x;
5824 int gd_y = gfx->src_y;
5825 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5826 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5831 // do not use touch buttons if overlay touch buttons are disabled
5832 if (is_touch_button && !setup.touch.overlay_buttons)
5835 if (global.use_envelope_request && !is_touch_button)
5837 setRequestPosition(&base_x, &base_y, TRUE);
5839 // check if request buttons are outside of envelope and fix, if needed
5840 if (x < 0 || x + gfx->width > request.width ||
5841 y < 0 || y + gfx->height > request.height)
5843 if (id == TOOL_CTRL_ID_YES)
5846 y = request.height - 2 * request.border_size - gfx->height;
5848 else if (id == TOOL_CTRL_ID_NO)
5850 x = request.width - 2 * request.border_size - gfx->width;
5851 y = request.height - 2 * request.border_size - gfx->height;
5853 else if (id == TOOL_CTRL_ID_CONFIRM)
5855 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5856 y = request.height - 2 * request.border_size - gfx->height;
5858 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5860 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5862 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5863 y = request.height - 2 * request.border_size - gfx->height * 2;
5865 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5866 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5871 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5874 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5876 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5877 pos->size, &deco_bitmap, &deco_x, &deco_y);
5878 deco_xpos = (gfx->width - pos->size) / 2;
5879 deco_ypos = (gfx->height - pos->size) / 2;
5882 gi = CreateGadget(GDI_CUSTOM_ID, id,
5883 GDI_IMAGE_ID, graphic,
5884 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5887 GDI_WIDTH, gfx->width,
5888 GDI_HEIGHT, gfx->height,
5889 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5890 GDI_STATE, GD_BUTTON_UNPRESSED,
5891 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5892 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5893 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5894 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5895 GDI_DECORATION_SIZE, pos->size, pos->size,
5896 GDI_DECORATION_SHIFTING, 1, 1,
5897 GDI_DIRECT_DRAW, FALSE,
5898 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5899 GDI_EVENT_MASK, event_mask,
5900 GDI_CALLBACK_ACTION, HandleToolButtons,
5904 Fail("cannot create gadget");
5906 tool_gadget[id] = gi;
5910 void FreeToolButtons(void)
5914 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5915 FreeGadget(tool_gadget[i]);
5918 static void MapToolButtons(unsigned int req_state)
5920 if (req_state & REQ_ASK)
5922 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
5923 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
5924 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
5925 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
5927 else if (req_state & REQ_CONFIRM)
5929 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
5930 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
5932 else if (req_state & REQ_PLAYER)
5934 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
5935 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
5936 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
5937 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
5941 static void UnmapToolButtons(void)
5945 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5946 UnmapGadget(tool_gadget[i]);
5949 static void HandleToolButtons(struct GadgetInfo *gi)
5951 request_gadget_id = gi->custom_id;
5954 static struct Mapping_BD_to_RND_object
5957 boolean is_rnd_to_bd_mapping; // unique mapping BD <-> RND
5963 bd_object_mapping_list[] =
5965 // additional RND style elements mapped to BD style elements must be listed first
5981 EL_STEELWALL, -1, -1
5985 EL_BD_DIAMOND, -1, -1
6005 EL_EXIT_CLOSED, -1, -1
6008 // BD style elements with their corresponding RND style elements
6019 O_DIRT_SLOPED_UP_RIGHT, TRUE,
6020 EL_BD_SAND_SLOPED_UP_RIGHT, -1, -1
6023 O_DIRT_SLOPED_UP_LEFT, TRUE,
6024 EL_BD_SAND_SLOPED_UP_LEFT, -1, -1
6027 O_DIRT_SLOPED_DOWN_LEFT, TRUE,
6028 EL_BD_SAND_SLOPED_DOWN_LEFT, -1, -1
6031 O_DIRT_SLOPED_DOWN_RIGHT, TRUE,
6032 EL_BD_SAND_SLOPED_DOWN_RIGHT, -1, -1
6036 EL_BD_SAND_BALL, -1, -1
6039 O_DIRT_BALL_F, FALSE,
6040 EL_BD_SAND_BALL, ACTION_FALLING, -1
6044 EL_BD_SAND_LOOSE, -1, -1
6047 O_DIRT_LOOSE_F, FALSE,
6048 EL_BD_SAND_LOOSE, ACTION_FALLING, -1
6052 EL_BD_SAND_2, -1, -1
6059 O_BRICK_SLOPED_UP_RIGHT, TRUE,
6060 EL_BD_WALL_SLOPED_UP_RIGHT, -1, -1
6063 O_BRICK_SLOPED_UP_LEFT, TRUE,
6064 EL_BD_WALL_SLOPED_UP_LEFT, -1, -1
6067 O_BRICK_SLOPED_DOWN_LEFT, TRUE,
6068 EL_BD_WALL_SLOPED_DOWN_LEFT, -1, -1
6071 O_BRICK_SLOPED_DOWN_RIGHT, TRUE,
6072 EL_BD_WALL_SLOPED_DOWN_RIGHT, -1, -1
6075 O_BRICK_NON_SLOPED, TRUE,
6076 EL_BD_WALL_NON_SLOPED, -1, -1
6080 EL_BD_MAGIC_WALL, ACTION_ACTIVE, -1
6084 EL_BD_EXIT_CLOSED, -1, -1
6088 EL_BD_EXIT_OPEN, -1, -1
6091 O_PRE_INVIS_OUTBOX, TRUE,
6092 EL_BD_INVISIBLE_EXIT_CLOSED, -1, -1
6095 O_INVIS_OUTBOX, TRUE,
6096 EL_BD_INVISIBLE_EXIT_OPEN, -1, -1
6100 EL_BD_STEELWALL, -1, -1
6103 O_STEEL_SLOPED_UP_RIGHT, TRUE,
6104 EL_BD_STEELWALL_SLOPED_UP_RIGHT, -1, -1
6107 O_STEEL_SLOPED_UP_LEFT, TRUE,
6108 EL_BD_STEELWALL_SLOPED_UP_LEFT, -1, -1
6111 O_STEEL_SLOPED_DOWN_LEFT, TRUE,
6112 EL_BD_STEELWALL_SLOPED_DOWN_LEFT, -1, -1
6115 O_STEEL_SLOPED_DOWN_RIGHT, TRUE,
6116 EL_BD_STEELWALL_SLOPED_DOWN_RIGHT, -1, -1
6119 O_STEEL_EXPLODABLE, TRUE,
6120 EL_BD_STEELWALL_EXPLODABLE, -1, -1
6123 O_STEEL_EATABLE, TRUE,
6124 EL_BD_STEELWALL_DIGGABLE, -1, -1
6127 O_BRICK_EATABLE, TRUE,
6128 EL_BD_WALL_DIGGABLE, -1, -1
6136 EL_BD_ROCK, ACTION_FALLING, -1
6139 O_FLYING_STONE, TRUE,
6140 EL_BD_FLYING_ROCK, -1, -1
6143 O_FLYING_STONE_F, FALSE,
6144 EL_BD_FLYING_ROCK, ACTION_FALLING, -1
6148 EL_BD_MEGA_ROCK, -1, -1
6151 O_MEGA_STONE_F, FALSE,
6152 EL_BD_MEGA_ROCK, ACTION_FALLING, -1
6156 EL_BD_DIAMOND, -1, -1
6160 EL_BD_DIAMOND, ACTION_FALLING, -1
6163 O_FLYING_DIAMOND, TRUE,
6164 EL_BD_FLYING_DIAMOND, -1, -1
6167 O_FLYING_DIAMOND_F, FALSE,
6168 EL_BD_FLYING_DIAMOND, ACTION_FALLING, -1
6176 EL_BD_NUT, ACTION_FALLING, -1
6179 O_BLADDER_SPENDER, TRUE,
6180 EL_BD_BLADDER_SPENDER, -1, -1
6187 O_H_EXPANDING_WALL, TRUE,
6188 EL_BD_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6191 O_V_EXPANDING_WALL, TRUE,
6192 EL_BD_EXPANDABLE_WALL_VERTICAL, -1, -1
6195 O_EXPANDING_WALL, TRUE,
6196 EL_BD_EXPANDABLE_WALL_ANY, -1, -1
6199 O_H_EXPANDING_STEEL_WALL, TRUE,
6200 EL_BD_EXPANDABLE_STEELWALL_HORIZONTAL, -1, -1
6203 O_V_EXPANDING_STEEL_WALL, TRUE,
6204 EL_BD_EXPANDABLE_STEELWALL_VERTICAL, -1, -1
6207 O_EXPANDING_STEEL_WALL, TRUE,
6208 EL_BD_EXPANDABLE_STEELWALL_ANY, -1, -1
6211 O_EXPANDING_WALL_SWITCH, TRUE,
6212 EL_BD_EXPANDABLE_WALL_SWITCH_HORIZONTAL, -1, -1
6215 O_CREATURE_SWITCH, TRUE,
6216 EL_BD_CREATURE_SWITCH, -1, -1
6219 O_BITER_SWITCH, TRUE,
6220 EL_BD_BITER_SWITCH_1, -1, -1
6223 O_REPLICATOR_SWITCH, TRUE,
6224 EL_BD_REPLICATOR_SWITCH, -1, -1
6227 O_CONVEYOR_SWITCH, TRUE,
6228 EL_BD_CONVEYOR_SWITCH, -1, -1
6231 O_CONVEYOR_DIR_SWITCH, TRUE,
6232 EL_BD_CONVEYOR_DIR_SWITCH_RIGHT, -1, -1
6239 O_FALLING_WALL, TRUE,
6240 EL_BD_FALLING_WALL, -1, -1
6243 O_FALLING_WALL_F, FALSE,
6244 EL_BD_FALLING_WALL, ACTION_FALLING, -1
6251 O_TIME_PENALTY, TRUE,
6252 EL_BD_TIME_PENALTY, -1, -1
6256 EL_BD_GRAVESTONE, -1, -1
6259 O_STONE_GLUED, TRUE,
6260 EL_BD_ROCK_GLUED, -1, -1
6263 O_DIAMOND_GLUED, TRUE,
6264 EL_BD_DIAMOND_GLUED, -1, -1
6267 O_DIAMOND_KEY, TRUE,
6268 EL_BD_DIAMOND_KEY, -1, -1
6271 O_TRAPPED_DIAMOND, TRUE,
6272 EL_BD_TRAPPED_DIAMOND, -1, -1
6280 EL_BD_SAND_GLUED, -1, -1
6296 EL_BD_GATE_1, -1, -1
6300 EL_BD_GATE_2, -1, -1
6304 EL_BD_GATE_3, -1, -1
6311 O_GRAVITY_SWITCH, TRUE,
6312 EL_BD_GRAVITY_SWITCH, -1, -1
6315 O_PNEUMATIC_HAMMER, TRUE,
6316 EL_BD_PNEUMATIC_HAMMER, -1, -1
6320 EL_BD_TELEPORTER, -1, -1
6324 EL_BD_SKELETON, -1, -1
6396 EL_BD_COW_LEFT, -1, -1
6400 EL_BD_COW_UP, -1, -1
6404 EL_BD_COW_RIGHT, -1, -1
6408 EL_BD_COW_DOWN, -1, -1
6411 O_COW_ENCLOSED_1, FALSE,
6412 EL_BD_COW_DOWN, -1, -1
6415 O_COW_ENCLOSED_2, FALSE,
6416 EL_BD_COW_DOWN, -1, -1
6419 O_COW_ENCLOSED_3, FALSE,
6420 EL_BD_COW_DOWN, -1, -1
6423 O_COW_ENCLOSED_4, FALSE,
6424 EL_BD_COW_DOWN, -1, -1
6427 O_COW_ENCLOSED_5, FALSE,
6428 EL_BD_COW_DOWN, -1, -1
6431 O_COW_ENCLOSED_6, FALSE,
6432 EL_BD_COW_DOWN, -1, -1
6435 O_COW_ENCLOSED_7, FALSE,
6436 EL_BD_COW_DOWN, -1, -1
6439 O_WALLED_DIAMOND, TRUE,
6440 EL_BD_WALL_DIAMOND, -1, -1
6443 O_WALLED_KEY_1, TRUE,
6444 EL_BD_WALL_KEY_1, -1, -1
6447 O_WALLED_KEY_2, TRUE,
6448 EL_BD_WALL_KEY_2, -1, -1
6451 O_WALLED_KEY_3, TRUE,
6452 EL_BD_WALL_KEY_3, -1, -1
6456 EL_BD_AMOEBA, -1, -1
6460 EL_BD_AMOEBA_2, -1, -1
6464 EL_BD_REPLICATOR, -1, -1
6467 O_CONVEYOR_LEFT, TRUE,
6468 EL_BD_CONVEYOR_LEFT, -1, -1
6471 O_CONVEYOR_RIGHT, TRUE,
6472 EL_BD_CONVEYOR_RIGHT, -1, -1
6484 EL_BD_VOODOO_DOLL, -1, -1
6492 EL_BD_BLADDER, -1, -1
6496 EL_BD_BLADDER, -1, -1
6500 EL_BD_BLADDER, -1, -1
6504 EL_BD_BLADDER, -1, -1
6508 EL_BD_BLADDER, -1, -1
6512 EL_BD_BLADDER, -1, -1
6516 EL_BD_BLADDER, -1, -1
6520 EL_BD_BLADDER, -1, -1
6524 EL_BD_BLADDER, -1, -1
6527 O_WAITING_STONE, TRUE,
6528 EL_BD_WAITING_ROCK, -1, -1
6531 O_CHASING_STONE, TRUE,
6532 EL_BD_CHASING_ROCK, -1, -1
6540 EL_BD_FIREFLY_LEFT, -1, -1
6544 EL_BD_FIREFLY_UP, -1, -1
6548 EL_BD_FIREFLY_RIGHT, -1, -1
6552 EL_BD_FIREFLY_DOWN, -1, -1
6555 O_ALT_FIREFLY_1, TRUE,
6556 EL_BD_FIREFLY_2_LEFT, -1, -1
6559 O_ALT_FIREFLY_2, TRUE,
6560 EL_BD_FIREFLY_2_UP, -1, -1
6563 O_ALT_FIREFLY_3, TRUE,
6564 EL_BD_FIREFLY_2_RIGHT, -1, -1
6567 O_ALT_FIREFLY_4, TRUE,
6568 EL_BD_FIREFLY_2_DOWN, -1, -1
6572 EL_BD_BUTTERFLY_LEFT, -1, -1
6576 EL_BD_BUTTERFLY_UP, -1, -1
6580 EL_BD_BUTTERFLY_RIGHT, -1, -1
6584 EL_BD_BUTTERFLY_DOWN, -1, -1
6587 O_ALT_BUTTER_1, TRUE,
6588 EL_BD_BUTTERFLY_2_LEFT, -1, -1
6591 O_ALT_BUTTER_2, TRUE,
6592 EL_BD_BUTTERFLY_2_UP, -1, -1
6595 O_ALT_BUTTER_3, TRUE,
6596 EL_BD_BUTTERFLY_2_RIGHT, -1, -1
6599 O_ALT_BUTTER_4, TRUE,
6600 EL_BD_BUTTERFLY_2_DOWN, -1, -1
6604 EL_BD_STONEFLY_LEFT, -1, -1
6608 EL_BD_STONEFLY_UP, -1, -1
6612 EL_BD_STONEFLY_RIGHT, -1, -1
6616 EL_BD_STONEFLY_DOWN, -1, -1
6620 EL_BD_BITER_UP, -1, -1
6624 EL_BD_BITER_RIGHT, -1, -1
6628 EL_BD_BITER_DOWN, -1, -1
6632 EL_BD_BITER_LEFT, -1, -1
6635 O_DRAGONFLY_1, TRUE,
6636 EL_BD_DRAGONFLY_LEFT, -1, -1
6639 O_DRAGONFLY_2, TRUE,
6640 EL_BD_DRAGONFLY_UP, -1, -1
6643 O_DRAGONFLY_3, TRUE,
6644 EL_BD_DRAGONFLY_RIGHT, -1, -1
6647 O_DRAGONFLY_4, TRUE,
6648 EL_BD_DRAGONFLY_DOWN, -1, -1
6652 EL_BD_PLAYER, ACTION_GROWING, -1
6656 EL_BD_PLAYER, ACTION_GROWING, -1
6660 EL_BD_PLAYER, ACTION_GROWING, -1
6664 EL_BD_PLAYER, -1, -1
6667 O_PLAYER_BOMB, TRUE,
6668 EL_BD_PLAYER_WITH_BOMB, -1, -1
6671 O_PLAYER_GLUED, TRUE,
6672 EL_BD_PLAYER_GLUED, -1, -1
6675 O_PLAYER_STIRRING, TRUE,
6676 EL_BD_PLAYER_STIRRING, -1, -1
6683 O_BOMB_TICK_1, FALSE,
6684 EL_BD_BOMB, ACTION_ACTIVE, -1
6687 O_BOMB_TICK_2, FALSE,
6688 EL_BD_BOMB, ACTION_ACTIVE, -1
6691 O_BOMB_TICK_3, FALSE,
6692 EL_BD_BOMB, ACTION_ACTIVE, -1
6695 O_BOMB_TICK_4, FALSE,
6696 EL_BD_BOMB, ACTION_ACTIVE, -1
6699 O_BOMB_TICK_5, FALSE,
6700 EL_BD_BOMB, ACTION_ACTIVE, -1
6703 O_BOMB_TICK_6, FALSE,
6704 EL_BD_BOMB, ACTION_ACTIVE, -1
6707 O_BOMB_TICK_7, FALSE,
6708 EL_BD_BOMB, ACTION_ACTIVE, -1
6712 EL_BD_NITRO_PACK, -1, -1
6715 O_NITRO_PACK_F, FALSE,
6716 EL_BD_NITRO_PACK, ACTION_FALLING, -1
6719 O_NITRO_PACK_EXPLODE, FALSE,
6720 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
6723 O_PRE_CLOCK_1, FALSE,
6724 EL_BD_CLOCK, ACTION_GROWING, -1
6727 O_PRE_CLOCK_2, FALSE,
6728 EL_BD_CLOCK, ACTION_GROWING, -1
6731 O_PRE_CLOCK_3, FALSE,
6732 EL_BD_CLOCK, ACTION_GROWING, -1
6735 O_PRE_CLOCK_4, FALSE,
6736 EL_BD_CLOCK, ACTION_GROWING, -1
6740 EL_BD_DIAMOND, ACTION_GROWING, -1
6744 EL_BD_DIAMOND, ACTION_GROWING, -1
6748 EL_BD_DIAMOND, ACTION_GROWING, -1
6752 EL_BD_DIAMOND, ACTION_GROWING, -1
6756 EL_BD_DIAMOND, ACTION_GROWING, -1
6760 EL_DEFAULT, ACTION_EXPLODING, -1
6764 EL_DEFAULT, ACTION_EXPLODING, -1
6768 EL_DEFAULT, ACTION_EXPLODING, -1
6772 EL_DEFAULT, ACTION_EXPLODING, -1
6776 EL_DEFAULT, ACTION_EXPLODING, -1
6779 O_PRE_STONE_1, FALSE,
6780 EL_BD_ROCK, ACTION_GROWING, -1
6783 O_PRE_STONE_2, FALSE,
6784 EL_BD_ROCK, ACTION_GROWING, -1
6787 O_PRE_STONE_3, FALSE,
6788 EL_BD_ROCK, ACTION_GROWING, -1
6791 O_PRE_STONE_4, FALSE,
6792 EL_BD_ROCK, ACTION_GROWING, -1
6795 O_PRE_STEEL_1, FALSE,
6796 EL_BD_STEELWALL, ACTION_GROWING, -1
6799 O_PRE_STEEL_2, FALSE,
6800 EL_BD_STEELWALL, ACTION_GROWING, -1
6803 O_PRE_STEEL_3, FALSE,
6804 EL_BD_STEELWALL, ACTION_GROWING, -1
6807 O_PRE_STEEL_4, FALSE,
6808 EL_BD_STEELWALL, ACTION_GROWING, -1
6811 O_GHOST_EXPL_1, FALSE,
6812 EL_BD_GHOST, ACTION_EXPLODING, -1
6815 O_GHOST_EXPL_2, FALSE,
6816 EL_BD_GHOST, ACTION_EXPLODING, -1
6819 O_GHOST_EXPL_3, FALSE,
6820 EL_BD_GHOST, ACTION_EXPLODING, -1
6823 O_GHOST_EXPL_4, FALSE,
6824 EL_BD_GHOST, ACTION_EXPLODING, -1
6827 O_BOMB_EXPL_1, FALSE,
6828 EL_BD_BOMB, ACTION_EXPLODING, -1
6831 O_BOMB_EXPL_2, FALSE,
6832 EL_BD_BOMB, ACTION_EXPLODING, -1
6835 O_BOMB_EXPL_3, FALSE,
6836 EL_BD_BOMB, ACTION_EXPLODING, -1
6839 O_BOMB_EXPL_4, FALSE,
6840 EL_BD_BOMB, ACTION_EXPLODING, -1
6843 O_NITRO_EXPL_1, FALSE,
6844 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
6847 O_NITRO_EXPL_2, FALSE,
6848 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
6851 O_NITRO_EXPL_3, FALSE,
6852 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
6855 O_NITRO_EXPL_4, FALSE,
6856 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
6859 O_AMOEBA_2_EXPL_1, FALSE,
6860 EL_BD_AMOEBA_2, ACTION_EXPLODING, -1
6863 O_AMOEBA_2_EXPL_2, FALSE,
6864 EL_BD_AMOEBA_2, ACTION_EXPLODING, -1
6867 O_AMOEBA_2_EXPL_3, FALSE,
6868 EL_BD_AMOEBA_2, ACTION_EXPLODING, -1
6871 O_AMOEBA_2_EXPL_4, FALSE,
6872 EL_BD_AMOEBA_2, ACTION_EXPLODING, -1
6875 O_NUT_EXPL_1, FALSE,
6876 EL_BD_NUT, ACTION_BREAKING, -1
6879 O_NUT_EXPL_2, FALSE,
6880 EL_BD_NUT, ACTION_BREAKING, -1
6883 O_NUT_EXPL_3, FALSE,
6884 EL_BD_NUT, ACTION_BREAKING, -1
6887 O_NUT_EXPL_4, FALSE,
6888 EL_BD_NUT, ACTION_BREAKING, -1
6891 O_PLAYER_PNEUMATIC_LEFT, FALSE,
6892 EL_BD_PLAYER, ACTION_HITTING, MV_BIT_LEFT
6895 O_PLAYER_PNEUMATIC_RIGHT, FALSE,
6896 EL_BD_PLAYER, ACTION_HITTING, MV_BIT_RIGHT
6899 O_PNEUMATIC_ACTIVE_LEFT, TRUE,
6900 EL_BD_PNEUMATIC_HAMMER, ACTION_HITTING, MV_BIT_LEFT
6903 O_PNEUMATIC_ACTIVE_RIGHT, TRUE,
6904 EL_BD_PNEUMATIC_HAMMER, ACTION_HITTING, MV_BIT_RIGHT
6907 // helper (runtime) elements
6910 O_FAKE_BONUS, FALSE,
6911 EL_BD_FAKE_BONUS, -1, -1
6914 O_INBOX_CLOSED, FALSE,
6918 O_INBOX_OPEN, FALSE,
6919 EL_BD_INBOX, ACTION_OPENING, -1
6922 O_OUTBOX_CLOSED, FALSE,
6923 EL_BD_EXIT_CLOSED, -1, -1
6926 O_OUTBOX_OPEN, FALSE,
6927 EL_BD_EXIT_OPEN, -1, -1
6931 EL_BD_COVERED, -1, -1
6934 O_PLAYER_LEFT, FALSE,
6935 EL_BD_PLAYER, ACTION_MOVING, MV_BIT_LEFT
6938 O_PLAYER_RIGHT, FALSE,
6939 EL_BD_PLAYER, ACTION_MOVING, MV_BIT_RIGHT
6942 O_PLAYER_BLINK, FALSE,
6943 EL_BD_PLAYER, ACTION_BORING_1, -1
6946 O_PLAYER_TAP, FALSE,
6947 EL_BD_PLAYER, ACTION_BORING_2, -1
6950 O_PLAYER_TAP_BLINK, FALSE,
6951 EL_BD_PLAYER, ACTION_BORING_3, -1
6954 O_CREATURE_SWITCH_ON, FALSE,
6955 EL_BD_CREATURE_SWITCH_ACTIVE, -1, -1
6958 O_EXPANDING_WALL_SWITCH_HORIZ, FALSE,
6959 EL_BD_EXPANDABLE_WALL_SWITCH_HORIZONTAL, -1, -1
6962 O_EXPANDING_WALL_SWITCH_VERT, FALSE,
6963 EL_BD_EXPANDABLE_WALL_SWITCH_VERTICAL, -1, -1
6966 O_GRAVITY_SWITCH_ACTIVE, FALSE,
6967 EL_BD_GRAVITY_SWITCH_ACTIVE, -1, -1
6970 O_REPLICATOR_SWITCH_OFF, FALSE,
6971 EL_BD_REPLICATOR_SWITCH, -1, -1
6974 O_REPLICATOR_SWITCH_ON, FALSE,
6975 EL_BD_REPLICATOR_SWITCH_ACTIVE, -1, -1
6978 O_CONVEYOR_DIR_NORMAL, FALSE,
6979 EL_BD_CONVEYOR_DIR_SWITCH_RIGHT, -1, -1
6982 O_CONVEYOR_DIR_CHANGED, FALSE,
6983 EL_BD_CONVEYOR_DIR_SWITCH_LEFT, -1, -1
6986 O_CONVEYOR_SWITCH_OFF, FALSE,
6987 EL_BD_CONVEYOR_SWITCH, -1, -1
6990 O_CONVEYOR_SWITCH_ON, FALSE,
6991 EL_BD_CONVEYOR_SWITCH_ACTIVE, -1, -1
6994 O_MAGIC_WALL_ACTIVE, FALSE,
6995 EL_BD_MAGIC_WALL_ACTIVE, -1, -1
6998 O_REPLICATOR_ACTIVE, FALSE,
6999 EL_BD_REPLICATOR_ACTIVE, -1, -1
7002 O_CONVEYOR_LEFT_ACTIVE, FALSE,
7003 EL_BD_CONVEYOR_LEFT_ACTIVE, -1, -1
7006 O_CONVEYOR_RIGHT_ACTIVE, FALSE,
7007 EL_BD_CONVEYOR_RIGHT_ACTIVE, -1, -1
7010 O_BITER_SWITCH_1, FALSE,
7011 EL_BD_BITER_SWITCH_1, -1, -1
7014 O_BITER_SWITCH_2, FALSE,
7015 EL_BD_BITER_SWITCH_2, -1, -1
7018 O_BITER_SWITCH_3, FALSE,
7019 EL_BD_BITER_SWITCH_3, -1, -1
7022 O_BITER_SWITCH_4, FALSE,
7023 EL_BD_BITER_SWITCH_4, -1, -1
7032 int map_element_RND_to_BD(int element_rnd)
7034 static unsigned short mapping_RND_to_BD[NUM_FILE_ELEMENTS];
7035 static boolean mapping_initialized = FALSE;
7037 if (!mapping_initialized)
7041 // return "O_UNKNOWN" for all undefined elements in mapping array
7042 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7043 mapping_RND_to_BD[i] = O_UNKNOWN;
7045 for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
7046 if (bd_object_mapping_list[i].is_rnd_to_bd_mapping)
7047 mapping_RND_to_BD[bd_object_mapping_list[i].element_rnd] =
7048 bd_object_mapping_list[i].element_bd;
7050 mapping_initialized = TRUE;
7053 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7055 Warn("invalid RND element %d", element_rnd);
7060 return mapping_RND_to_BD[element_rnd];
7063 int map_element_BD_to_RND(int element_bd)
7065 static unsigned short mapping_BD_to_RND[O_MAX_ALL];
7066 static boolean mapping_initialized = FALSE;
7068 if (!mapping_initialized)
7072 // return "EL_UNKNOWN" for all undefined elements in mapping array
7073 for (i = 0; i < O_MAX_ALL; i++)
7074 mapping_BD_to_RND[i] = EL_UNKNOWN;
7076 for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
7077 mapping_BD_to_RND[bd_object_mapping_list[i].element_bd] =
7078 bd_object_mapping_list[i].element_rnd;
7080 mapping_initialized = TRUE;
7083 if (element_bd < 0 || element_bd >= O_MAX_ALL)
7085 Warn("invalid BD element %d", element_bd);
7090 return mapping_BD_to_RND[element_bd];
7093 static struct Mapping_EM_to_RND_object
7096 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
7097 boolean is_backside; // backside of moving element
7103 em_object_mapping_list[GAME_TILE_MAX + 1] =
7106 Zborder, FALSE, FALSE,
7110 Zplayer, FALSE, FALSE,
7119 Ztank, FALSE, FALSE,
7123 Zeater, FALSE, FALSE,
7127 Zdynamite, FALSE, FALSE,
7131 Zboom, FALSE, FALSE,
7136 Xchain, FALSE, FALSE,
7137 EL_DEFAULT, ACTION_EXPLODING, -1
7140 Xboom_bug, FALSE, FALSE,
7141 EL_BUG, ACTION_EXPLODING, -1
7144 Xboom_tank, FALSE, FALSE,
7145 EL_SPACESHIP, ACTION_EXPLODING, -1
7148 Xboom_android, FALSE, FALSE,
7149 EL_EMC_ANDROID, ACTION_OTHER, -1
7152 Xboom_1, FALSE, FALSE,
7153 EL_DEFAULT, ACTION_EXPLODING, -1
7156 Xboom_2, FALSE, FALSE,
7157 EL_DEFAULT, ACTION_EXPLODING, -1
7161 Xblank, TRUE, FALSE,
7166 Xsplash_e, FALSE, FALSE,
7167 EL_ACID_SPLASH_RIGHT, -1, -1
7170 Xsplash_w, FALSE, FALSE,
7171 EL_ACID_SPLASH_LEFT, -1, -1
7175 Xplant, TRUE, FALSE,
7176 EL_EMC_PLANT, -1, -1
7179 Yplant, FALSE, FALSE,
7180 EL_EMC_PLANT, -1, -1
7184 Xacid_1, TRUE, FALSE,
7188 Xacid_2, FALSE, FALSE,
7192 Xacid_3, FALSE, FALSE,
7196 Xacid_4, FALSE, FALSE,
7200 Xacid_5, FALSE, FALSE,
7204 Xacid_6, FALSE, FALSE,
7208 Xacid_7, FALSE, FALSE,
7212 Xacid_8, FALSE, FALSE,
7217 Xfake_acid_1, TRUE, FALSE,
7218 EL_EMC_FAKE_ACID, -1, -1
7221 Xfake_acid_2, FALSE, FALSE,
7222 EL_EMC_FAKE_ACID, -1, -1
7225 Xfake_acid_3, FALSE, FALSE,
7226 EL_EMC_FAKE_ACID, -1, -1
7229 Xfake_acid_4, FALSE, FALSE,
7230 EL_EMC_FAKE_ACID, -1, -1
7233 Xfake_acid_5, FALSE, FALSE,
7234 EL_EMC_FAKE_ACID, -1, -1
7237 Xfake_acid_6, FALSE, FALSE,
7238 EL_EMC_FAKE_ACID, -1, -1
7241 Xfake_acid_7, FALSE, FALSE,
7242 EL_EMC_FAKE_ACID, -1, -1
7245 Xfake_acid_8, FALSE, FALSE,
7246 EL_EMC_FAKE_ACID, -1, -1
7250 Xfake_acid_1_player, FALSE, FALSE,
7251 EL_EMC_FAKE_ACID, -1, -1
7254 Xfake_acid_2_player, FALSE, FALSE,
7255 EL_EMC_FAKE_ACID, -1, -1
7258 Xfake_acid_3_player, FALSE, FALSE,
7259 EL_EMC_FAKE_ACID, -1, -1
7262 Xfake_acid_4_player, FALSE, FALSE,
7263 EL_EMC_FAKE_ACID, -1, -1
7266 Xfake_acid_5_player, FALSE, FALSE,
7267 EL_EMC_FAKE_ACID, -1, -1
7270 Xfake_acid_6_player, FALSE, FALSE,
7271 EL_EMC_FAKE_ACID, -1, -1
7274 Xfake_acid_7_player, FALSE, FALSE,
7275 EL_EMC_FAKE_ACID, -1, -1
7278 Xfake_acid_8_player, FALSE, FALSE,
7279 EL_EMC_FAKE_ACID, -1, -1
7283 Xgrass, TRUE, FALSE,
7284 EL_EMC_GRASS, -1, -1
7287 Ygrass_nB, FALSE, FALSE,
7288 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
7291 Ygrass_eB, FALSE, FALSE,
7292 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
7295 Ygrass_sB, FALSE, FALSE,
7296 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
7299 Ygrass_wB, FALSE, FALSE,
7300 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
7308 Ydirt_nB, FALSE, FALSE,
7309 EL_SAND, ACTION_DIGGING, MV_BIT_UP
7312 Ydirt_eB, FALSE, FALSE,
7313 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
7316 Ydirt_sB, FALSE, FALSE,
7317 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
7320 Ydirt_wB, FALSE, FALSE,
7321 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
7325 Xandroid, TRUE, FALSE,
7326 EL_EMC_ANDROID, ACTION_ACTIVE, -1
7329 Xandroid_1_n, FALSE, FALSE,
7330 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
7333 Xandroid_2_n, FALSE, FALSE,
7334 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
7337 Xandroid_1_e, FALSE, FALSE,
7338 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
7341 Xandroid_2_e, FALSE, FALSE,
7342 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
7345 Xandroid_1_w, FALSE, FALSE,
7346 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
7349 Xandroid_2_w, FALSE, FALSE,
7350 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
7353 Xandroid_1_s, FALSE, FALSE,
7354 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
7357 Xandroid_2_s, FALSE, FALSE,
7358 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
7361 Yandroid_n, FALSE, FALSE,
7362 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
7365 Yandroid_nB, FALSE, TRUE,
7366 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
7369 Yandroid_ne, FALSE, FALSE,
7370 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
7373 Yandroid_neB, FALSE, TRUE,
7374 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
7377 Yandroid_e, FALSE, FALSE,
7378 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
7381 Yandroid_eB, FALSE, TRUE,
7382 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
7385 Yandroid_se, FALSE, FALSE,
7386 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
7389 Yandroid_seB, FALSE, TRUE,
7390 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
7393 Yandroid_s, FALSE, FALSE,
7394 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
7397 Yandroid_sB, FALSE, TRUE,
7398 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
7401 Yandroid_sw, FALSE, FALSE,
7402 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
7405 Yandroid_swB, FALSE, TRUE,
7406 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
7409 Yandroid_w, FALSE, FALSE,
7410 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
7413 Yandroid_wB, FALSE, TRUE,
7414 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
7417 Yandroid_nw, FALSE, FALSE,
7418 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
7421 Yandroid_nwB, FALSE, TRUE,
7422 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
7426 Xeater_n, TRUE, FALSE,
7427 EL_YAMYAM_UP, -1, -1
7430 Xeater_e, TRUE, FALSE,
7431 EL_YAMYAM_RIGHT, -1, -1
7434 Xeater_w, TRUE, FALSE,
7435 EL_YAMYAM_LEFT, -1, -1
7438 Xeater_s, TRUE, FALSE,
7439 EL_YAMYAM_DOWN, -1, -1
7442 Yeater_n, FALSE, FALSE,
7443 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
7446 Yeater_nB, FALSE, TRUE,
7447 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
7450 Yeater_e, FALSE, FALSE,
7451 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
7454 Yeater_eB, FALSE, TRUE,
7455 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
7458 Yeater_s, FALSE, FALSE,
7459 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
7462 Yeater_sB, FALSE, TRUE,
7463 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
7466 Yeater_w, FALSE, FALSE,
7467 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
7470 Yeater_wB, FALSE, TRUE,
7471 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
7474 Yeater_stone, FALSE, FALSE,
7475 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
7478 Yeater_spring, FALSE, FALSE,
7479 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
7483 Xalien, TRUE, FALSE,
7487 Xalien_pause, FALSE, FALSE,
7491 Yalien_n, FALSE, FALSE,
7492 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
7495 Yalien_nB, FALSE, TRUE,
7496 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
7499 Yalien_e, FALSE, FALSE,
7500 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
7503 Yalien_eB, FALSE, TRUE,
7504 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
7507 Yalien_s, FALSE, FALSE,
7508 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
7511 Yalien_sB, FALSE, TRUE,
7512 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
7515 Yalien_w, FALSE, FALSE,
7516 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
7519 Yalien_wB, FALSE, TRUE,
7520 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
7523 Yalien_stone, FALSE, FALSE,
7524 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
7527 Yalien_spring, FALSE, FALSE,
7528 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
7532 Xbug_1_n, TRUE, FALSE,
7536 Xbug_1_e, TRUE, FALSE,
7537 EL_BUG_RIGHT, -1, -1
7540 Xbug_1_s, TRUE, FALSE,
7544 Xbug_1_w, TRUE, FALSE,
7548 Xbug_2_n, FALSE, FALSE,
7552 Xbug_2_e, FALSE, FALSE,
7553 EL_BUG_RIGHT, -1, -1
7556 Xbug_2_s, FALSE, FALSE,
7560 Xbug_2_w, FALSE, FALSE,
7564 Ybug_n, FALSE, FALSE,
7565 EL_BUG, ACTION_MOVING, MV_BIT_UP
7568 Ybug_nB, FALSE, TRUE,
7569 EL_BUG, ACTION_MOVING, MV_BIT_UP
7572 Ybug_e, FALSE, FALSE,
7573 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
7576 Ybug_eB, FALSE, TRUE,
7577 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
7580 Ybug_s, FALSE, FALSE,
7581 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
7584 Ybug_sB, FALSE, TRUE,
7585 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
7588 Ybug_w, FALSE, FALSE,
7589 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
7592 Ybug_wB, FALSE, TRUE,
7593 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
7596 Ybug_w_n, FALSE, FALSE,
7597 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
7600 Ybug_n_e, FALSE, FALSE,
7601 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
7604 Ybug_e_s, FALSE, FALSE,
7605 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
7608 Ybug_s_w, FALSE, FALSE,
7609 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
7612 Ybug_e_n, FALSE, FALSE,
7613 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
7616 Ybug_s_e, FALSE, FALSE,
7617 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
7620 Ybug_w_s, FALSE, FALSE,
7621 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
7624 Ybug_n_w, FALSE, FALSE,
7625 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
7628 Ybug_stone, FALSE, FALSE,
7629 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
7632 Ybug_spring, FALSE, FALSE,
7633 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
7637 Xtank_1_n, TRUE, FALSE,
7638 EL_SPACESHIP_UP, -1, -1
7641 Xtank_1_e, TRUE, FALSE,
7642 EL_SPACESHIP_RIGHT, -1, -1
7645 Xtank_1_s, TRUE, FALSE,
7646 EL_SPACESHIP_DOWN, -1, -1
7649 Xtank_1_w, TRUE, FALSE,
7650 EL_SPACESHIP_LEFT, -1, -1
7653 Xtank_2_n, FALSE, FALSE,
7654 EL_SPACESHIP_UP, -1, -1
7657 Xtank_2_e, FALSE, FALSE,
7658 EL_SPACESHIP_RIGHT, -1, -1
7661 Xtank_2_s, FALSE, FALSE,
7662 EL_SPACESHIP_DOWN, -1, -1
7665 Xtank_2_w, FALSE, FALSE,
7666 EL_SPACESHIP_LEFT, -1, -1
7669 Ytank_n, FALSE, FALSE,
7670 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
7673 Ytank_nB, FALSE, TRUE,
7674 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
7677 Ytank_e, FALSE, FALSE,
7678 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
7681 Ytank_eB, FALSE, TRUE,
7682 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
7685 Ytank_s, FALSE, FALSE,
7686 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
7689 Ytank_sB, FALSE, TRUE,
7690 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
7693 Ytank_w, FALSE, FALSE,
7694 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
7697 Ytank_wB, FALSE, TRUE,
7698 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
7701 Ytank_w_n, FALSE, FALSE,
7702 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
7705 Ytank_n_e, FALSE, FALSE,
7706 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
7709 Ytank_e_s, FALSE, FALSE,
7710 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
7713 Ytank_s_w, FALSE, FALSE,
7714 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
7717 Ytank_e_n, FALSE, FALSE,
7718 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
7721 Ytank_s_e, FALSE, FALSE,
7722 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
7725 Ytank_w_s, FALSE, FALSE,
7726 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
7729 Ytank_n_w, FALSE, FALSE,
7730 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
7733 Ytank_stone, FALSE, FALSE,
7734 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
7737 Ytank_spring, FALSE, FALSE,
7738 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
7742 Xemerald, TRUE, FALSE,
7746 Xemerald_pause, FALSE, FALSE,
7750 Xemerald_fall, FALSE, FALSE,
7754 Xemerald_shine, FALSE, FALSE,
7755 EL_EMERALD, ACTION_TWINKLING, -1
7758 Yemerald_s, FALSE, FALSE,
7759 EL_EMERALD, ACTION_FALLING, -1
7762 Yemerald_sB, FALSE, TRUE,
7763 EL_EMERALD, ACTION_FALLING, -1
7766 Yemerald_e, FALSE, FALSE,
7767 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
7770 Yemerald_eB, FALSE, TRUE,
7771 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
7774 Yemerald_w, FALSE, FALSE,
7775 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
7778 Yemerald_wB, FALSE, TRUE,
7779 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
7782 Yemerald_blank, FALSE, FALSE,
7783 EL_EMERALD, ACTION_COLLECTING, -1
7787 Xdiamond, TRUE, FALSE,
7791 Xdiamond_pause, FALSE, FALSE,
7795 Xdiamond_fall, FALSE, FALSE,
7799 Xdiamond_shine, FALSE, FALSE,
7800 EL_DIAMOND, ACTION_TWINKLING, -1
7803 Ydiamond_s, FALSE, FALSE,
7804 EL_DIAMOND, ACTION_FALLING, -1
7807 Ydiamond_sB, FALSE, TRUE,
7808 EL_DIAMOND, ACTION_FALLING, -1
7811 Ydiamond_e, FALSE, FALSE,
7812 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
7815 Ydiamond_eB, FALSE, TRUE,
7816 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
7819 Ydiamond_w, FALSE, FALSE,
7820 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
7823 Ydiamond_wB, FALSE, TRUE,
7824 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
7827 Ydiamond_blank, FALSE, FALSE,
7828 EL_DIAMOND, ACTION_COLLECTING, -1
7831 Ydiamond_stone, FALSE, FALSE,
7832 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
7836 Xstone, TRUE, FALSE,
7840 Xstone_pause, FALSE, FALSE,
7844 Xstone_fall, FALSE, FALSE,
7848 Ystone_s, FALSE, FALSE,
7849 EL_ROCK, ACTION_FALLING, -1
7852 Ystone_sB, FALSE, TRUE,
7853 EL_ROCK, ACTION_FALLING, -1
7856 Ystone_e, FALSE, FALSE,
7857 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
7860 Ystone_eB, FALSE, TRUE,
7861 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
7864 Ystone_w, FALSE, FALSE,
7865 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
7868 Ystone_wB, FALSE, TRUE,
7869 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
7877 Xbomb_pause, FALSE, FALSE,
7881 Xbomb_fall, FALSE, FALSE,
7885 Ybomb_s, FALSE, FALSE,
7886 EL_BOMB, ACTION_FALLING, -1
7889 Ybomb_sB, FALSE, TRUE,
7890 EL_BOMB, ACTION_FALLING, -1
7893 Ybomb_e, FALSE, FALSE,
7894 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
7897 Ybomb_eB, FALSE, TRUE,
7898 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
7901 Ybomb_w, FALSE, FALSE,
7902 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
7905 Ybomb_wB, FALSE, TRUE,
7906 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
7909 Ybomb_blank, FALSE, FALSE,
7910 EL_BOMB, ACTION_ACTIVATING, -1
7918 Xnut_pause, FALSE, FALSE,
7922 Xnut_fall, FALSE, FALSE,
7926 Ynut_s, FALSE, FALSE,
7927 EL_NUT, ACTION_FALLING, -1
7930 Ynut_sB, FALSE, TRUE,
7931 EL_NUT, ACTION_FALLING, -1
7934 Ynut_e, FALSE, FALSE,
7935 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
7938 Ynut_eB, FALSE, TRUE,
7939 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
7942 Ynut_w, FALSE, FALSE,
7943 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
7946 Ynut_wB, FALSE, TRUE,
7947 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
7950 Ynut_stone, FALSE, FALSE,
7951 EL_NUT, ACTION_BREAKING, -1
7955 Xspring, TRUE, FALSE,
7959 Xspring_pause, FALSE, FALSE,
7963 Xspring_e, TRUE, FALSE,
7964 EL_SPRING_RIGHT, -1, -1
7967 Xspring_w, TRUE, FALSE,
7968 EL_SPRING_LEFT, -1, -1
7971 Xspring_fall, FALSE, FALSE,
7975 Yspring_s, FALSE, FALSE,
7976 EL_SPRING, ACTION_FALLING, -1
7979 Yspring_sB, FALSE, TRUE,
7980 EL_SPRING, ACTION_FALLING, -1
7983 Yspring_e, FALSE, FALSE,
7984 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
7987 Yspring_eB, FALSE, TRUE,
7988 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
7991 Yspring_w, FALSE, FALSE,
7992 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
7995 Yspring_wB, FALSE, TRUE,
7996 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
7999 Yspring_alien_e, FALSE, FALSE,
8000 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
8003 Yspring_alien_eB, FALSE, TRUE,
8004 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
8007 Yspring_alien_w, FALSE, FALSE,
8008 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
8011 Yspring_alien_wB, FALSE, TRUE,
8012 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
8016 Xpush_emerald_e, FALSE, FALSE,
8017 EL_EMERALD, -1, MV_BIT_RIGHT
8020 Xpush_emerald_w, FALSE, FALSE,
8021 EL_EMERALD, -1, MV_BIT_LEFT
8024 Xpush_diamond_e, FALSE, FALSE,
8025 EL_DIAMOND, -1, MV_BIT_RIGHT
8028 Xpush_diamond_w, FALSE, FALSE,
8029 EL_DIAMOND, -1, MV_BIT_LEFT
8032 Xpush_stone_e, FALSE, FALSE,
8033 EL_ROCK, -1, MV_BIT_RIGHT
8036 Xpush_stone_w, FALSE, FALSE,
8037 EL_ROCK, -1, MV_BIT_LEFT
8040 Xpush_bomb_e, FALSE, FALSE,
8041 EL_BOMB, -1, MV_BIT_RIGHT
8044 Xpush_bomb_w, FALSE, FALSE,
8045 EL_BOMB, -1, MV_BIT_LEFT
8048 Xpush_nut_e, FALSE, FALSE,
8049 EL_NUT, -1, MV_BIT_RIGHT
8052 Xpush_nut_w, FALSE, FALSE,
8053 EL_NUT, -1, MV_BIT_LEFT
8056 Xpush_spring_e, FALSE, FALSE,
8057 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
8060 Xpush_spring_w, FALSE, FALSE,
8061 EL_SPRING_LEFT, -1, MV_BIT_LEFT
8065 Xdynamite, TRUE, FALSE,
8066 EL_EM_DYNAMITE, -1, -1
8069 Ydynamite_blank, FALSE, FALSE,
8070 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
8073 Xdynamite_1, TRUE, FALSE,
8074 EL_EM_DYNAMITE_ACTIVE, -1, -1
8077 Xdynamite_2, FALSE, FALSE,
8078 EL_EM_DYNAMITE_ACTIVE, -1, -1
8081 Xdynamite_3, FALSE, FALSE,
8082 EL_EM_DYNAMITE_ACTIVE, -1, -1
8085 Xdynamite_4, FALSE, FALSE,
8086 EL_EM_DYNAMITE_ACTIVE, -1, -1
8090 Xkey_1, TRUE, FALSE,
8094 Xkey_2, TRUE, FALSE,
8098 Xkey_3, TRUE, FALSE,
8102 Xkey_4, TRUE, FALSE,
8106 Xkey_5, TRUE, FALSE,
8107 EL_EMC_KEY_5, -1, -1
8110 Xkey_6, TRUE, FALSE,
8111 EL_EMC_KEY_6, -1, -1
8114 Xkey_7, TRUE, FALSE,
8115 EL_EMC_KEY_7, -1, -1
8118 Xkey_8, TRUE, FALSE,
8119 EL_EMC_KEY_8, -1, -1
8123 Xdoor_1, TRUE, FALSE,
8124 EL_EM_GATE_1, -1, -1
8127 Xdoor_2, TRUE, FALSE,
8128 EL_EM_GATE_2, -1, -1
8131 Xdoor_3, TRUE, FALSE,
8132 EL_EM_GATE_3, -1, -1
8135 Xdoor_4, TRUE, FALSE,
8136 EL_EM_GATE_4, -1, -1
8139 Xdoor_5, TRUE, FALSE,
8140 EL_EMC_GATE_5, -1, -1
8143 Xdoor_6, TRUE, FALSE,
8144 EL_EMC_GATE_6, -1, -1
8147 Xdoor_7, TRUE, FALSE,
8148 EL_EMC_GATE_7, -1, -1
8151 Xdoor_8, TRUE, FALSE,
8152 EL_EMC_GATE_8, -1, -1
8156 Xfake_door_1, TRUE, FALSE,
8157 EL_EM_GATE_1_GRAY, -1, -1
8160 Xfake_door_2, TRUE, FALSE,
8161 EL_EM_GATE_2_GRAY, -1, -1
8164 Xfake_door_3, TRUE, FALSE,
8165 EL_EM_GATE_3_GRAY, -1, -1
8168 Xfake_door_4, TRUE, FALSE,
8169 EL_EM_GATE_4_GRAY, -1, -1
8172 Xfake_door_5, TRUE, FALSE,
8173 EL_EMC_GATE_5_GRAY, -1, -1
8176 Xfake_door_6, TRUE, FALSE,
8177 EL_EMC_GATE_6_GRAY, -1, -1
8180 Xfake_door_7, TRUE, FALSE,
8181 EL_EMC_GATE_7_GRAY, -1, -1
8184 Xfake_door_8, TRUE, FALSE,
8185 EL_EMC_GATE_8_GRAY, -1, -1
8189 Xballoon, TRUE, FALSE,
8193 Yballoon_n, FALSE, FALSE,
8194 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
8197 Yballoon_nB, FALSE, TRUE,
8198 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
8201 Yballoon_e, FALSE, FALSE,
8202 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
8205 Yballoon_eB, FALSE, TRUE,
8206 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
8209 Yballoon_s, FALSE, FALSE,
8210 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
8213 Yballoon_sB, FALSE, TRUE,
8214 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
8217 Yballoon_w, FALSE, FALSE,
8218 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
8221 Yballoon_wB, FALSE, TRUE,
8222 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
8226 Xball_1, TRUE, FALSE,
8227 EL_EMC_MAGIC_BALL, -1, -1
8230 Yball_1, FALSE, FALSE,
8231 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
8234 Xball_2, FALSE, FALSE,
8235 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
8238 Yball_2, FALSE, FALSE,
8239 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
8242 Yball_blank, FALSE, FALSE,
8243 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
8247 Xamoeba_1, TRUE, FALSE,
8248 EL_AMOEBA_DRY, ACTION_OTHER, -1
8251 Xamoeba_2, FALSE, FALSE,
8252 EL_AMOEBA_DRY, ACTION_OTHER, -1
8255 Xamoeba_3, FALSE, FALSE,
8256 EL_AMOEBA_DRY, ACTION_OTHER, -1
8259 Xamoeba_4, FALSE, FALSE,
8260 EL_AMOEBA_DRY, ACTION_OTHER, -1
8263 Xamoeba_5, TRUE, FALSE,
8264 EL_AMOEBA_WET, ACTION_OTHER, -1
8267 Xamoeba_6, FALSE, FALSE,
8268 EL_AMOEBA_WET, ACTION_OTHER, -1
8271 Xamoeba_7, FALSE, FALSE,
8272 EL_AMOEBA_WET, ACTION_OTHER, -1
8275 Xamoeba_8, FALSE, FALSE,
8276 EL_AMOEBA_WET, ACTION_OTHER, -1
8281 EL_AMOEBA_DROP, ACTION_GROWING, -1
8284 Xdrip_fall, FALSE, FALSE,
8285 EL_AMOEBA_DROP, -1, -1
8288 Xdrip_stretch, FALSE, FALSE,
8289 EL_AMOEBA_DROP, ACTION_FALLING, -1
8292 Xdrip_stretchB, FALSE, TRUE,
8293 EL_AMOEBA_DROP, ACTION_FALLING, -1
8296 Ydrip_1_s, FALSE, FALSE,
8297 EL_AMOEBA_DROP, ACTION_FALLING, -1
8300 Ydrip_1_sB, FALSE, TRUE,
8301 EL_AMOEBA_DROP, ACTION_FALLING, -1
8304 Ydrip_2_s, FALSE, FALSE,
8305 EL_AMOEBA_DROP, ACTION_FALLING, -1
8308 Ydrip_2_sB, FALSE, TRUE,
8309 EL_AMOEBA_DROP, ACTION_FALLING, -1
8313 Xwonderwall, TRUE, FALSE,
8314 EL_MAGIC_WALL, -1, -1
8317 Ywonderwall, FALSE, FALSE,
8318 EL_MAGIC_WALL, ACTION_ACTIVE, -1
8322 Xwheel, TRUE, FALSE,
8323 EL_ROBOT_WHEEL, -1, -1
8326 Ywheel, FALSE, FALSE,
8327 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
8331 Xswitch, TRUE, FALSE,
8332 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
8335 Yswitch, FALSE, FALSE,
8336 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
8340 Xbumper, TRUE, FALSE,
8341 EL_EMC_SPRING_BUMPER, -1, -1
8344 Ybumper, FALSE, FALSE,
8345 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
8349 Xacid_nw, TRUE, FALSE,
8350 EL_ACID_POOL_TOPLEFT, -1, -1
8353 Xacid_ne, TRUE, FALSE,
8354 EL_ACID_POOL_TOPRIGHT, -1, -1
8357 Xacid_sw, TRUE, FALSE,
8358 EL_ACID_POOL_BOTTOMLEFT, -1, -1
8361 Xacid_s, TRUE, FALSE,
8362 EL_ACID_POOL_BOTTOM, -1, -1
8365 Xacid_se, TRUE, FALSE,
8366 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
8370 Xfake_blank, TRUE, FALSE,
8371 EL_INVISIBLE_WALL, -1, -1
8374 Yfake_blank, FALSE, FALSE,
8375 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
8379 Xfake_grass, TRUE, FALSE,
8380 EL_EMC_FAKE_GRASS, -1, -1
8383 Yfake_grass, FALSE, FALSE,
8384 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
8388 Xfake_amoeba, TRUE, FALSE,
8389 EL_EMC_DRIPPER, -1, -1
8392 Yfake_amoeba, FALSE, FALSE,
8393 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
8397 Xlenses, TRUE, FALSE,
8398 EL_EMC_LENSES, -1, -1
8402 Xmagnify, TRUE, FALSE,
8403 EL_EMC_MAGNIFIER, -1, -1
8408 EL_QUICKSAND_EMPTY, -1, -1
8411 Xsand_stone, TRUE, FALSE,
8412 EL_QUICKSAND_FULL, -1, -1
8415 Xsand_stonein_1, FALSE, TRUE,
8416 EL_ROCK, ACTION_FILLING, -1
8419 Xsand_stonein_2, FALSE, TRUE,
8420 EL_ROCK, ACTION_FILLING, -1
8423 Xsand_stonein_3, FALSE, TRUE,
8424 EL_ROCK, ACTION_FILLING, -1
8427 Xsand_stonein_4, FALSE, TRUE,
8428 EL_ROCK, ACTION_FILLING, -1
8431 Xsand_sandstone_1, FALSE, FALSE,
8432 EL_QUICKSAND_FILLING, -1, -1
8435 Xsand_sandstone_2, FALSE, FALSE,
8436 EL_QUICKSAND_FILLING, -1, -1
8439 Xsand_sandstone_3, FALSE, FALSE,
8440 EL_QUICKSAND_FILLING, -1, -1
8443 Xsand_sandstone_4, FALSE, FALSE,
8444 EL_QUICKSAND_FILLING, -1, -1
8447 Xsand_stonesand_1, FALSE, FALSE,
8448 EL_QUICKSAND_EMPTYING, -1, -1
8451 Xsand_stonesand_2, FALSE, FALSE,
8452 EL_QUICKSAND_EMPTYING, -1, -1
8455 Xsand_stonesand_3, FALSE, FALSE,
8456 EL_QUICKSAND_EMPTYING, -1, -1
8459 Xsand_stonesand_4, FALSE, FALSE,
8460 EL_QUICKSAND_EMPTYING, -1, -1
8463 Xsand_stoneout_1, FALSE, FALSE,
8464 EL_ROCK, ACTION_EMPTYING, -1
8467 Xsand_stoneout_2, FALSE, FALSE,
8468 EL_ROCK, ACTION_EMPTYING, -1
8471 Xsand_stonesand_quickout_1, FALSE, FALSE,
8472 EL_QUICKSAND_EMPTYING, -1, -1
8475 Xsand_stonesand_quickout_2, FALSE, FALSE,
8476 EL_QUICKSAND_EMPTYING, -1, -1
8480 Xslide_ns, TRUE, FALSE,
8481 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
8484 Yslide_ns_blank, FALSE, FALSE,
8485 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
8488 Xslide_ew, TRUE, FALSE,
8489 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
8492 Yslide_ew_blank, FALSE, FALSE,
8493 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
8497 Xwind_n, TRUE, FALSE,
8498 EL_BALLOON_SWITCH_UP, -1, -1
8501 Xwind_e, TRUE, FALSE,
8502 EL_BALLOON_SWITCH_RIGHT, -1, -1
8505 Xwind_s, TRUE, FALSE,
8506 EL_BALLOON_SWITCH_DOWN, -1, -1
8509 Xwind_w, TRUE, FALSE,
8510 EL_BALLOON_SWITCH_LEFT, -1, -1
8513 Xwind_any, TRUE, FALSE,
8514 EL_BALLOON_SWITCH_ANY, -1, -1
8517 Xwind_stop, TRUE, FALSE,
8518 EL_BALLOON_SWITCH_NONE, -1, -1
8523 EL_EM_EXIT_CLOSED, -1, -1
8526 Xexit_1, TRUE, FALSE,
8527 EL_EM_EXIT_OPEN, -1, -1
8530 Xexit_2, FALSE, FALSE,
8531 EL_EM_EXIT_OPEN, -1, -1
8534 Xexit_3, FALSE, FALSE,
8535 EL_EM_EXIT_OPEN, -1, -1
8539 Xpause, FALSE, FALSE,
8544 Xwall_1, TRUE, FALSE,
8548 Xwall_2, TRUE, FALSE,
8549 EL_EMC_WALL_14, -1, -1
8552 Xwall_3, TRUE, FALSE,
8553 EL_EMC_WALL_15, -1, -1
8556 Xwall_4, TRUE, FALSE,
8557 EL_EMC_WALL_16, -1, -1
8561 Xroundwall_1, TRUE, FALSE,
8562 EL_WALL_SLIPPERY, -1, -1
8565 Xroundwall_2, TRUE, FALSE,
8566 EL_EMC_WALL_SLIPPERY_2, -1, -1
8569 Xroundwall_3, TRUE, FALSE,
8570 EL_EMC_WALL_SLIPPERY_3, -1, -1
8573 Xroundwall_4, TRUE, FALSE,
8574 EL_EMC_WALL_SLIPPERY_4, -1, -1
8578 Xsteel_1, TRUE, FALSE,
8579 EL_STEELWALL, -1, -1
8582 Xsteel_2, TRUE, FALSE,
8583 EL_EMC_STEELWALL_2, -1, -1
8586 Xsteel_3, TRUE, FALSE,
8587 EL_EMC_STEELWALL_3, -1, -1
8590 Xsteel_4, TRUE, FALSE,
8591 EL_EMC_STEELWALL_4, -1, -1
8595 Xdecor_1, TRUE, FALSE,
8596 EL_EMC_WALL_8, -1, -1
8599 Xdecor_2, TRUE, FALSE,
8600 EL_EMC_WALL_6, -1, -1
8603 Xdecor_3, TRUE, FALSE,
8604 EL_EMC_WALL_4, -1, -1
8607 Xdecor_4, TRUE, FALSE,
8608 EL_EMC_WALL_7, -1, -1
8611 Xdecor_5, TRUE, FALSE,
8612 EL_EMC_WALL_5, -1, -1
8615 Xdecor_6, TRUE, FALSE,
8616 EL_EMC_WALL_9, -1, -1
8619 Xdecor_7, TRUE, FALSE,
8620 EL_EMC_WALL_10, -1, -1
8623 Xdecor_8, TRUE, FALSE,
8624 EL_EMC_WALL_1, -1, -1
8627 Xdecor_9, TRUE, FALSE,
8628 EL_EMC_WALL_2, -1, -1
8631 Xdecor_10, TRUE, FALSE,
8632 EL_EMC_WALL_3, -1, -1
8635 Xdecor_11, TRUE, FALSE,
8636 EL_EMC_WALL_11, -1, -1
8639 Xdecor_12, TRUE, FALSE,
8640 EL_EMC_WALL_12, -1, -1
8644 Xalpha_0, TRUE, FALSE,
8645 EL_CHAR('0'), -1, -1
8648 Xalpha_1, TRUE, FALSE,
8649 EL_CHAR('1'), -1, -1
8652 Xalpha_2, TRUE, FALSE,
8653 EL_CHAR('2'), -1, -1
8656 Xalpha_3, TRUE, FALSE,
8657 EL_CHAR('3'), -1, -1
8660 Xalpha_4, TRUE, FALSE,
8661 EL_CHAR('4'), -1, -1
8664 Xalpha_5, TRUE, FALSE,
8665 EL_CHAR('5'), -1, -1
8668 Xalpha_6, TRUE, FALSE,
8669 EL_CHAR('6'), -1, -1
8672 Xalpha_7, TRUE, FALSE,
8673 EL_CHAR('7'), -1, -1
8676 Xalpha_8, TRUE, FALSE,
8677 EL_CHAR('8'), -1, -1
8680 Xalpha_9, TRUE, FALSE,
8681 EL_CHAR('9'), -1, -1
8684 Xalpha_excla, TRUE, FALSE,
8685 EL_CHAR('!'), -1, -1
8688 Xalpha_apost, TRUE, FALSE,
8689 EL_CHAR('\''), -1, -1
8692 Xalpha_comma, TRUE, FALSE,
8693 EL_CHAR(','), -1, -1
8696 Xalpha_minus, TRUE, FALSE,
8697 EL_CHAR('-'), -1, -1
8700 Xalpha_perio, TRUE, FALSE,
8701 EL_CHAR('.'), -1, -1
8704 Xalpha_colon, TRUE, FALSE,
8705 EL_CHAR(':'), -1, -1
8708 Xalpha_quest, TRUE, FALSE,
8709 EL_CHAR('?'), -1, -1
8712 Xalpha_a, TRUE, FALSE,
8713 EL_CHAR('A'), -1, -1
8716 Xalpha_b, TRUE, FALSE,
8717 EL_CHAR('B'), -1, -1
8720 Xalpha_c, TRUE, FALSE,
8721 EL_CHAR('C'), -1, -1
8724 Xalpha_d, TRUE, FALSE,
8725 EL_CHAR('D'), -1, -1
8728 Xalpha_e, TRUE, FALSE,
8729 EL_CHAR('E'), -1, -1
8732 Xalpha_f, TRUE, FALSE,
8733 EL_CHAR('F'), -1, -1
8736 Xalpha_g, TRUE, FALSE,
8737 EL_CHAR('G'), -1, -1
8740 Xalpha_h, TRUE, FALSE,
8741 EL_CHAR('H'), -1, -1
8744 Xalpha_i, TRUE, FALSE,
8745 EL_CHAR('I'), -1, -1
8748 Xalpha_j, TRUE, FALSE,
8749 EL_CHAR('J'), -1, -1
8752 Xalpha_k, TRUE, FALSE,
8753 EL_CHAR('K'), -1, -1
8756 Xalpha_l, TRUE, FALSE,
8757 EL_CHAR('L'), -1, -1
8760 Xalpha_m, TRUE, FALSE,
8761 EL_CHAR('M'), -1, -1
8764 Xalpha_n, TRUE, FALSE,
8765 EL_CHAR('N'), -1, -1
8768 Xalpha_o, TRUE, FALSE,
8769 EL_CHAR('O'), -1, -1
8772 Xalpha_p, TRUE, FALSE,
8773 EL_CHAR('P'), -1, -1
8776 Xalpha_q, TRUE, FALSE,
8777 EL_CHAR('Q'), -1, -1
8780 Xalpha_r, TRUE, FALSE,
8781 EL_CHAR('R'), -1, -1
8784 Xalpha_s, TRUE, FALSE,
8785 EL_CHAR('S'), -1, -1
8788 Xalpha_t, TRUE, FALSE,
8789 EL_CHAR('T'), -1, -1
8792 Xalpha_u, TRUE, FALSE,
8793 EL_CHAR('U'), -1, -1
8796 Xalpha_v, TRUE, FALSE,
8797 EL_CHAR('V'), -1, -1
8800 Xalpha_w, TRUE, FALSE,
8801 EL_CHAR('W'), -1, -1
8804 Xalpha_x, TRUE, FALSE,
8805 EL_CHAR('X'), -1, -1
8808 Xalpha_y, TRUE, FALSE,
8809 EL_CHAR('Y'), -1, -1
8812 Xalpha_z, TRUE, FALSE,
8813 EL_CHAR('Z'), -1, -1
8816 Xalpha_arrow_e, TRUE, FALSE,
8817 EL_CHAR('>'), -1, -1
8820 Xalpha_arrow_w, TRUE, FALSE,
8821 EL_CHAR('<'), -1, -1
8824 Xalpha_copyr, TRUE, FALSE,
8825 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
8829 Ykey_1_blank, FALSE, FALSE,
8830 EL_EM_KEY_1, ACTION_COLLECTING, -1
8833 Ykey_2_blank, FALSE, FALSE,
8834 EL_EM_KEY_2, ACTION_COLLECTING, -1
8837 Ykey_3_blank, FALSE, FALSE,
8838 EL_EM_KEY_3, ACTION_COLLECTING, -1
8841 Ykey_4_blank, FALSE, FALSE,
8842 EL_EM_KEY_4, ACTION_COLLECTING, -1
8845 Ykey_5_blank, FALSE, FALSE,
8846 EL_EMC_KEY_5, ACTION_COLLECTING, -1
8849 Ykey_6_blank, FALSE, FALSE,
8850 EL_EMC_KEY_6, ACTION_COLLECTING, -1
8853 Ykey_7_blank, FALSE, FALSE,
8854 EL_EMC_KEY_7, ACTION_COLLECTING, -1
8857 Ykey_8_blank, FALSE, FALSE,
8858 EL_EMC_KEY_8, ACTION_COLLECTING, -1
8861 Ylenses_blank, FALSE, FALSE,
8862 EL_EMC_LENSES, ACTION_COLLECTING, -1
8865 Ymagnify_blank, FALSE, FALSE,
8866 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
8869 Ygrass_blank, FALSE, FALSE,
8870 EL_EMC_GRASS, ACTION_SNAPPING, -1
8873 Ydirt_blank, FALSE, FALSE,
8874 EL_SAND, ACTION_SNAPPING, -1
8883 static struct Mapping_EM_to_RND_player
8892 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
8896 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
8900 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
8904 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
8908 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
8912 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
8916 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
8920 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
8924 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
8928 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
8932 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
8936 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
8940 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
8944 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
8948 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
8952 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
8956 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
8960 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
8964 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
8968 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
8972 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
8976 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
8980 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
8984 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
8988 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
8992 EL_PLAYER_1, ACTION_DEFAULT, -1,
8996 EL_PLAYER_2, ACTION_DEFAULT, -1,
9000 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
9004 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
9008 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
9012 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
9016 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
9020 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
9024 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
9028 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
9032 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
9036 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
9040 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
9044 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
9048 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
9052 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
9056 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
9060 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
9064 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
9068 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
9072 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
9076 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
9080 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
9084 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
9088 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
9092 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
9096 EL_PLAYER_3, ACTION_DEFAULT, -1,
9100 EL_PLAYER_4, ACTION_DEFAULT, -1,
9109 int map_element_RND_to_EM_cave(int element_rnd)
9111 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
9112 static boolean mapping_initialized = FALSE;
9114 if (!mapping_initialized)
9118 // return "Xalpha_quest" for all undefined elements in mapping array
9119 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9120 mapping_RND_to_EM[i] = Xalpha_quest;
9122 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
9123 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
9124 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
9125 em_object_mapping_list[i].element_em;
9127 mapping_initialized = TRUE;
9130 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
9132 Warn("invalid RND level element %d", element_rnd);
9137 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
9140 int map_element_EM_to_RND_cave(int element_em_cave)
9142 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
9143 static boolean mapping_initialized = FALSE;
9145 if (!mapping_initialized)
9149 // return "EL_UNKNOWN" for all undefined elements in mapping array
9150 for (i = 0; i < GAME_TILE_MAX; i++)
9151 mapping_EM_to_RND[i] = EL_UNKNOWN;
9153 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
9154 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
9155 em_object_mapping_list[i].element_rnd;
9157 mapping_initialized = TRUE;
9160 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
9162 Warn("invalid EM cave element %d", element_em_cave);
9167 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
9170 int map_element_EM_to_RND_game(int element_em_game)
9172 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
9173 static boolean mapping_initialized = FALSE;
9175 if (!mapping_initialized)
9179 // return "EL_UNKNOWN" for all undefined elements in mapping array
9180 for (i = 0; i < GAME_TILE_MAX; i++)
9181 mapping_EM_to_RND[i] = EL_UNKNOWN;
9183 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
9184 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
9185 em_object_mapping_list[i].element_rnd;
9187 mapping_initialized = TRUE;
9190 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
9192 Warn("invalid EM game element %d", element_em_game);
9197 return mapping_EM_to_RND[element_em_game];
9200 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
9202 struct LevelInfo_EM *level_em = level->native_em_level;
9203 struct CAVE *cav = level_em->cav;
9206 for (i = 0; i < GAME_TILE_MAX; i++)
9207 cav->android_array[i] = Cblank;
9209 for (i = 0; i < level->num_android_clone_elements; i++)
9211 int element_rnd = level->android_clone_element[i];
9212 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
9214 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
9215 if (em_object_mapping_list[j].element_rnd == element_rnd)
9216 cav->android_array[em_object_mapping_list[j].element_em] =
9221 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
9223 struct LevelInfo_EM *level_em = level->native_em_level;
9224 struct CAVE *cav = level_em->cav;
9227 level->num_android_clone_elements = 0;
9229 for (i = 0; i < GAME_TILE_MAX; i++)
9231 int element_em_cave = cav->android_array[i];
9233 boolean element_found = FALSE;
9235 if (element_em_cave == Cblank)
9238 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
9240 for (j = 0; j < level->num_android_clone_elements; j++)
9241 if (level->android_clone_element[j] == element_rnd)
9242 element_found = TRUE;
9246 level->android_clone_element[level->num_android_clone_elements++] =
9249 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
9254 if (level->num_android_clone_elements == 0)
9256 level->num_android_clone_elements = 1;
9257 level->android_clone_element[0] = EL_EMPTY;
9261 int map_direction_RND_to_EM(int direction)
9263 return (direction == MV_UP ? 0 :
9264 direction == MV_RIGHT ? 1 :
9265 direction == MV_DOWN ? 2 :
9266 direction == MV_LEFT ? 3 :
9270 int map_direction_EM_to_RND(int direction)
9272 return (direction == 0 ? MV_UP :
9273 direction == 1 ? MV_RIGHT :
9274 direction == 2 ? MV_DOWN :
9275 direction == 3 ? MV_LEFT :
9279 int map_element_RND_to_SP(int element_rnd)
9281 int element_sp = 0x20; // map unknown elements to yellow "hardware"
9283 if (element_rnd >= EL_SP_START &&
9284 element_rnd <= EL_SP_END)
9285 element_sp = element_rnd - EL_SP_START;
9286 else if (element_rnd == EL_EMPTY_SPACE)
9288 else if (element_rnd == EL_INVISIBLE_WALL)
9294 int map_element_SP_to_RND(int element_sp)
9296 int element_rnd = EL_UNKNOWN;
9298 if (element_sp >= 0x00 &&
9300 element_rnd = EL_SP_START + element_sp;
9301 else if (element_sp == 0x28)
9302 element_rnd = EL_INVISIBLE_WALL;
9307 int map_action_SP_to_RND(int action_sp)
9311 case actActive: return ACTION_ACTIVE;
9312 case actImpact: return ACTION_IMPACT;
9313 case actExploding: return ACTION_EXPLODING;
9314 case actDigging: return ACTION_DIGGING;
9315 case actSnapping: return ACTION_SNAPPING;
9316 case actCollecting: return ACTION_COLLECTING;
9317 case actPassing: return ACTION_PASSING;
9318 case actPushing: return ACTION_PUSHING;
9319 case actDropping: return ACTION_DROPPING;
9321 default: return ACTION_DEFAULT;
9325 int map_element_RND_to_MM(int element_rnd)
9327 return (element_rnd >= EL_MM_START_1 &&
9328 element_rnd <= EL_MM_END_1 ?
9329 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
9331 element_rnd >= EL_MM_START_2 &&
9332 element_rnd <= EL_MM_END_2 ?
9333 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
9335 element_rnd >= EL_MM_START_3 &&
9336 element_rnd <= EL_MM_END_3 ?
9337 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
9339 element_rnd >= EL_CHAR_START &&
9340 element_rnd <= EL_CHAR_END ?
9341 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
9343 element_rnd >= EL_MM_RUNTIME_START &&
9344 element_rnd <= EL_MM_RUNTIME_END ?
9345 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
9347 EL_MM_EMPTY_NATIVE);
9350 int map_element_MM_to_RND(int element_mm)
9352 return (element_mm == EL_MM_EMPTY_NATIVE ||
9353 element_mm == EL_DF_EMPTY_NATIVE ?
9356 element_mm >= EL_MM_START_1_NATIVE &&
9357 element_mm <= EL_MM_END_1_NATIVE ?
9358 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
9360 element_mm >= EL_MM_START_2_NATIVE &&
9361 element_mm <= EL_MM_END_2_NATIVE ?
9362 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
9364 element_mm >= EL_MM_START_3_NATIVE &&
9365 element_mm <= EL_MM_END_3_NATIVE ?
9366 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
9368 element_mm >= EL_MM_CHAR_START_NATIVE &&
9369 element_mm <= EL_MM_CHAR_END_NATIVE ?
9370 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
9372 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
9373 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
9374 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
9379 int map_action_MM_to_RND(int action_mm)
9381 // all MM actions are defined to exactly match their RND counterparts
9385 int map_sound_MM_to_RND(int sound_mm)
9389 case SND_MM_GAME_LEVELTIME_CHARGING:
9390 return SND_GAME_LEVELTIME_CHARGING;
9392 case SND_MM_GAME_HEALTH_CHARGING:
9393 return SND_GAME_HEALTH_CHARGING;
9396 return SND_UNDEFINED;
9400 int map_mm_wall_element(int element)
9402 return (element >= EL_MM_STEEL_WALL_START &&
9403 element <= EL_MM_STEEL_WALL_END ?
9406 element >= EL_MM_WOODEN_WALL_START &&
9407 element <= EL_MM_WOODEN_WALL_END ?
9410 element >= EL_MM_ICE_WALL_START &&
9411 element <= EL_MM_ICE_WALL_END ?
9414 element >= EL_MM_AMOEBA_WALL_START &&
9415 element <= EL_MM_AMOEBA_WALL_END ?
9418 element >= EL_DF_STEEL_WALL_START &&
9419 element <= EL_DF_STEEL_WALL_END ?
9422 element >= EL_DF_WOODEN_WALL_START &&
9423 element <= EL_DF_WOODEN_WALL_END ?
9429 int map_mm_wall_element_editor(int element)
9433 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
9434 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
9435 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
9436 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
9437 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
9438 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
9440 default: return element;
9444 int get_next_element(int element)
9448 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
9449 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
9450 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
9451 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
9452 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
9453 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
9454 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
9455 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
9456 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
9457 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
9458 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
9460 default: return element;
9464 int el2img_mm(int element_mm)
9466 return el2img(map_element_MM_to_RND(element_mm));
9469 int el_act2img_mm(int element_mm, int action)
9471 return el_act2img(map_element_MM_to_RND(element_mm), action);
9474 int el_act_dir2img(int element, int action, int direction)
9476 element = GFX_ELEMENT(element);
9477 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
9479 // direction_graphic[][] == graphic[] for undefined direction graphics
9480 return element_info[element].direction_graphic[action][direction];
9483 static int el_act_dir2crm(int element, int action, int direction)
9485 element = GFX_ELEMENT(element);
9486 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
9488 // direction_graphic[][] == graphic[] for undefined direction graphics
9489 return element_info[element].direction_crumbled[action][direction];
9492 int el_act2img(int element, int action)
9494 element = GFX_ELEMENT(element);
9496 return element_info[element].graphic[action];
9499 int el_act2crm(int element, int action)
9501 element = GFX_ELEMENT(element);
9503 return element_info[element].crumbled[action];
9506 int el_dir2img(int element, int direction)
9508 element = GFX_ELEMENT(element);
9510 return el_act_dir2img(element, ACTION_DEFAULT, direction);
9513 int el2baseimg(int element)
9515 return element_info[element].graphic[ACTION_DEFAULT];
9518 int el2img(int element)
9520 element = GFX_ELEMENT(element);
9522 return element_info[element].graphic[ACTION_DEFAULT];
9525 int el2edimg(int element)
9527 element = GFX_ELEMENT(element);
9529 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
9532 int el2preimg(int element)
9534 element = GFX_ELEMENT(element);
9536 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
9539 int el2panelimg(int element)
9541 element = GFX_ELEMENT(element);
9543 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
9546 int font2baseimg(int font_nr)
9548 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
9551 int getBeltNrFromBeltElement(int element)
9553 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
9554 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
9555 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
9558 int getBeltNrFromBeltActiveElement(int element)
9560 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
9561 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
9562 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
9565 int getBeltNrFromBeltSwitchElement(int element)
9567 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
9568 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
9569 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
9572 int getBeltDirNrFromBeltElement(int element)
9574 static int belt_base_element[4] =
9576 EL_CONVEYOR_BELT_1_LEFT,
9577 EL_CONVEYOR_BELT_2_LEFT,
9578 EL_CONVEYOR_BELT_3_LEFT,
9579 EL_CONVEYOR_BELT_4_LEFT
9582 int belt_nr = getBeltNrFromBeltElement(element);
9583 int belt_dir_nr = element - belt_base_element[belt_nr];
9585 return (belt_dir_nr % 3);
9588 int getBeltDirNrFromBeltSwitchElement(int element)
9590 static int belt_base_element[4] =
9592 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
9593 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
9594 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
9595 EL_CONVEYOR_BELT_4_SWITCH_LEFT
9598 int belt_nr = getBeltNrFromBeltSwitchElement(element);
9599 int belt_dir_nr = element - belt_base_element[belt_nr];
9601 return (belt_dir_nr % 3);
9604 int getBeltDirFromBeltElement(int element)
9606 static int belt_move_dir[3] =
9613 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
9615 return belt_move_dir[belt_dir_nr];
9618 int getBeltDirFromBeltSwitchElement(int element)
9620 static int belt_move_dir[3] =
9627 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
9629 return belt_move_dir[belt_dir_nr];
9632 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
9634 static int belt_base_element[4] =
9636 EL_CONVEYOR_BELT_1_LEFT,
9637 EL_CONVEYOR_BELT_2_LEFT,
9638 EL_CONVEYOR_BELT_3_LEFT,
9639 EL_CONVEYOR_BELT_4_LEFT
9642 return belt_base_element[belt_nr] + belt_dir_nr;
9645 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
9647 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
9649 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
9652 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
9654 static int belt_base_element[4] =
9656 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
9657 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
9658 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
9659 EL_CONVEYOR_BELT_4_SWITCH_LEFT
9662 return belt_base_element[belt_nr] + belt_dir_nr;
9665 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
9667 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
9669 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
9672 boolean swapTiles_EM(boolean is_pre_emc_cave)
9674 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
9677 boolean getTeamMode_EM(void)
9679 return game.team_mode || network_playing;
9682 boolean isActivePlayer_EM(int player_nr)
9684 return stored_player[player_nr].active;
9687 unsigned int InitRND(int seed)
9689 if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
9690 return InitEngineRandom_BD(seed);
9691 else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9692 return InitEngineRandom_EM(seed);
9693 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
9694 return InitEngineRandom_SP(seed);
9695 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
9696 return InitEngineRandom_MM(seed);
9698 return InitEngineRandom_RND(seed);
9701 static struct Mapping_BD_to_RND_object bd_object_mapping[O_MAX_ALL];
9702 static struct Mapping_EM_to_RND_object em_object_mapping[GAME_TILE_MAX];
9703 static struct Mapping_EM_to_RND_player em_player_mapping[MAX_PLAYERS][PLY_MAX];
9705 static int get_effective_element_EM(int tile, int frame_em)
9707 int element = em_object_mapping[tile].element_rnd;
9708 int action = em_object_mapping[tile].action;
9709 boolean is_backside = em_object_mapping[tile].is_backside;
9710 boolean action_removing = (action == ACTION_DIGGING ||
9711 action == ACTION_SNAPPING ||
9712 action == ACTION_COLLECTING);
9720 return (frame_em > 5 ? EL_EMPTY : element);
9726 else // frame_em == 7
9737 case Ydiamond_stone:
9741 case Xdrip_stretchB:
9757 case Ymagnify_blank:
9760 case Xsand_stonein_1:
9761 case Xsand_stonein_2:
9762 case Xsand_stonein_3:
9763 case Xsand_stonein_4:
9767 return (is_backside || action_removing ? EL_EMPTY : element);
9772 static boolean check_linear_animation_EM(int tile)
9776 case Xsand_stonesand_1:
9777 case Xsand_stonesand_quickout_1:
9778 case Xsand_sandstone_1:
9779 case Xsand_stonein_1:
9780 case Xsand_stoneout_1:
9808 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
9809 boolean has_crumbled_graphics,
9810 int crumbled, int sync_frame)
9812 // if element can be crumbled, but certain action graphics are just empty
9813 // space (like instantly snapping sand to empty space in 1 frame), do not
9814 // treat these empty space graphics as crumbled graphics in EMC engine
9815 if (crumbled == IMG_EMPTY_SPACE)
9816 has_crumbled_graphics = FALSE;
9818 if (has_crumbled_graphics)
9820 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
9821 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
9822 g_crumbled->anim_delay,
9823 g_crumbled->anim_mode,
9824 g_crumbled->anim_start_frame,
9827 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
9828 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
9830 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
9831 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
9833 g_em->has_crumbled_graphics = TRUE;
9837 g_em->crumbled_bitmap = NULL;
9838 g_em->crumbled_src_x = 0;
9839 g_em->crumbled_src_y = 0;
9840 g_em->crumbled_border_size = 0;
9841 g_em->crumbled_tile_size = 0;
9843 g_em->has_crumbled_graphics = FALSE;
9848 void ResetGfxAnimation_EM(int x, int y, int tile)
9854 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
9855 int tile, int frame_em, int x, int y)
9857 int action = em_object_mapping[tile].action;
9858 int direction = em_object_mapping[tile].direction;
9859 int effective_element = get_effective_element_EM(tile, frame_em);
9860 int graphic = (direction == MV_NONE ?
9861 el_act2img(effective_element, action) :
9862 el_act_dir2img(effective_element, action, direction));
9863 struct GraphicInfo *g = &graphic_info[graphic];
9865 boolean action_removing = (action == ACTION_DIGGING ||
9866 action == ACTION_SNAPPING ||
9867 action == ACTION_COLLECTING);
9868 boolean action_moving = (action == ACTION_FALLING ||
9869 action == ACTION_MOVING ||
9870 action == ACTION_PUSHING ||
9871 action == ACTION_EATING ||
9872 action == ACTION_FILLING ||
9873 action == ACTION_EMPTYING);
9874 boolean action_falling = (action == ACTION_FALLING ||
9875 action == ACTION_FILLING ||
9876 action == ACTION_EMPTYING);
9878 // special case: graphic uses "2nd movement tile" and has defined
9879 // 7 frames for movement animation (or less) => use default graphic
9880 // for last (8th) frame which ends the movement animation
9881 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
9883 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
9884 graphic = (direction == MV_NONE ?
9885 el_act2img(effective_element, action) :
9886 el_act_dir2img(effective_element, action, direction));
9888 g = &graphic_info[graphic];
9891 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
9895 else if (action_moving)
9897 boolean is_backside = em_object_mapping[tile].is_backside;
9901 int direction = em_object_mapping[tile].direction;
9902 int move_dir = (action_falling ? MV_DOWN : direction);
9907 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
9908 if (g->double_movement && frame_em == 0)
9912 if (move_dir == MV_LEFT)
9913 GfxFrame[x - 1][y] = GfxFrame[x][y];
9914 else if (move_dir == MV_RIGHT)
9915 GfxFrame[x + 1][y] = GfxFrame[x][y];
9916 else if (move_dir == MV_UP)
9917 GfxFrame[x][y - 1] = GfxFrame[x][y];
9918 else if (move_dir == MV_DOWN)
9919 GfxFrame[x][y + 1] = GfxFrame[x][y];
9926 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
9927 if (tile == Xsand_stonesand_quickout_1 ||
9928 tile == Xsand_stonesand_quickout_2)
9932 if (graphic_info[graphic].anim_global_sync)
9933 sync_frame = FrameCounter;
9934 else if (graphic_info[graphic].anim_global_anim_sync)
9935 sync_frame = getGlobalAnimSyncFrame();
9936 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
9937 sync_frame = GfxFrame[x][y];
9939 sync_frame = 0; // playfield border (pseudo steel)
9941 SetRandomAnimationValue(x, y);
9943 int frame = getAnimationFrame(g->anim_frames,
9946 g->anim_start_frame,
9949 g_em->unique_identifier =
9950 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
9953 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
9954 int tile, int frame_em, int x, int y)
9956 int action = em_object_mapping[tile].action;
9957 int direction = em_object_mapping[tile].direction;
9958 boolean is_backside = em_object_mapping[tile].is_backside;
9959 int effective_element = get_effective_element_EM(tile, frame_em);
9960 int effective_action = action;
9961 int graphic = (direction == MV_NONE ?
9962 el_act2img(effective_element, effective_action) :
9963 el_act_dir2img(effective_element, effective_action,
9965 int crumbled = (direction == MV_NONE ?
9966 el_act2crm(effective_element, effective_action) :
9967 el_act_dir2crm(effective_element, effective_action,
9969 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
9970 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
9971 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9972 struct GraphicInfo *g = &graphic_info[graphic];
9975 // special case: graphic uses "2nd movement tile" and has defined
9976 // 7 frames for movement animation (or less) => use default graphic
9977 // for last (8th) frame which ends the movement animation
9978 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
9980 effective_action = ACTION_DEFAULT;
9981 graphic = (direction == MV_NONE ?
9982 el_act2img(effective_element, effective_action) :
9983 el_act_dir2img(effective_element, effective_action,
9985 crumbled = (direction == MV_NONE ?
9986 el_act2crm(effective_element, effective_action) :
9987 el_act_dir2crm(effective_element, effective_action,
9990 g = &graphic_info[graphic];
9993 if (graphic_info[graphic].anim_global_sync)
9994 sync_frame = FrameCounter;
9995 else if (graphic_info[graphic].anim_global_anim_sync)
9996 sync_frame = getGlobalAnimSyncFrame();
9997 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
9998 sync_frame = GfxFrame[x][y];
10000 sync_frame = 0; // playfield border (pseudo steel)
10002 SetRandomAnimationValue(x, y);
10004 int frame = getAnimationFrame(g->anim_frames,
10007 g->anim_start_frame,
10010 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
10011 g->double_movement && is_backside);
10013 // (updating the "crumbled" graphic definitions is probably not really needed,
10014 // as animations for crumbled graphics can't be longer than one EMC cycle)
10015 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
10019 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
10020 int player_nr, int anim, int frame_em)
10022 int element = em_player_mapping[player_nr][anim].element_rnd;
10023 int action = em_player_mapping[player_nr][anim].action;
10024 int direction = em_player_mapping[player_nr][anim].direction;
10025 int graphic = (direction == MV_NONE ?
10026 el_act2img(element, action) :
10027 el_act_dir2img(element, action, direction));
10028 struct GraphicInfo *g = &graphic_info[graphic];
10031 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
10033 stored_player[player_nr].StepFrame = frame_em;
10035 sync_frame = stored_player[player_nr].Frame;
10037 int frame = getAnimationFrame(g->anim_frames,
10040 g->anim_start_frame,
10043 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
10044 &g_em->src_x, &g_em->src_y, FALSE);
10047 #define BD_GFX_RANGE(a, n, i) ((i) >= (a) && (i) < (a) + (n))
10048 #define BD_GFX_FRAME(b, i) (((i) - (b)) * 8)
10050 void InitGraphicInfo_BD(void)
10054 // always start with reliable default values
10055 for (i = 0; i < O_MAX_ALL; i++)
10057 bd_object_mapping[i].element_rnd = EL_UNKNOWN;
10058 bd_object_mapping[i].action = ACTION_DEFAULT;
10059 bd_object_mapping[i].direction = MV_NONE;
10062 for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
10064 int e = bd_object_mapping_list[i].element_bd;
10066 bd_object_mapping[e].element_rnd = bd_object_mapping_list[i].element_rnd;
10068 if (bd_object_mapping_list[i].action != -1)
10069 bd_object_mapping[e].action = bd_object_mapping_list[i].action;
10071 if (bd_object_mapping_list[i].direction != -1)
10072 bd_object_mapping[e].direction =
10073 MV_DIR_FROM_BIT(bd_object_mapping_list[i].direction);
10076 for (i = 0; i < O_MAX_ALL; i++)
10078 int element = bd_object_mapping[i].element_rnd;
10079 int action = bd_object_mapping[i].action;
10080 int direction = bd_object_mapping[i].direction;
10082 for (j = 0; j < 8; j++)
10084 int effective_element = element;
10085 int effective_action = action;
10086 int graphic = (el_act_dir2img(effective_element, effective_action,
10088 struct GraphicInfo *g = &graphic_info[graphic];
10089 struct GraphicInfo_BD *g_bd = &graphic_info_bd_object[i][j];
10090 Bitmap *src_bitmap;
10092 int sync_frame = (BD_GFX_RANGE(O_PRE_PL_1, 3, i) ? BD_GFX_FRAME(O_PRE_PL_1, i) :
10093 BD_GFX_RANGE(O_PRE_DIA_1, 5, i) ? BD_GFX_FRAME(O_PRE_DIA_1, i) :
10094 BD_GFX_RANGE(O_PRE_STONE_1, 4, i) ? BD_GFX_FRAME(O_PRE_STONE_1, i) :
10095 BD_GFX_RANGE(O_PRE_STEEL_1, 4, i) ? BD_GFX_FRAME(O_PRE_STEEL_1, i) :
10096 BD_GFX_RANGE(O_BOMB_TICK_1, 7, i) ? BD_GFX_FRAME(O_BOMB_TICK_1, i) :
10097 BD_GFX_RANGE(O_BOMB_EXPL_1, 4, i) ? BD_GFX_FRAME(O_BOMB_EXPL_1, i) :
10098 BD_GFX_RANGE(O_NUT_EXPL_1, 4, i) ? BD_GFX_FRAME(O_NUT_EXPL_1, i) :
10099 BD_GFX_RANGE(O_GHOST_EXPL_1, 4, i) ? BD_GFX_FRAME(O_GHOST_EXPL_1, i) :
10100 BD_GFX_RANGE(O_EXPLODE_1, 5, i) ? BD_GFX_FRAME(O_EXPLODE_1, i) :
10101 BD_GFX_RANGE(O_PRE_CLOCK_1, 4, i) ? BD_GFX_FRAME(O_PRE_CLOCK_1, i) :
10102 BD_GFX_RANGE(O_NITRO_EXPL_1, 4, i) ? BD_GFX_FRAME(O_NITRO_EXPL_1, i) :
10103 BD_GFX_RANGE(O_AMOEBA_2_EXPL_1, 4, i) ? BD_GFX_FRAME(O_AMOEBA_2_EXPL_1, i):
10104 i == O_INBOX_OPEN || i == O_OUTBOX_OPEN ? j :
10106 int frame = getAnimationFrame(g->anim_frames,
10109 g->anim_start_frame,
10112 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
10114 g_bd->bitmap = src_bitmap;
10115 g_bd->src_x = src_x;
10116 g_bd->src_y = src_y;
10117 g_bd->width = TILEX;
10118 g_bd->height = TILEY;
10123 void InitGraphicInfo_EM(void)
10127 // always start with reliable default values
10128 for (i = 0; i < GAME_TILE_MAX; i++)
10130 em_object_mapping[i].element_rnd = EL_UNKNOWN;
10131 em_object_mapping[i].is_backside = FALSE;
10132 em_object_mapping[i].action = ACTION_DEFAULT;
10133 em_object_mapping[i].direction = MV_NONE;
10136 // always start with reliable default values
10137 for (p = 0; p < MAX_PLAYERS; p++)
10139 for (i = 0; i < PLY_MAX; i++)
10141 em_player_mapping[p][i].element_rnd = EL_UNKNOWN;
10142 em_player_mapping[p][i].action = ACTION_DEFAULT;
10143 em_player_mapping[p][i].direction = MV_NONE;
10147 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
10149 int e = em_object_mapping_list[i].element_em;
10151 em_object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
10152 em_object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
10154 if (em_object_mapping_list[i].action != -1)
10155 em_object_mapping[e].action = em_object_mapping_list[i].action;
10157 if (em_object_mapping_list[i].direction != -1)
10158 em_object_mapping[e].direction =
10159 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
10162 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
10164 int a = em_player_mapping_list[i].action_em;
10165 int p = em_player_mapping_list[i].player_nr;
10167 em_player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
10169 if (em_player_mapping_list[i].action != -1)
10170 em_player_mapping[p][a].action = em_player_mapping_list[i].action;
10172 if (em_player_mapping_list[i].direction != -1)
10173 em_player_mapping[p][a].direction =
10174 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
10177 for (i = 0; i < GAME_TILE_MAX; i++)
10179 int element = em_object_mapping[i].element_rnd;
10180 int action = em_object_mapping[i].action;
10181 int direction = em_object_mapping[i].direction;
10182 boolean is_backside = em_object_mapping[i].is_backside;
10183 boolean action_exploding = ((action == ACTION_EXPLODING ||
10184 action == ACTION_SMASHED_BY_ROCK ||
10185 action == ACTION_SMASHED_BY_SPRING) &&
10186 element != EL_DIAMOND);
10187 boolean action_active = (action == ACTION_ACTIVE);
10188 boolean action_other = (action == ACTION_OTHER);
10190 for (j = 0; j < 8; j++)
10192 int effective_element = get_effective_element_EM(i, j);
10193 int effective_action = (j < 7 ? action :
10194 i == Xdrip_stretch ? action :
10195 i == Xdrip_stretchB ? action :
10196 i == Ydrip_1_s ? action :
10197 i == Ydrip_1_sB ? action :
10198 i == Yball_1 ? action :
10199 i == Xball_2 ? action :
10200 i == Yball_2 ? action :
10201 i == Yball_blank ? action :
10202 i == Ykey_1_blank ? action :
10203 i == Ykey_2_blank ? action :
10204 i == Ykey_3_blank ? action :
10205 i == Ykey_4_blank ? action :
10206 i == Ykey_5_blank ? action :
10207 i == Ykey_6_blank ? action :
10208 i == Ykey_7_blank ? action :
10209 i == Ykey_8_blank ? action :
10210 i == Ylenses_blank ? action :
10211 i == Ymagnify_blank ? action :
10212 i == Ygrass_blank ? action :
10213 i == Ydirt_blank ? action :
10214 i == Xsand_stonein_1 ? action :
10215 i == Xsand_stonein_2 ? action :
10216 i == Xsand_stonein_3 ? action :
10217 i == Xsand_stonein_4 ? action :
10218 i == Xsand_stoneout_1 ? action :
10219 i == Xsand_stoneout_2 ? action :
10220 i == Xboom_android ? ACTION_EXPLODING :
10221 action_exploding ? ACTION_EXPLODING :
10222 action_active ? action :
10223 action_other ? action :
10225 int graphic = (el_act_dir2img(effective_element, effective_action,
10227 int crumbled = (el_act_dir2crm(effective_element, effective_action,
10229 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
10230 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
10231 boolean has_action_graphics = (graphic != base_graphic);
10232 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
10233 struct GraphicInfo *g = &graphic_info[graphic];
10234 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
10235 Bitmap *src_bitmap;
10237 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
10238 boolean special_animation = (action != ACTION_DEFAULT &&
10239 g->anim_frames == 3 &&
10240 g->anim_delay == 2 &&
10241 g->anim_mode & ANIM_LINEAR);
10242 int sync_frame = (i == Xdrip_stretch ? 7 :
10243 i == Xdrip_stretchB ? 7 :
10244 i == Ydrip_2_s ? j + 8 :
10245 i == Ydrip_2_sB ? j + 8 :
10247 i == Xacid_2 ? 10 :
10248 i == Xacid_3 ? 20 :
10249 i == Xacid_4 ? 30 :
10250 i == Xacid_5 ? 40 :
10251 i == Xacid_6 ? 50 :
10252 i == Xacid_7 ? 60 :
10253 i == Xacid_8 ? 70 :
10254 i == Xfake_acid_1 ? 0 :
10255 i == Xfake_acid_2 ? 10 :
10256 i == Xfake_acid_3 ? 20 :
10257 i == Xfake_acid_4 ? 30 :
10258 i == Xfake_acid_5 ? 40 :
10259 i == Xfake_acid_6 ? 50 :
10260 i == Xfake_acid_7 ? 60 :
10261 i == Xfake_acid_8 ? 70 :
10262 i == Xfake_acid_1_player ? 0 :
10263 i == Xfake_acid_2_player ? 10 :
10264 i == Xfake_acid_3_player ? 20 :
10265 i == Xfake_acid_4_player ? 30 :
10266 i == Xfake_acid_5_player ? 40 :
10267 i == Xfake_acid_6_player ? 50 :
10268 i == Xfake_acid_7_player ? 60 :
10269 i == Xfake_acid_8_player ? 70 :
10271 i == Yball_2 ? j + 8 :
10272 i == Yball_blank ? j + 1 :
10273 i == Ykey_1_blank ? j + 1 :
10274 i == Ykey_2_blank ? j + 1 :
10275 i == Ykey_3_blank ? j + 1 :
10276 i == Ykey_4_blank ? j + 1 :
10277 i == Ykey_5_blank ? j + 1 :
10278 i == Ykey_6_blank ? j + 1 :
10279 i == Ykey_7_blank ? j + 1 :
10280 i == Ykey_8_blank ? j + 1 :
10281 i == Ylenses_blank ? j + 1 :
10282 i == Ymagnify_blank ? j + 1 :
10283 i == Ygrass_blank ? j + 1 :
10284 i == Ydirt_blank ? j + 1 :
10285 i == Xamoeba_1 ? 0 :
10286 i == Xamoeba_2 ? 1 :
10287 i == Xamoeba_3 ? 2 :
10288 i == Xamoeba_4 ? 3 :
10289 i == Xamoeba_5 ? 0 :
10290 i == Xamoeba_6 ? 1 :
10291 i == Xamoeba_7 ? 2 :
10292 i == Xamoeba_8 ? 3 :
10293 i == Xexit_2 ? j + 8 :
10294 i == Xexit_3 ? j + 16 :
10295 i == Xdynamite_1 ? 0 :
10296 i == Xdynamite_2 ? 8 :
10297 i == Xdynamite_3 ? 16 :
10298 i == Xdynamite_4 ? 24 :
10299 i == Xsand_stonein_1 ? j + 1 :
10300 i == Xsand_stonein_2 ? j + 9 :
10301 i == Xsand_stonein_3 ? j + 17 :
10302 i == Xsand_stonein_4 ? j + 25 :
10303 i == Xsand_stoneout_1 && j == 0 ? 0 :
10304 i == Xsand_stoneout_1 && j == 1 ? 0 :
10305 i == Xsand_stoneout_1 && j == 2 ? 1 :
10306 i == Xsand_stoneout_1 && j == 3 ? 2 :
10307 i == Xsand_stoneout_1 && j == 4 ? 2 :
10308 i == Xsand_stoneout_1 && j == 5 ? 3 :
10309 i == Xsand_stoneout_1 && j == 6 ? 4 :
10310 i == Xsand_stoneout_1 && j == 7 ? 4 :
10311 i == Xsand_stoneout_2 && j == 0 ? 5 :
10312 i == Xsand_stoneout_2 && j == 1 ? 6 :
10313 i == Xsand_stoneout_2 && j == 2 ? 7 :
10314 i == Xsand_stoneout_2 && j == 3 ? 8 :
10315 i == Xsand_stoneout_2 && j == 4 ? 9 :
10316 i == Xsand_stoneout_2 && j == 5 ? 11 :
10317 i == Xsand_stoneout_2 && j == 6 ? 13 :
10318 i == Xsand_stoneout_2 && j == 7 ? 15 :
10319 i == Xboom_bug && j == 1 ? 2 :
10320 i == Xboom_bug && j == 2 ? 2 :
10321 i == Xboom_bug && j == 3 ? 4 :
10322 i == Xboom_bug && j == 4 ? 4 :
10323 i == Xboom_bug && j == 5 ? 2 :
10324 i == Xboom_bug && j == 6 ? 2 :
10325 i == Xboom_bug && j == 7 ? 0 :
10326 i == Xboom_tank && j == 1 ? 2 :
10327 i == Xboom_tank && j == 2 ? 2 :
10328 i == Xboom_tank && j == 3 ? 4 :
10329 i == Xboom_tank && j == 4 ? 4 :
10330 i == Xboom_tank && j == 5 ? 2 :
10331 i == Xboom_tank && j == 6 ? 2 :
10332 i == Xboom_tank && j == 7 ? 0 :
10333 i == Xboom_android && j == 7 ? 6 :
10334 i == Xboom_1 && j == 1 ? 2 :
10335 i == Xboom_1 && j == 2 ? 2 :
10336 i == Xboom_1 && j == 3 ? 4 :
10337 i == Xboom_1 && j == 4 ? 4 :
10338 i == Xboom_1 && j == 5 ? 6 :
10339 i == Xboom_1 && j == 6 ? 6 :
10340 i == Xboom_1 && j == 7 ? 8 :
10341 i == Xboom_2 && j == 0 ? 8 :
10342 i == Xboom_2 && j == 1 ? 8 :
10343 i == Xboom_2 && j == 2 ? 10 :
10344 i == Xboom_2 && j == 3 ? 10 :
10345 i == Xboom_2 && j == 4 ? 10 :
10346 i == Xboom_2 && j == 5 ? 12 :
10347 i == Xboom_2 && j == 6 ? 12 :
10348 i == Xboom_2 && j == 7 ? 12 :
10349 special_animation && j == 4 ? 3 :
10350 effective_action != action ? 0 :
10352 int frame = getAnimationFrame(g->anim_frames,
10355 g->anim_start_frame,
10358 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
10359 g->double_movement && is_backside);
10361 g_em->bitmap = src_bitmap;
10362 g_em->src_x = src_x;
10363 g_em->src_y = src_y;
10364 g_em->src_offset_x = 0;
10365 g_em->src_offset_y = 0;
10366 g_em->dst_offset_x = 0;
10367 g_em->dst_offset_y = 0;
10368 g_em->width = TILEX;
10369 g_em->height = TILEY;
10371 g_em->preserve_background = FALSE;
10373 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
10376 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
10377 effective_action == ACTION_MOVING ||
10378 effective_action == ACTION_PUSHING ||
10379 effective_action == ACTION_EATING)) ||
10380 (!has_action_graphics && (effective_action == ACTION_FILLING ||
10381 effective_action == ACTION_EMPTYING)))
10384 (effective_action == ACTION_FALLING ||
10385 effective_action == ACTION_FILLING ||
10386 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
10387 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
10388 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
10389 int num_steps = (i == Ydrip_1_s ? 16 :
10390 i == Ydrip_1_sB ? 16 :
10391 i == Ydrip_2_s ? 16 :
10392 i == Ydrip_2_sB ? 16 :
10393 i == Xsand_stonein_1 ? 32 :
10394 i == Xsand_stonein_2 ? 32 :
10395 i == Xsand_stonein_3 ? 32 :
10396 i == Xsand_stonein_4 ? 32 :
10397 i == Xsand_stoneout_1 ? 16 :
10398 i == Xsand_stoneout_2 ? 16 : 8);
10399 int cx = ABS(dx) * (TILEX / num_steps);
10400 int cy = ABS(dy) * (TILEY / num_steps);
10401 int step_frame = (i == Ydrip_2_s ? j + 8 :
10402 i == Ydrip_2_sB ? j + 8 :
10403 i == Xsand_stonein_2 ? j + 8 :
10404 i == Xsand_stonein_3 ? j + 16 :
10405 i == Xsand_stonein_4 ? j + 24 :
10406 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
10407 int step = (is_backside ? step_frame : num_steps - step_frame);
10409 if (is_backside) // tile where movement starts
10411 if (dx < 0 || dy < 0)
10413 g_em->src_offset_x = cx * step;
10414 g_em->src_offset_y = cy * step;
10418 g_em->dst_offset_x = cx * step;
10419 g_em->dst_offset_y = cy * step;
10422 else // tile where movement ends
10424 if (dx < 0 || dy < 0)
10426 g_em->dst_offset_x = cx * step;
10427 g_em->dst_offset_y = cy * step;
10431 g_em->src_offset_x = cx * step;
10432 g_em->src_offset_y = cy * step;
10436 g_em->width = TILEX - cx * step;
10437 g_em->height = TILEY - cy * step;
10440 // create unique graphic identifier to decide if tile must be redrawn
10441 /* bit 31 - 16 (16 bit): EM style graphic
10442 bit 15 - 12 ( 4 bit): EM style frame
10443 bit 11 - 6 ( 6 bit): graphic width
10444 bit 5 - 0 ( 6 bit): graphic height */
10445 g_em->unique_identifier =
10446 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
10450 for (i = 0; i < GAME_TILE_MAX; i++)
10452 for (j = 0; j < 8; j++)
10454 int element = em_object_mapping[i].element_rnd;
10455 int action = em_object_mapping[i].action;
10456 int direction = em_object_mapping[i].direction;
10457 boolean is_backside = em_object_mapping[i].is_backside;
10458 int graphic_action = el_act_dir2img(element, action, direction);
10459 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
10461 if ((action == ACTION_SMASHED_BY_ROCK ||
10462 action == ACTION_SMASHED_BY_SPRING ||
10463 action == ACTION_EATING) &&
10464 graphic_action == graphic_default)
10466 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
10467 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
10468 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
10469 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
10472 // no separate animation for "smashed by rock" -- use rock instead
10473 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
10474 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
10476 g_em->bitmap = g_xx->bitmap;
10477 g_em->src_x = g_xx->src_x;
10478 g_em->src_y = g_xx->src_y;
10479 g_em->src_offset_x = g_xx->src_offset_x;
10480 g_em->src_offset_y = g_xx->src_offset_y;
10481 g_em->dst_offset_x = g_xx->dst_offset_x;
10482 g_em->dst_offset_y = g_xx->dst_offset_y;
10483 g_em->width = g_xx->width;
10484 g_em->height = g_xx->height;
10485 g_em->unique_identifier = g_xx->unique_identifier;
10488 g_em->preserve_background = TRUE;
10493 for (p = 0; p < MAX_PLAYERS; p++)
10495 for (i = 0; i < PLY_MAX; i++)
10497 int element = em_player_mapping[p][i].element_rnd;
10498 int action = em_player_mapping[p][i].action;
10499 int direction = em_player_mapping[p][i].direction;
10501 for (j = 0; j < 8; j++)
10503 int effective_element = element;
10504 int effective_action = action;
10505 int graphic = (direction == MV_NONE ?
10506 el_act2img(effective_element, effective_action) :
10507 el_act_dir2img(effective_element, effective_action,
10509 struct GraphicInfo *g = &graphic_info[graphic];
10510 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
10511 Bitmap *src_bitmap;
10513 int sync_frame = j;
10514 int frame = getAnimationFrame(g->anim_frames,
10517 g->anim_start_frame,
10520 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
10522 g_em->bitmap = src_bitmap;
10523 g_em->src_x = src_x;
10524 g_em->src_y = src_y;
10525 g_em->src_offset_x = 0;
10526 g_em->src_offset_y = 0;
10527 g_em->dst_offset_x = 0;
10528 g_em->dst_offset_y = 0;
10529 g_em->width = TILEX;
10530 g_em->height = TILEY;
10536 static void CheckSaveEngineSnapshot_EM(int frame,
10537 boolean any_player_moving,
10538 boolean any_player_snapping,
10539 boolean any_player_dropping)
10541 if (frame == 7 && !any_player_dropping)
10543 if (!local_player->was_waiting)
10545 if (!CheckSaveEngineSnapshotToList())
10548 local_player->was_waiting = TRUE;
10551 else if (any_player_moving || any_player_snapping || any_player_dropping)
10553 local_player->was_waiting = FALSE;
10557 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
10558 boolean murphy_is_dropping)
10560 if (murphy_is_waiting)
10562 if (!local_player->was_waiting)
10564 if (!CheckSaveEngineSnapshotToList())
10567 local_player->was_waiting = TRUE;
10572 local_player->was_waiting = FALSE;
10576 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
10577 boolean button_released)
10579 if (button_released)
10581 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
10582 CheckSaveEngineSnapshotToList();
10584 else if (element_clicked)
10586 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
10587 CheckSaveEngineSnapshotToList();
10589 game.snapshot.changed_action = TRUE;
10593 boolean CheckSingleStepMode_EM(int frame,
10594 boolean any_player_moving,
10595 boolean any_player_snapping,
10596 boolean any_player_dropping)
10598 if (tape.single_step && tape.recording && !tape.pausing)
10599 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
10600 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10602 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
10603 any_player_snapping, any_player_dropping);
10605 return tape.pausing;
10608 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
10609 boolean murphy_is_dropping)
10611 boolean murphy_starts_dropping = FALSE;
10614 for (i = 0; i < MAX_PLAYERS; i++)
10615 if (stored_player[i].force_dropping)
10616 murphy_starts_dropping = TRUE;
10618 if (tape.single_step && tape.recording && !tape.pausing)
10619 if (murphy_is_waiting && !murphy_starts_dropping)
10620 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10622 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
10625 void CheckSingleStepMode_MM(boolean element_clicked,
10626 boolean button_released)
10628 if (tape.single_step && tape.recording && !tape.pausing)
10629 if (button_released)
10630 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10632 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
10635 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
10636 int graphic, int sync_frame)
10638 int frame = getGraphicAnimationFrame(graphic, sync_frame);
10640 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
10643 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
10645 return (IS_NEXT_FRAME(sync_frame, graphic));
10648 int getGraphicInfo_Delay(int graphic)
10650 return graphic_info[graphic].anim_delay;
10653 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
10655 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
10658 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
10664 void PlayMenuSoundExt(int sound)
10666 if (sound == SND_UNDEFINED)
10669 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
10670 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
10673 if (IS_LOOP_SOUND(sound))
10674 PlaySoundLoop(sound);
10679 void PlayMenuSound(void)
10681 PlayMenuSoundExt(menu.sound[game_status]);
10684 void PlayMenuSoundStereo(int sound, int stereo_position)
10686 if (sound == SND_UNDEFINED)
10689 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
10690 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
10693 if (IS_LOOP_SOUND(sound))
10694 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
10696 PlaySoundStereo(sound, stereo_position);
10699 void PlayMenuSoundIfLoopExt(int sound)
10701 if (sound == SND_UNDEFINED)
10704 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
10705 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
10708 if (IS_LOOP_SOUND(sound))
10709 PlaySoundLoop(sound);
10712 void PlayMenuSoundIfLoop(void)
10714 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
10717 void PlayMenuMusicExt(int music)
10719 if (music == MUS_UNDEFINED)
10722 if (!setup.sound_music)
10725 if (IS_LOOP_MUSIC(music))
10726 PlayMusicLoop(music);
10731 void PlayMenuMusic(void)
10733 char *curr_music = getCurrentlyPlayingMusicFilename();
10734 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
10736 if (!strEqual(curr_music, next_music))
10737 PlayMenuMusicExt(menu.music[game_status]);
10740 void PlayMenuSoundsAndMusic(void)
10746 static void FadeMenuSounds(void)
10751 static void FadeMenuMusic(void)
10753 char *curr_music = getCurrentlyPlayingMusicFilename();
10754 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
10756 if (!strEqual(curr_music, next_music))
10760 void FadeMenuSoundsAndMusic(void)
10766 void PlaySoundActivating(void)
10769 PlaySound(SND_MENU_ITEM_ACTIVATING);
10773 void PlaySoundSelecting(void)
10776 PlaySound(SND_MENU_ITEM_SELECTING);
10780 void ToggleFullscreenIfNeeded(void)
10782 // if setup and video fullscreen state are already matching, nothing do do
10783 if (setup.fullscreen == video.fullscreen_enabled ||
10784 !video.fullscreen_available)
10787 SDLSetWindowFullscreen(setup.fullscreen);
10789 // set setup value according to successfully changed fullscreen mode
10790 setup.fullscreen = video.fullscreen_enabled;
10793 void ChangeWindowScalingIfNeeded(void)
10795 // if setup and video window scaling are already matching, nothing do do
10796 if (setup.window_scaling_percent == video.window_scaling_percent ||
10797 video.fullscreen_enabled)
10800 SDLSetWindowScaling(setup.window_scaling_percent);
10802 // set setup value according to successfully changed window scaling
10803 setup.window_scaling_percent = video.window_scaling_percent;
10806 void ChangeVsyncModeIfNeeded(void)
10808 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
10809 int video_vsync_mode = video.vsync_mode;
10811 // if setup and video vsync mode are already matching, nothing do do
10812 if (setup_vsync_mode == video_vsync_mode)
10815 // if renderer is using OpenGL, vsync mode can directly be changed
10816 SDLSetScreenVsyncMode(setup.vsync_mode);
10818 // if vsync mode unchanged, try re-creating renderer to set vsync mode
10819 if (video.vsync_mode == video_vsync_mode)
10821 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
10823 // save backbuffer content which gets lost when re-creating screen
10824 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
10826 // force re-creating screen and renderer to set new vsync mode
10827 video.fullscreen_enabled = !setup.fullscreen;
10829 // when creating new renderer, destroy textures linked to old renderer
10830 FreeAllImageTextures(); // needs old renderer to free the textures
10832 // re-create screen and renderer (including change of vsync mode)
10833 ChangeVideoModeIfNeeded(setup.fullscreen);
10835 // set setup value according to successfully changed fullscreen mode
10836 setup.fullscreen = video.fullscreen_enabled;
10838 // restore backbuffer content from temporary backbuffer backup bitmap
10839 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
10840 FreeBitmap(tmp_backbuffer);
10842 // update visible window/screen
10843 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
10845 // when changing vsync mode, re-create textures for new renderer
10846 InitImageTextures();
10849 // set setup value according to successfully changed vsync mode
10850 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
10853 static void JoinRectangles(int *x, int *y, int *width, int *height,
10854 int x2, int y2, int width2, int height2)
10856 // do not join with "off-screen" rectangle
10857 if (x2 == -1 || y2 == -1)
10862 *width = MAX(*width, width2);
10863 *height = MAX(*height, height2);
10866 void SetAnimStatus(int anim_status_new)
10868 if (anim_status_new == GAME_MODE_MAIN)
10869 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
10870 else if (anim_status_new == GAME_MODE_NAMES)
10871 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
10872 else if (anim_status_new == GAME_MODE_SCORES)
10873 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
10875 global.anim_status_next = anim_status_new;
10877 // directly set screen modes that are entered without fading
10878 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
10879 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
10880 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
10881 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
10882 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
10883 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
10884 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
10885 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
10886 global.anim_status = global.anim_status_next;
10889 void SetGameStatus(int game_status_new)
10891 if (game_status_new != game_status)
10892 game_status_last_screen = game_status;
10894 game_status = game_status_new;
10896 SetAnimStatus(game_status_new);
10899 void SetFontStatus(int game_status_new)
10901 static int last_game_status = -1;
10903 if (game_status_new != -1)
10905 // set game status for font use after storing last game status
10906 last_game_status = game_status;
10907 game_status = game_status_new;
10911 // reset game status after font use from last stored game status
10912 game_status = last_game_status;
10916 void ResetFontStatus(void)
10921 void SetLevelSetInfo(char *identifier, int level_nr)
10923 setString(&levelset.identifier, identifier);
10925 levelset.level_nr = level_nr;
10928 boolean CheckIfAllViewportsHaveChanged(void)
10930 // if game status has not changed, viewports have not changed either
10931 if (game_status == game_status_last)
10934 // check if all viewports have changed with current game status
10936 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
10937 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
10938 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
10939 int new_real_sx = vp_playfield->x;
10940 int new_real_sy = vp_playfield->y;
10941 int new_full_sxsize = vp_playfield->width;
10942 int new_full_sysize = vp_playfield->height;
10943 int new_dx = vp_door_1->x;
10944 int new_dy = vp_door_1->y;
10945 int new_dxsize = vp_door_1->width;
10946 int new_dysize = vp_door_1->height;
10947 int new_vx = vp_door_2->x;
10948 int new_vy = vp_door_2->y;
10949 int new_vxsize = vp_door_2->width;
10950 int new_vysize = vp_door_2->height;
10952 boolean playfield_viewport_has_changed =
10953 (new_real_sx != REAL_SX ||
10954 new_real_sy != REAL_SY ||
10955 new_full_sxsize != FULL_SXSIZE ||
10956 new_full_sysize != FULL_SYSIZE);
10958 boolean door_1_viewport_has_changed =
10961 new_dxsize != DXSIZE ||
10962 new_dysize != DYSIZE);
10964 boolean door_2_viewport_has_changed =
10967 new_vxsize != VXSIZE ||
10968 new_vysize != VYSIZE ||
10969 game_status_last == GAME_MODE_EDITOR);
10971 return (playfield_viewport_has_changed &&
10972 door_1_viewport_has_changed &&
10973 door_2_viewport_has_changed);
10976 boolean CheckFadeAll(void)
10978 return (CheckIfGlobalBorderHasChanged() ||
10979 CheckIfAllViewportsHaveChanged());
10982 void ChangeViewportPropertiesIfNeeded(void)
10984 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
10985 FALSE : setup.small_game_graphics);
10986 int gfx_game_mode = getGlobalGameStatus(game_status);
10987 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
10989 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
10990 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
10991 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
10992 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
10993 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
10994 int new_win_xsize = vp_window->width;
10995 int new_win_ysize = vp_window->height;
10996 int border_left = vp_playfield->border_left;
10997 int border_right = vp_playfield->border_right;
10998 int border_top = vp_playfield->border_top;
10999 int border_bottom = vp_playfield->border_bottom;
11000 int new_sx = vp_playfield->x + border_left;
11001 int new_sy = vp_playfield->y + border_top;
11002 int new_sxsize = vp_playfield->width - border_left - border_right;
11003 int new_sysize = vp_playfield->height - border_top - border_bottom;
11004 int new_real_sx = vp_playfield->x;
11005 int new_real_sy = vp_playfield->y;
11006 int new_full_sxsize = vp_playfield->width;
11007 int new_full_sysize = vp_playfield->height;
11008 int new_dx = vp_door_1->x;
11009 int new_dy = vp_door_1->y;
11010 int new_dxsize = vp_door_1->width;
11011 int new_dysize = vp_door_1->height;
11012 int new_vx = vp_door_2->x;
11013 int new_vy = vp_door_2->y;
11014 int new_vxsize = vp_door_2->width;
11015 int new_vysize = vp_door_2->height;
11016 int new_ex = vp_door_3->x;
11017 int new_ey = vp_door_3->y;
11018 int new_exsize = vp_door_3->width;
11019 int new_eysize = vp_door_3->height;
11020 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
11021 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
11022 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
11023 int new_scr_fieldx = new_sxsize / tilesize;
11024 int new_scr_fieldy = new_sysize / tilesize;
11025 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
11026 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
11027 boolean init_gfx_buffers = FALSE;
11028 boolean init_video_buffer = FALSE;
11029 boolean init_gadgets_and_anims = FALSE;
11030 boolean init_bd_graphics = FALSE;
11031 boolean init_em_graphics = FALSE;
11033 if (new_win_xsize != WIN_XSIZE ||
11034 new_win_ysize != WIN_YSIZE)
11036 WIN_XSIZE = new_win_xsize;
11037 WIN_YSIZE = new_win_ysize;
11039 init_video_buffer = TRUE;
11040 init_gfx_buffers = TRUE;
11041 init_gadgets_and_anims = TRUE;
11043 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
11046 if (new_scr_fieldx != SCR_FIELDX ||
11047 new_scr_fieldy != SCR_FIELDY)
11049 // this always toggles between MAIN and GAME when using small tile size
11051 SCR_FIELDX = new_scr_fieldx;
11052 SCR_FIELDY = new_scr_fieldy;
11054 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
11057 if (new_sx != SX ||
11065 new_sxsize != SXSIZE ||
11066 new_sysize != SYSIZE ||
11067 new_dxsize != DXSIZE ||
11068 new_dysize != DYSIZE ||
11069 new_vxsize != VXSIZE ||
11070 new_vysize != VYSIZE ||
11071 new_exsize != EXSIZE ||
11072 new_eysize != EYSIZE ||
11073 new_real_sx != REAL_SX ||
11074 new_real_sy != REAL_SY ||
11075 new_full_sxsize != FULL_SXSIZE ||
11076 new_full_sysize != FULL_SYSIZE ||
11077 new_tilesize_var != TILESIZE_VAR
11080 // ------------------------------------------------------------------------
11081 // determine next fading area for changed viewport definitions
11082 // ------------------------------------------------------------------------
11084 // start with current playfield area (default fading area)
11087 FADE_SXSIZE = FULL_SXSIZE;
11088 FADE_SYSIZE = FULL_SYSIZE;
11090 // add new playfield area if position or size has changed
11091 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
11092 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
11094 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11095 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
11098 // add current and new door 1 area if position or size has changed
11099 if (new_dx != DX || new_dy != DY ||
11100 new_dxsize != DXSIZE || new_dysize != DYSIZE)
11102 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11103 DX, DY, DXSIZE, DYSIZE);
11104 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11105 new_dx, new_dy, new_dxsize, new_dysize);
11108 // add current and new door 2 area if position or size has changed
11109 if (new_vx != VX || new_vy != VY ||
11110 new_vxsize != VXSIZE || new_vysize != VYSIZE)
11112 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11113 VX, VY, VXSIZE, VYSIZE);
11114 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11115 new_vx, new_vy, new_vxsize, new_vysize);
11118 // ------------------------------------------------------------------------
11119 // handle changed tile size
11120 // ------------------------------------------------------------------------
11122 if (new_tilesize_var != TILESIZE_VAR)
11124 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
11126 // changing tile size invalidates scroll values of engine snapshots
11127 FreeEngineSnapshotSingle();
11129 // changing tile size requires update of graphic mapping for BD/EM engine
11130 init_bd_graphics = TRUE;
11131 init_em_graphics = TRUE;
11142 SXSIZE = new_sxsize;
11143 SYSIZE = new_sysize;
11144 DXSIZE = new_dxsize;
11145 DYSIZE = new_dysize;
11146 VXSIZE = new_vxsize;
11147 VYSIZE = new_vysize;
11148 EXSIZE = new_exsize;
11149 EYSIZE = new_eysize;
11150 REAL_SX = new_real_sx;
11151 REAL_SY = new_real_sy;
11152 FULL_SXSIZE = new_full_sxsize;
11153 FULL_SYSIZE = new_full_sysize;
11154 TILESIZE_VAR = new_tilesize_var;
11156 init_gfx_buffers = TRUE;
11157 init_gadgets_and_anims = TRUE;
11159 // Debug("tools:viewport", "viewports: init_gfx_buffers");
11160 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
11163 if (init_gfx_buffers)
11165 // Debug("tools:viewport", "init_gfx_buffers");
11167 SCR_FIELDX = new_scr_fieldx_buffers;
11168 SCR_FIELDY = new_scr_fieldy_buffers;
11172 SCR_FIELDX = new_scr_fieldx;
11173 SCR_FIELDY = new_scr_fieldy;
11175 SetDrawDeactivationMask(REDRAW_NONE);
11176 SetDrawBackgroundMask(REDRAW_FIELD);
11179 if (init_video_buffer)
11181 // Debug("tools:viewport", "init_video_buffer");
11183 FreeAllImageTextures(); // needs old renderer to free the textures
11185 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
11186 InitImageTextures();
11189 if (init_gadgets_and_anims)
11191 // Debug("tools:viewport", "init_gadgets_and_anims");
11194 InitGlobalAnimations();
11197 if (init_bd_graphics)
11199 InitGraphicInfo_BD();
11202 if (init_em_graphics)
11204 InitGraphicInfo_EM();
11208 void OpenURL(char *url)
11210 #if SDL_VERSION_ATLEAST(2,0,14)
11213 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
11214 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
11215 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
11219 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
11221 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
11224 char *getCurrentLevelsetName(void)
11226 return leveldir_current->name;
11230 // ============================================================================
11232 // ============================================================================
11234 #if defined(PLATFORM_WINDOWS)
11235 /* FILETIME of Jan 1 1970 00:00:00. */
11236 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
11239 * timezone information is stored outside the kernel so tzp isn't used anymore.
11241 * Note: this function is not for Win32 high precision timing purpose. See
11244 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
11246 FILETIME file_time;
11247 SYSTEMTIME system_time;
11248 ULARGE_INTEGER ularge;
11250 GetSystemTime(&system_time);
11251 SystemTimeToFileTime(&system_time, &file_time);
11252 ularge.LowPart = file_time.dwLowDateTime;
11253 ularge.HighPart = file_time.dwHighDateTime;
11255 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
11256 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
11262 static char *test_init_uuid_random_function_simple(void)
11264 static char seed_text[100];
11265 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
11267 sprintf(seed_text, "%d", seed);
11272 static char *test_init_uuid_random_function_better(void)
11274 static char seed_text[100];
11275 struct timeval current_time;
11277 gettimeofday(¤t_time, NULL);
11279 prng_seed_bytes(¤t_time, sizeof(current_time));
11281 sprintf(seed_text, "%ld.%ld",
11282 (long)current_time.tv_sec,
11283 (long)current_time.tv_usec);
11288 #if defined(PLATFORM_WINDOWS)
11289 static char *test_init_uuid_random_function_better_windows(void)
11291 static char seed_text[100];
11292 struct timeval current_time;
11294 gettimeofday_windows(¤t_time, NULL);
11296 prng_seed_bytes(¤t_time, sizeof(current_time));
11298 sprintf(seed_text, "%ld.%ld",
11299 (long)current_time.tv_sec,
11300 (long)current_time.tv_usec);
11306 static unsigned int test_uuid_random_function_simple(int max)
11308 return GetSimpleRandom(max);
11311 static unsigned int test_uuid_random_function_better(int max)
11313 return (max > 0 ? prng_get_uint() % max : 0);
11316 #if defined(PLATFORM_WINDOWS)
11317 #define NUM_UUID_TESTS 3
11319 #define NUM_UUID_TESTS 2
11322 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
11324 HashTable *hash_seeds =
11325 create_hashtable(get_hash_from_string, hash_key_strings_are_equal, free, NULL);
11326 HashTable *hash_uuids =
11327 create_hashtable(get_hash_from_string, hash_key_strings_are_equal, free, NULL);
11328 static char message[100];
11331 char *random_name = (nr == 0 ? "simple" : "better");
11332 char *random_type = (always_seed ? "always" : "only once");
11333 char *(*init_random_function)(void) =
11335 test_init_uuid_random_function_simple :
11336 test_init_uuid_random_function_better);
11337 unsigned int (*random_function)(int) =
11339 test_uuid_random_function_simple :
11340 test_uuid_random_function_better);
11343 #if defined(PLATFORM_WINDOWS)
11346 random_name = "windows";
11347 init_random_function = test_init_uuid_random_function_better_windows;
11353 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
11354 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
11356 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
11357 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
11358 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
11360 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
11364 // always initialize random number generator at least once
11365 init_random_function();
11367 unsigned int time_start = SDL_GetTicks();
11369 for (i = 0; i < num_uuids; i++)
11373 char *seed = getStringCopy(init_random_function());
11375 hashtable_remove(hash_seeds, seed);
11376 hashtable_insert(hash_seeds, seed, "1");
11379 char *uuid = getStringCopy(getUUIDExt(random_function));
11381 hashtable_remove(hash_uuids, uuid);
11382 hashtable_insert(hash_uuids, uuid, "1");
11385 int num_unique_seeds = hashtable_count(hash_seeds);
11386 int num_unique_uuids = hashtable_count(hash_uuids);
11388 unsigned int time_needed = SDL_GetTicks() - time_start;
11390 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
11392 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
11395 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
11397 if (nr == NUM_UUID_TESTS - 1 && always_seed)
11398 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
11400 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
11402 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
11404 Request(message, REQ_CONFIRM);
11406 hashtable_destroy(hash_seeds);
11407 hashtable_destroy(hash_uuids);
11410 void TestGeneratingUUIDs(void)
11412 int num_uuids = 1000000;
11415 for (i = 0; i < NUM_UUID_TESTS; i++)
11416 for (j = 0; j < 2; j++)
11417 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
11419 CloseAllAndExit(0);