1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 #define DEBUG_FRAME_TIME FALSE
28 // tool button identifiers
29 #define TOOL_CTRL_ID_YES 0
30 #define TOOL_CTRL_ID_NO 1
31 #define TOOL_CTRL_ID_CONFIRM 2
32 #define TOOL_CTRL_ID_PLAYER_1 3
33 #define TOOL_CTRL_ID_PLAYER_2 4
34 #define TOOL_CTRL_ID_PLAYER_3 5
35 #define TOOL_CTRL_ID_PLAYER_4 6
36 #define TOOL_CTRL_ID_TOUCH_YES 7
37 #define TOOL_CTRL_ID_TOUCH_NO 8
38 #define TOOL_CTRL_ID_TOUCH_CONFIRM 9
40 #define NUM_TOOL_BUTTONS 10
42 // constants for number of doors and door parts
44 #define NUM_PANELS NUM_DOORS
45 // #define NUM_PANELS 0
46 #define MAX_PARTS_PER_DOOR 8
47 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
48 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
51 struct DoorPartOrderInfo
57 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
59 struct DoorPartControlInfo
63 struct DoorPartPosInfo *pos;
66 static struct DoorPartControlInfo door_part_controls[] =
70 IMG_GFX_DOOR_1_PART_1,
75 IMG_GFX_DOOR_1_PART_2,
80 IMG_GFX_DOOR_1_PART_3,
85 IMG_GFX_DOOR_1_PART_4,
90 IMG_GFX_DOOR_1_PART_5,
95 IMG_GFX_DOOR_1_PART_6,
100 IMG_GFX_DOOR_1_PART_7,
105 IMG_GFX_DOOR_1_PART_8,
111 IMG_GFX_DOOR_2_PART_1,
116 IMG_GFX_DOOR_2_PART_2,
121 IMG_GFX_DOOR_2_PART_3,
126 IMG_GFX_DOOR_2_PART_4,
131 IMG_GFX_DOOR_2_PART_5,
136 IMG_GFX_DOOR_2_PART_6,
141 IMG_GFX_DOOR_2_PART_7,
146 IMG_GFX_DOOR_2_PART_8,
152 IMG_BACKGROUND_PANEL,
168 static struct XY xy_topdown[] =
177 // forward declaration for internal use
178 static void MapToolButtons(unsigned int);
179 static void UnmapToolButtons(void);
180 static void HandleToolButtons(struct GadgetInfo *);
181 static int el_act_dir2crm(int, int, int);
182 static int el_act2crm(int, int);
184 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
185 static int request_gadget_id = -1;
187 static char *print_if_not_empty(int element)
189 static char *s = NULL;
190 char *token_name = element_info[element].token_name;
195 s = checked_malloc(strlen(token_name) + 10 + 1);
197 if (element != EL_EMPTY)
198 sprintf(s, "%d\t['%s']", element, token_name);
200 sprintf(s, "%d", element);
205 int getFieldbufferOffsetX_RND(int dir, int pos)
207 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
208 int dx = (dir & MV_HORIZONTAL ? pos : 0);
209 int dx_var = dx * TILESIZE_VAR / TILESIZE;
212 if (EVEN(SCR_FIELDX))
214 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
215 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
217 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
218 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
220 fx += (dx_var > 0 ? TILEX_VAR : 0);
227 if (full_lev_fieldx <= SCR_FIELDX)
229 if (EVEN(SCR_FIELDX))
230 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
232 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
238 int getFieldbufferOffsetY_RND(int dir, int pos)
240 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
241 int dy = (dir & MV_VERTICAL ? pos : 0);
242 int dy_var = dy * TILESIZE_VAR / TILESIZE;
245 if (EVEN(SCR_FIELDY))
247 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
248 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
250 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
251 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
253 fy += (dy_var > 0 ? TILEY_VAR : 0);
260 if (full_lev_fieldy <= SCR_FIELDY)
262 if (EVEN(SCR_FIELDY))
263 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
265 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
271 static int getLevelFromScreenX_RND(int sx)
273 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
276 int lx = LEVELX((px + dx) / TILESIZE_VAR);
281 static int getLevelFromScreenY_RND(int sy)
283 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
286 int ly = LEVELY((py + dy) / TILESIZE_VAR);
291 static int getLevelFromScreenX_EM(int sx)
293 int level_xsize = level.native_em_level->cav->width;
294 int full_xsize = level_xsize * TILESIZE_VAR;
296 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
298 int fx = getFieldbufferOffsetX_EM();
301 int lx = LEVELX((px + dx) / TILESIZE_VAR);
306 static int getLevelFromScreenY_EM(int sy)
308 int level_ysize = level.native_em_level->cav->height;
309 int full_ysize = level_ysize * TILESIZE_VAR;
311 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
313 int fy = getFieldbufferOffsetY_EM();
316 int ly = LEVELY((py + dy) / TILESIZE_VAR);
321 static int getLevelFromScreenX_SP(int sx)
323 int menBorder = setup.sp_show_border_elements;
324 int level_xsize = level.native_sp_level->width;
325 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
327 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
329 int fx = getFieldbufferOffsetX_SP();
332 int lx = LEVELX((px + dx) / TILESIZE_VAR);
337 static int getLevelFromScreenY_SP(int sy)
339 int menBorder = setup.sp_show_border_elements;
340 int level_ysize = level.native_sp_level->height;
341 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
343 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
345 int fy = getFieldbufferOffsetY_SP();
348 int ly = LEVELY((py + dy) / TILESIZE_VAR);
353 static int getLevelFromScreenX_MM(int sx)
355 int level_xsize = level.native_mm_level->fieldx;
356 int full_xsize = level_xsize * TILESIZE_VAR;
358 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
361 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
366 static int getLevelFromScreenY_MM(int sy)
368 int level_ysize = level.native_mm_level->fieldy;
369 int full_ysize = level_ysize * TILESIZE_VAR;
371 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
374 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
379 int getLevelFromScreenX(int x)
381 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
382 return getLevelFromScreenX_EM(x);
383 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
384 return getLevelFromScreenX_SP(x);
385 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
386 return getLevelFromScreenX_MM(x);
388 return getLevelFromScreenX_RND(x);
391 int getLevelFromScreenY(int y)
393 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
394 return getLevelFromScreenY_EM(y);
395 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
396 return getLevelFromScreenY_SP(y);
397 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
398 return getLevelFromScreenY_MM(y);
400 return getLevelFromScreenY_RND(y);
403 int getScreenFieldSizeX(void)
405 return (tape.playing ? tape.scr_fieldx : SCR_FIELDX);
408 int getScreenFieldSizeY(void)
410 return (tape.playing ? tape.scr_fieldy : SCR_FIELDY);
413 void DumpTile(int x, int y)
420 Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
423 if (!IN_LEV_FIELD(x, y))
425 Info("(not in level field)");
431 token_name = element_info[Tile[x][y]].token_name;
433 Info("Tile: %d\t['%s']", Tile[x][y], token_name);
434 Info("Back: %s", print_if_not_empty(Back[x][y]));
435 Info("Store: %s", print_if_not_empty(Store[x][y]));
436 Info("Store2: %s", print_if_not_empty(Store2[x][y]));
437 Info("StorePlayer: %s", print_if_not_empty(StorePlayer[x][y]));
438 Info("MovPos: %d", MovPos[x][y]);
439 Info("MovDir: %d", MovDir[x][y]);
440 Info("MovDelay: %d", MovDelay[x][y]);
441 Info("ChangeDelay: %d", ChangeDelay[x][y]);
442 Info("CustomValue: %d", CustomValue[x][y]);
443 Info("GfxElement: %d", GfxElement[x][y]);
444 Info("GfxAction: %d", GfxAction[x][y]);
445 Info("GfxFrame: %d [%d]", GfxFrame[x][y], FrameCounter);
446 Info("Player x/y: %d, %d", local_player->jx, local_player->jy);
450 void DumpTileFromScreen(int sx, int sy)
452 int lx = getLevelFromScreenX(sx);
453 int ly = getLevelFromScreenY(sy);
458 void SetDrawtoField(int mode)
460 if (mode == DRAW_TO_FIELDBUFFER)
466 BX2 = SCR_FIELDX + 1;
467 BY2 = SCR_FIELDY + 1;
469 drawto_field = fieldbuffer;
471 else // DRAW_TO_BACKBUFFER
477 BX2 = SCR_FIELDX - 1;
478 BY2 = SCR_FIELDY - 1;
480 drawto_field = backbuffer;
484 int GetDrawtoField(void)
486 return (drawto_field == fieldbuffer ? DRAW_TO_FIELDBUFFER : DRAW_TO_BACKBUFFER);
489 static void RedrawPlayfield_RND(void)
491 if (game.envelope_active)
494 DrawLevel(REDRAW_ALL);
498 void RedrawPlayfield(void)
500 if (game_status != GAME_MODE_PLAYING)
503 if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
504 RedrawPlayfield_BD(TRUE);
505 else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
506 RedrawPlayfield_EM(TRUE);
507 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
508 RedrawPlayfield_SP(TRUE);
509 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
510 RedrawPlayfield_MM();
511 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
512 RedrawPlayfield_RND();
514 BlitScreenToBitmap(backbuffer);
516 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
520 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
523 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
524 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
526 // may happen for "border.draw_masked.*" with undefined "global.border.*"
527 if (src_bitmap == NULL)
530 if (x == -1 && y == -1)
533 if (draw_target == DRAW_TO_SCREEN)
534 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
536 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
539 static void DrawMaskedBorderExt_FIELD(int draw_target)
541 if (global.border_status >= GAME_MODE_MAIN &&
542 global.border_status <= GAME_MODE_PLAYING &&
543 border.draw_masked[global.border_status])
544 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
548 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
550 // when drawing to backbuffer, never draw border over open doors
551 if (draw_target == DRAW_TO_BACKBUFFER &&
552 (GetDoorState() & DOOR_OPEN_1))
555 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
556 (global.border_status != GAME_MODE_EDITOR ||
557 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
558 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
561 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
563 // when drawing to backbuffer, never draw border over open doors
564 if (draw_target == DRAW_TO_BACKBUFFER &&
565 (GetDoorState() & DOOR_OPEN_2))
568 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
569 global.border_status != GAME_MODE_EDITOR)
570 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
573 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
575 // currently not available
578 static void DrawMaskedBorderExt_ALL(int draw_target)
580 DrawMaskedBorderExt_FIELD(draw_target);
581 DrawMaskedBorderExt_DOOR_1(draw_target);
582 DrawMaskedBorderExt_DOOR_2(draw_target);
583 DrawMaskedBorderExt_DOOR_3(draw_target);
586 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
588 // never draw masked screen borders on borderless screens
589 if (global.border_status == GAME_MODE_LOADING ||
590 global.border_status == GAME_MODE_TITLE)
593 if (redraw_mask & REDRAW_ALL)
594 DrawMaskedBorderExt_ALL(draw_target);
597 if (redraw_mask & REDRAW_FIELD)
598 DrawMaskedBorderExt_FIELD(draw_target);
599 if (redraw_mask & REDRAW_DOOR_1)
600 DrawMaskedBorderExt_DOOR_1(draw_target);
601 if (redraw_mask & REDRAW_DOOR_2)
602 DrawMaskedBorderExt_DOOR_2(draw_target);
603 if (redraw_mask & REDRAW_DOOR_3)
604 DrawMaskedBorderExt_DOOR_3(draw_target);
608 void DrawMaskedBorder_FIELD(void)
610 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
613 void DrawMaskedBorder(int redraw_mask)
615 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
618 void DrawMaskedBorderToTarget(int draw_target)
620 if (draw_target == DRAW_TO_BACKBUFFER ||
621 draw_target == DRAW_TO_SCREEN)
623 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
627 int last_border_status = global.border_status;
629 if (draw_target == DRAW_TO_FADE_SOURCE)
631 global.border_status = gfx.fade_border_source_status;
632 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
634 else if (draw_target == DRAW_TO_FADE_TARGET)
636 global.border_status = gfx.fade_border_target_status;
637 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
640 // always use global border for PLAYING when restarting the game
641 if (global.border_status == GAME_MODE_PSEUDO_RESTARTING)
642 global.border_status = GAME_MODE_PLAYING;
644 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
646 global.border_status = last_border_status;
647 gfx.masked_border_bitmap_ptr = backbuffer;
651 void DrawTileCursor(int draw_target, int drawing_stage)
653 int tile_cursor_active = (game_status == GAME_MODE_PLAYING);
655 DrawTileCursor_MM(draw_target, drawing_stage, tile_cursor_active);
658 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
660 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
663 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
665 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
666 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
668 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
671 void BlitScreenToBitmap(Bitmap *target_bitmap)
673 if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
674 BlitScreenToBitmap_BD(target_bitmap);
675 else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
676 BlitScreenToBitmap_EM(target_bitmap);
677 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
678 BlitScreenToBitmap_SP(target_bitmap);
679 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
680 BlitScreenToBitmap_MM(target_bitmap);
681 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
682 BlitScreenToBitmap_RND(target_bitmap);
684 redraw_mask |= REDRAW_FIELD;
687 static void DrawFramesPerSecond(void)
690 int font_nr = FONT_TEXT_2;
691 int font_width = getFontWidth(font_nr);
692 int draw_deactivation_mask = GetDrawDeactivationMask();
693 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
695 // draw FPS with leading space (needed if field buffer deactivated)
696 sprintf(text, " %04.1f fps", global.frames_per_second);
698 // override draw deactivation mask (required for invisible warp mode)
699 SetDrawDeactivationMask(REDRAW_NONE);
701 // draw opaque FPS if field buffer deactivated, else draw masked FPS
702 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
703 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
705 // set draw deactivation mask to previous value
706 SetDrawDeactivationMask(draw_deactivation_mask);
708 // force full-screen redraw in this frame
709 redraw_mask = REDRAW_ALL;
713 static void PrintFrameTimeDebugging(void)
715 static unsigned int last_counter = 0;
716 unsigned int counter = Counter();
717 int diff_1 = counter - last_counter;
718 int diff_2 = diff_1 - GAME_FRAME_DELAY;
720 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
721 char diff_bar[2 * diff_2_max + 5];
725 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
727 for (i = 0; i < diff_2_max; i++)
728 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
729 i >= diff_2_max - diff_2_cut ? '-' : ' ');
731 diff_bar[pos++] = '|';
733 for (i = 0; i < diff_2_max; i++)
734 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
736 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
738 diff_bar[pos++] = '\0';
740 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
743 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
746 last_counter = counter;
750 static int unifiedRedrawMask(int mask)
752 if (mask & REDRAW_ALL)
755 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
761 static boolean equalRedrawMasks(int mask_1, int mask_2)
763 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
766 void BackToFront(void)
768 static int last_redraw_mask = REDRAW_NONE;
770 // force screen redraw in every frame to continue drawing global animations
771 // (but always use the last redraw mask to prevent unwanted side effects)
772 if (redraw_mask == REDRAW_NONE)
773 redraw_mask = last_redraw_mask;
775 last_redraw_mask = redraw_mask;
778 // masked border now drawn immediately when blitting backbuffer to window
780 // draw masked border to all viewports, if defined
781 DrawMaskedBorder(redraw_mask);
784 // draw frames per second (only if debug mode is enabled)
785 if (redraw_mask & REDRAW_FPS)
786 DrawFramesPerSecond();
788 // remove playfield redraw before potentially merging with doors redraw
789 if (DrawingDeactivated(REAL_SX, REAL_SY))
790 redraw_mask &= ~REDRAW_FIELD;
792 // redraw complete window if both playfield and (some) doors need redraw
793 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
794 redraw_mask = REDRAW_ALL;
796 /* although redrawing the whole window would be fine for normal gameplay,
797 being able to only redraw the playfield is required for deactivating
798 certain drawing areas (mainly playfield) to work, which is needed for
799 warp-forward to be fast enough (by skipping redraw of most frames) */
801 if (redraw_mask & REDRAW_ALL)
803 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
805 else if (redraw_mask & REDRAW_FIELD)
807 BlitBitmap(backbuffer, window,
808 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
810 else if (redraw_mask & REDRAW_DOORS)
812 // merge door areas to prevent calling screen redraw more than once
818 if (redraw_mask & REDRAW_DOOR_1)
822 x2 = MAX(x2, DX + DXSIZE);
823 y2 = MAX(y2, DY + DYSIZE);
826 if (redraw_mask & REDRAW_DOOR_2)
830 x2 = MAX(x2, VX + VXSIZE);
831 y2 = MAX(y2, VY + VYSIZE);
834 if (redraw_mask & REDRAW_DOOR_3)
838 x2 = MAX(x2, EX + EXSIZE);
839 y2 = MAX(y2, EY + EYSIZE);
842 // make sure that at least one pixel is blitted, and inside the screen
843 // (else nothing is blitted, causing the animations not to be updated)
844 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
845 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
846 x2 = MIN(MAX(1, x2), WIN_XSIZE);
847 y2 = MIN(MAX(1, y2), WIN_YSIZE);
849 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
852 redraw_mask = REDRAW_NONE;
855 PrintFrameTimeDebugging();
859 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
861 unsigned int frame_delay_value_old = GetVideoFrameDelay();
863 SetVideoFrameDelay(frame_delay_value);
867 SetVideoFrameDelay(frame_delay_value_old);
870 static int fade_type_skip = FADE_TYPE_NONE;
872 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
874 void (*draw_border_function)(void) = NULL;
875 int x, y, width, height;
876 int fade_delay, post_delay;
878 if (fade_type == FADE_TYPE_FADE_OUT)
880 if (fade_type_skip != FADE_TYPE_NONE)
882 // skip all fade operations until specified fade operation
883 if (fade_type & fade_type_skip)
884 fade_type_skip = FADE_TYPE_NONE;
889 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
893 redraw_mask |= fade_mask;
895 if (fade_type == FADE_TYPE_SKIP)
897 fade_type_skip = fade_mode;
902 fade_delay = fading.fade_delay;
903 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
905 if (fade_type_skip != FADE_TYPE_NONE)
907 // skip all fade operations until specified fade operation
908 if (fade_type & fade_type_skip)
909 fade_type_skip = FADE_TYPE_NONE;
914 if (global.autoplay_leveldir)
919 if (fade_mask == REDRAW_FIELD)
924 height = FADE_SYSIZE;
926 if (border.draw_masked_when_fading)
927 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
929 DrawMaskedBorder_FIELD(); // draw once
939 // when switching screens without fading, set fade delay to zero
940 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
943 // do not display black frame when fading out without fade delay
944 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
947 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
948 draw_border_function);
950 redraw_mask &= ~fade_mask;
952 ClearAutoRepeatKeyEvents();
955 static void SetScreenStates_BeforeFadingIn(void)
957 // temporarily set screen mode for animations to screen after fading in
958 global.anim_status = global.anim_status_next;
960 // store backbuffer with all animations that will be started after fading in
961 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
963 // set screen mode for animations back to fading
964 global.anim_status = GAME_MODE_PSEUDO_FADING;
967 static void SetScreenStates_AfterFadingIn(void)
969 // store new source screen (to use correct masked border for fading)
970 gfx.fade_border_source_status = global.border_status;
972 global.anim_status = global.anim_status_next;
975 static void SetScreenStates_BeforeFadingOut(void)
977 // store new target screen (to use correct masked border for fading)
978 gfx.fade_border_target_status = game_status;
980 // set screen mode for animations to fading
981 global.anim_status = GAME_MODE_PSEUDO_FADING;
983 // store backbuffer with all animations that will be stopped for fading out
984 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
987 static void SetScreenStates_AfterFadingOut(void)
989 global.border_status = game_status;
991 // always use global border for PLAYING when restarting the game
992 if (global.border_status == GAME_MODE_PSEUDO_RESTARTING)
993 global.border_status = GAME_MODE_PLAYING;
996 void FadeIn(int fade_mask)
998 SetScreenStates_BeforeFadingIn();
1001 DrawMaskedBorder(REDRAW_ALL);
1004 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1005 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1007 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1011 FADE_SXSIZE = FULL_SXSIZE;
1012 FADE_SYSIZE = FULL_SYSIZE;
1014 // activate virtual buttons depending on upcoming game status
1015 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1016 game_status == GAME_MODE_PLAYING && !tape.playing)
1017 SetOverlayActive(TRUE);
1019 SetScreenStates_AfterFadingIn();
1021 // force update of global animation status in case of rapid screen changes
1022 redraw_mask = REDRAW_ALL;
1026 void FadeOut(int fade_mask)
1028 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1029 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1030 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1033 // when using BD game engine, cover playfield before fading out after a game
1034 if (game_bd.cover_screen)
1037 SetScreenStates_BeforeFadingOut();
1039 SetTileCursorActive(FALSE);
1040 SetOverlayActive(FALSE);
1043 DrawMaskedBorder(REDRAW_ALL);
1046 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1047 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1049 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1051 SetScreenStates_AfterFadingOut();
1054 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1056 static struct TitleFadingInfo fading_leave_stored;
1059 fading_leave_stored = fading_leave;
1061 fading = fading_leave_stored;
1064 void FadeSetEnterMenu(void)
1066 fading = menu.enter_menu;
1068 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1071 void FadeSetLeaveMenu(void)
1073 fading = menu.leave_menu;
1075 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1078 void FadeSetEnterScreen(void)
1080 fading = menu.enter_screen[game_status];
1082 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1085 void FadeSetNextScreen(void)
1087 fading = menu.next_screen[game_status];
1089 // (do not overwrite fade mode set by FadeSetEnterScreen)
1090 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1093 void FadeSetLeaveScreen(void)
1095 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1098 void FadeSetFromType(int type)
1100 if (type & TYPE_ENTER_SCREEN)
1101 FadeSetEnterScreen();
1102 else if (type & TYPE_ENTER)
1104 else if (type & TYPE_LEAVE)
1108 void FadeSetDisabled(void)
1110 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1112 fading = fading_none;
1115 void FadeSkipNextFadeIn(void)
1117 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1120 void FadeSkipNextFadeOut(void)
1122 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1125 static int getGlobalGameStatus(int status)
1127 return (status == GAME_MODE_PSEUDO_TYPENAME ? GAME_MODE_MAIN :
1128 status == GAME_MODE_SCOREINFO ? GAME_MODE_SCORES :
1132 int getImageFromGraphicOrDefault(int graphic, int default_graphic)
1134 if (graphic == IMG_UNDEFINED)
1135 return IMG_UNDEFINED;
1137 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1139 return (graphic_info[graphic].bitmap != NULL || redefined ?
1140 graphic : default_graphic);
1143 static int getBackgroundImage(int graphic)
1145 return getImageFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1148 static int getGlobalBorderImage(int graphic)
1150 return getImageFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1153 Bitmap *getGlobalBorderBitmapFromStatus(int status_raw)
1155 int status = getGlobalGameStatus(status_raw);
1157 (status == GAME_MODE_MAIN ? IMG_GLOBAL_BORDER_MAIN :
1158 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1159 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1160 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1162 int graphic_final = getGlobalBorderImage(graphic);
1164 return graphic_info[graphic_final].bitmap;
1167 void SetBackgroundImage(int graphic, int redraw_mask)
1169 struct GraphicInfo *g = &graphic_info[graphic];
1170 struct GraphicInfo g_undefined = { 0 };
1172 if (graphic == IMG_UNDEFINED)
1175 // always use original size bitmap for backgrounds, if existing
1176 Bitmap *bitmap = (g->bitmaps != NULL &&
1177 g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] != NULL ?
1178 g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] : g->bitmap);
1180 // remove every mask before setting mask for window, and
1181 // remove window area mask before setting mask for main or door area
1182 int remove_mask = (redraw_mask == REDRAW_ALL ? 0xffff : REDRAW_ALL);
1184 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
1185 SetBackgroundBitmap(NULL, remove_mask, 0, 0, 0, 0); // !!! FIX THIS !!!
1186 SetBackgroundBitmap(bitmap, redraw_mask,
1188 g->width, g->height);
1191 void SetWindowBackgroundImageIfDefined(int graphic)
1193 if (graphic_info[graphic].bitmap)
1194 SetBackgroundImage(graphic, REDRAW_ALL);
1197 void SetMainBackgroundImageIfDefined(int graphic)
1199 if (graphic_info[graphic].bitmap)
1200 SetBackgroundImage(graphic, REDRAW_FIELD);
1203 void SetDoorBackgroundImageIfDefined(int graphic)
1205 if (graphic_info[graphic].bitmap)
1206 SetBackgroundImage(graphic, REDRAW_DOOR_1);
1209 void SetWindowBackgroundImage(int graphic)
1211 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_ALL);
1214 void SetMainBackgroundImage(int graphic)
1216 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_FIELD);
1219 void SetDoorBackgroundImage(int graphic)
1221 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_DOOR_1);
1224 void SetPanelBackground(void)
1226 SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
1229 void DrawBackground(int x, int y, int width, int height)
1231 // "drawto" might still point to playfield buffer here (hall of fame)
1232 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1234 if (IN_GFX_FIELD_FULL(x, y))
1235 redraw_mask |= REDRAW_FIELD;
1236 else if (IN_GFX_DOOR_1(x, y))
1237 redraw_mask |= REDRAW_DOOR_1;
1238 else if (IN_GFX_DOOR_2(x, y))
1239 redraw_mask |= REDRAW_DOOR_2;
1240 else if (IN_GFX_DOOR_3(x, y))
1241 redraw_mask |= REDRAW_DOOR_3;
1244 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1246 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1248 if (font->bitmap == NULL)
1251 DrawBackground(x, y, width, height);
1254 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1256 struct GraphicInfo *g = &graphic_info[graphic];
1258 if (g->bitmap == NULL)
1261 DrawBackground(x, y, width, height);
1264 static int game_status_last = -1;
1265 static Bitmap *global_border_bitmap_last = NULL;
1266 static Bitmap *global_border_bitmap = NULL;
1267 static int real_sx_last = -1, real_sy_last = -1;
1268 static int full_sxsize_last = -1, full_sysize_last = -1;
1269 static int dx_last = -1, dy_last = -1;
1270 static int dxsize_last = -1, dysize_last = -1;
1271 static int vx_last = -1, vy_last = -1;
1272 static int vxsize_last = -1, vysize_last = -1;
1273 static int ex_last = -1, ey_last = -1;
1274 static int exsize_last = -1, eysize_last = -1;
1276 boolean CheckIfGlobalBorderHasChanged(void)
1278 // if game status has not changed, global border has not changed either
1279 if (game_status == game_status_last)
1282 // determine and store new global border bitmap for current game status
1283 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1285 return (global_border_bitmap_last != global_border_bitmap);
1288 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1290 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1291 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1293 // if game status has not changed, nothing has to be redrawn
1294 if (game_status == game_status_last)
1297 // redraw if last screen was title screen
1298 if (game_status_last == GAME_MODE_TITLE)
1301 // redraw if global screen border has changed
1302 if (CheckIfGlobalBorderHasChanged())
1305 // redraw if position or size of playfield area has changed
1306 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1307 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1310 // redraw if position or size of door area has changed
1311 if (dx_last != DX || dy_last != DY ||
1312 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1315 // redraw if position or size of tape area has changed
1316 if (vx_last != VX || vy_last != VY ||
1317 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1320 // redraw if position or size of editor area has changed
1321 if (ex_last != EX || ey_last != EY ||
1322 exsize_last != EXSIZE || eysize_last != EYSIZE)
1329 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1332 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1334 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1337 void RedrawGlobalBorder(void)
1339 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1341 RedrawGlobalBorderFromBitmap(bitmap);
1343 redraw_mask = REDRAW_ALL;
1346 static void RedrawGlobalBorderIfNeeded(void)
1348 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1349 if (game_status == game_status_last)
1353 // copy current draw buffer to later copy back areas that have not changed
1354 if (game_status_last != GAME_MODE_TITLE)
1355 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1357 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1358 if (CheckIfGlobalBorderRedrawIsNeeded())
1360 // determine and store new global border bitmap for current game status
1361 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1364 // redraw global screen border (or clear, if defined to be empty)
1365 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1367 if (game_status == GAME_MODE_EDITOR)
1368 DrawSpecialEditorDoor();
1370 // copy previous playfield and door areas, if they are defined on both
1371 // previous and current screen and if they still have the same size
1373 if (real_sx_last != -1 && real_sy_last != -1 &&
1374 REAL_SX != -1 && REAL_SY != -1 &&
1375 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1376 BlitBitmap(bitmap_db_store_1, backbuffer,
1377 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1380 if (dx_last != -1 && dy_last != -1 &&
1381 DX != -1 && DY != -1 &&
1382 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1383 BlitBitmap(bitmap_db_store_1, backbuffer,
1384 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1386 if (game_status != GAME_MODE_EDITOR)
1388 if (vx_last != -1 && vy_last != -1 &&
1389 VX != -1 && VY != -1 &&
1390 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1391 BlitBitmap(bitmap_db_store_1, backbuffer,
1392 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1396 if (ex_last != -1 && ey_last != -1 &&
1397 EX != -1 && EY != -1 &&
1398 exsize_last == EXSIZE && eysize_last == EYSIZE)
1399 BlitBitmap(bitmap_db_store_1, backbuffer,
1400 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1403 redraw_mask = REDRAW_ALL;
1406 game_status_last = game_status;
1408 global_border_bitmap_last = global_border_bitmap;
1410 real_sx_last = REAL_SX;
1411 real_sy_last = REAL_SY;
1412 full_sxsize_last = FULL_SXSIZE;
1413 full_sysize_last = FULL_SYSIZE;
1416 dxsize_last = DXSIZE;
1417 dysize_last = DYSIZE;
1420 vxsize_last = VXSIZE;
1421 vysize_last = VYSIZE;
1424 exsize_last = EXSIZE;
1425 eysize_last = EYSIZE;
1428 void ClearField(void)
1430 RedrawGlobalBorderIfNeeded();
1432 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1433 // (when entering hall of fame after playing)
1434 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1436 // !!! maybe this should be done before clearing the background !!!
1437 if (game_status == GAME_MODE_PLAYING)
1439 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1440 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1444 SetDrawtoField(DRAW_TO_BACKBUFFER);
1448 void MarkTileDirty(int x, int y)
1450 redraw_mask |= REDRAW_FIELD;
1453 void SetBorderElement(void)
1457 BorderElement = EL_EMPTY;
1459 // only the R'n'D game engine may use an additional steelwall border
1460 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1463 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1465 for (x = 0; x < lev_fieldx; x++)
1467 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1468 BorderElement = EL_STEELWALL;
1470 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1476 void FloodFillLevelExt(int start_x, int start_y, int fill_element,
1477 int max_array_fieldx, int max_array_fieldy,
1478 short field[max_array_fieldx][max_array_fieldy],
1479 int max_fieldx, int max_fieldy)
1481 static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY];
1482 struct XY *check = xy_topdown;
1483 int old_element = field[start_x][start_y];
1486 // do nothing if start field already has the desired content
1487 if (old_element == fill_element)
1490 stack_buffer[stack_pos++] = (struct XY){ start_x, start_y };
1492 while (stack_pos > 0)
1494 struct XY current = stack_buffer[--stack_pos];
1497 field[current.x][current.y] = fill_element;
1499 for (i = 0; i < 4; i++)
1501 int x = current.x + check[i].x;
1502 int y = current.y + check[i].y;
1504 // check for stack buffer overflow (should not happen)
1505 if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY)
1506 Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug.");
1508 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1509 stack_buffer[stack_pos++] = (struct XY){ x, y };
1514 void FloodFillLevel(int from_x, int from_y, int fill_element,
1515 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1516 int max_fieldx, int max_fieldy)
1518 FloodFillLevelExt(from_x, from_y, fill_element,
1519 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1520 max_fieldx, max_fieldy);
1523 void SetRandomAnimationValue(int x, int y)
1525 gfx.anim_random_frame = GfxRandom[x][y];
1528 void SetAnimationFirstLevel(int first_level)
1530 gfx.anim_first_level = first_level;
1533 int getGraphicAnimationFrame(int graphic, int sync_frame)
1535 // animation synchronized with global frame counter, not move position
1536 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1537 sync_frame = FrameCounter;
1538 else if (graphic_info[graphic].anim_global_anim_sync)
1539 sync_frame = getGlobalAnimSyncFrame();
1541 return getAnimationFrame(graphic_info[graphic].anim_frames,
1542 graphic_info[graphic].anim_delay,
1543 graphic_info[graphic].anim_mode,
1544 graphic_info[graphic].anim_start_frame,
1548 int getGraphicAnimationFrameXY(int graphic, int lx, int ly)
1550 if (graphic_info[graphic].anim_mode & ANIM_TILED)
1552 struct GraphicInfo *g = &graphic_info[graphic];
1553 int xsize = MAX(1, g->anim_frames_per_line);
1554 int ysize = MAX(1, g->anim_frames / xsize);
1555 int xoffset = g->anim_start_frame % xsize;
1556 int yoffset = g->anim_start_frame % ysize;
1557 // may be needed if screen field is significantly larger than playfield
1558 int x = (lx + xoffset + SCR_FIELDX * xsize) % xsize;
1559 int y = (ly + yoffset + SCR_FIELDY * ysize) % ysize;
1560 int sync_frame = y * xsize + x;
1562 return sync_frame % g->anim_frames;
1564 else if (graphic_info[graphic].anim_mode & ANIM_RANDOM_STATIC)
1566 struct GraphicInfo *g = &graphic_info[graphic];
1567 // may be needed if screen field is significantly larger than playfield
1568 int x = (lx + SCR_FIELDX * lev_fieldx) % lev_fieldx;
1569 int y = (ly + SCR_FIELDY * lev_fieldy) % lev_fieldy;
1570 int sync_frame = GfxRandomStatic[x][y];
1572 return sync_frame % g->anim_frames;
1576 int sync_frame = (IN_LEV_FIELD(lx, ly) ? GfxFrame[lx][ly] : -1);
1578 return getGraphicAnimationFrame(graphic, sync_frame);
1582 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1584 struct GraphicInfo *g = &graphic_info[graphic];
1585 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1587 if (tilesize == gfx.standard_tile_size)
1588 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1589 else if (tilesize == game.tile_size)
1590 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1592 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1595 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1596 boolean get_backside)
1598 struct GraphicInfo *g = &graphic_info[graphic];
1599 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1600 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1602 if (g->offset_y == 0) // frames are ordered horizontally
1604 int max_width = g->anim_frames_per_line * g->width;
1605 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1607 *x = pos % max_width;
1608 *y = src_y % g->height + pos / max_width * g->height;
1610 else if (g->offset_x == 0) // frames are ordered vertically
1612 int max_height = g->anim_frames_per_line * g->height;
1613 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1615 *x = src_x % g->width + pos / max_height * g->width;
1616 *y = pos % max_height;
1618 else // frames are ordered diagonally
1620 *x = src_x + frame * g->offset_x;
1621 *y = src_y + frame * g->offset_y;
1625 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1626 Bitmap **bitmap, int *x, int *y,
1627 boolean get_backside)
1629 struct GraphicInfo *g = &graphic_info[graphic];
1631 // if no graphics defined at all, use fallback graphics
1632 if (g->bitmaps == NULL)
1633 *g = graphic_info[IMG_CHAR_EXCLAM];
1635 // if no in-game graphics defined, always use standard graphic size
1636 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1637 tilesize = TILESIZE;
1639 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1640 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1642 *x = *x * tilesize / g->tile_size;
1643 *y = *y * tilesize / g->tile_size;
1646 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1647 Bitmap **bitmap, int *x, int *y)
1649 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1652 void getFixedGraphicSource(int graphic, int frame,
1653 Bitmap **bitmap, int *x, int *y)
1655 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1658 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1660 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1663 void getGlobalAnimGraphicSource(int graphic, int frame,
1664 Bitmap **bitmap, int *x, int *y)
1666 struct GraphicInfo *g = &graphic_info[graphic];
1668 // if no graphics defined at all, use fallback graphics
1669 if (g->bitmaps == NULL)
1670 *g = graphic_info[IMG_CHAR_EXCLAM];
1672 // use original size graphics, if existing, else use standard size graphics
1673 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1674 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1676 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1678 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1681 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1682 int *x, int *y, boolean get_backside)
1684 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1688 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1690 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1693 void DrawGraphic(int x, int y, int graphic, int frame)
1696 if (!IN_SCR_FIELD(x, y))
1698 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1699 Debug("draw:DrawGraphic", "This should never happen!");
1705 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1708 MarkTileDirty(x, y);
1711 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1714 if (!IN_SCR_FIELD(x, y))
1716 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1718 Debug("draw:DrawFixedGraphic", "This should never happen!");
1724 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1726 MarkTileDirty(x, y);
1729 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1735 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1737 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1740 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1746 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1747 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1750 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1753 if (!IN_SCR_FIELD(x, y))
1755 Debug("draw:DrawGraphicThruMask", "x = %d, y = %d, graphic = %d",
1757 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1763 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1766 MarkTileDirty(x, y);
1769 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1772 if (!IN_SCR_FIELD(x, y))
1774 Debug("draw:DrawFixedGraphicThruMask", "x = %d, y = %d, graphic = %d",
1776 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1782 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1784 MarkTileDirty(x, y);
1787 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1793 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1795 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1799 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1800 int graphic, int frame)
1805 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1807 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1811 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1813 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1815 MarkTileDirty(x / tilesize, y / tilesize);
1818 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1821 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1822 graphic, frame, tilesize);
1823 MarkTileDirty(x / tilesize, y / tilesize);
1826 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1832 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1833 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1836 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1837 int frame, int tilesize)
1842 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1843 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1846 void DrawMiniGraphic(int x, int y, int graphic)
1848 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX, SY + y * MINI_TILEY, graphic);
1849 MarkTileDirty(x / 2, y / 2);
1852 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1857 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1858 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1861 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1862 int graphic, int frame,
1863 int cut_mode, int mask_mode)
1868 int width = TILEX, height = TILEY;
1871 if (dx || dy) // shifted graphic
1873 if (x < BX1) // object enters playfield from the left
1880 else if (x > BX2) // object enters playfield from the right
1886 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1892 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1894 else if (dx) // general horizontal movement
1895 MarkTileDirty(x + SIGN(dx), y);
1897 if (y < BY1) // object enters playfield from the top
1899 if (cut_mode == CUT_BELOW) // object completely above top border
1907 else if (y > BY2) // object enters playfield from the bottom
1913 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1919 else if (dy > 0 && cut_mode == CUT_ABOVE)
1921 if (y == BY2) // object completely above bottom border
1927 MarkTileDirty(x, y + 1);
1928 } // object leaves playfield to the bottom
1929 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1931 else if (dy) // general vertical movement
1932 MarkTileDirty(x, y + SIGN(dy));
1936 if (!IN_SCR_FIELD(x, y))
1938 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1940 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1946 width = width * TILESIZE_VAR / TILESIZE;
1947 height = height * TILESIZE_VAR / TILESIZE;
1948 cx = cx * TILESIZE_VAR / TILESIZE;
1949 cy = cy * TILESIZE_VAR / TILESIZE;
1950 dx = dx * TILESIZE_VAR / TILESIZE;
1951 dy = dy * TILESIZE_VAR / TILESIZE;
1953 if (width > 0 && height > 0)
1955 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1960 dst_x = FX + x * TILEX_VAR + dx;
1961 dst_y = FY + y * TILEY_VAR + dy;
1963 if (mask_mode == USE_MASKING)
1964 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1967 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1970 MarkTileDirty(x, y);
1974 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1975 int graphic, int frame,
1976 int cut_mode, int mask_mode)
1981 int width = TILEX_VAR, height = TILEY_VAR;
1984 int x2 = x + SIGN(dx);
1985 int y2 = y + SIGN(dy);
1987 // movement with two-tile animations must be sync'ed with movement position,
1988 // not with current GfxFrame (which can be higher when using slow movement)
1989 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1990 int anim_frames = graphic_info[graphic].anim_frames;
1992 // (we also need anim_delay here for movement animations with less frames)
1993 int anim_delay = graphic_info[graphic].anim_delay;
1994 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1996 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1997 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1999 // re-calculate animation frame for two-tile movement animation
2000 frame = getGraphicAnimationFrame(graphic, sync_frame);
2002 // check if movement start graphic inside screen area and should be drawn
2003 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
2005 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
2007 dst_x = FX + x1 * TILEX_VAR;
2008 dst_y = FY + y1 * TILEY_VAR;
2010 if (mask_mode == USE_MASKING)
2011 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
2014 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
2017 MarkTileDirty(x1, y1);
2020 // check if movement end graphic inside screen area and should be drawn
2021 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
2023 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
2025 dst_x = FX + x2 * TILEX_VAR;
2026 dst_y = FY + y2 * TILEY_VAR;
2028 if (mask_mode == USE_MASKING)
2029 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
2032 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
2035 MarkTileDirty(x2, y2);
2039 static void DrawGraphicShifted(int x, int y, int dx, int dy,
2040 int graphic, int frame,
2041 int cut_mode, int mask_mode)
2045 DrawGraphic(x, y, graphic, frame);
2050 if (graphic_info[graphic].double_movement) // EM style movement images
2051 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2053 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2056 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
2057 int graphic, int frame, int cut_mode)
2059 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
2062 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
2063 int cut_mode, int mask_mode)
2065 int lx = LEVELX(x), ly = LEVELY(y);
2069 if (IN_LEV_FIELD(lx, ly))
2071 if (element == EL_EMPTY)
2072 element = GfxElementEmpty[lx][ly];
2074 SetRandomAnimationValue(lx, ly);
2076 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2077 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2079 // do not use double (EM style) movement graphic when not moving
2080 if (graphic_info[graphic].double_movement && !dx && !dy)
2082 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2083 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2086 if (game.use_masked_elements && (dx || dy))
2087 mask_mode = USE_MASKING;
2089 else // border element
2091 graphic = el2img(element);
2092 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2095 if (element == EL_EXPANDABLE_WALL)
2097 boolean left_stopped = FALSE, right_stopped = FALSE;
2099 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2100 left_stopped = TRUE;
2101 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2102 right_stopped = TRUE;
2104 if (left_stopped && right_stopped)
2106 else if (left_stopped)
2108 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2109 frame = graphic_info[graphic].anim_frames - 1;
2111 else if (right_stopped)
2113 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2114 frame = graphic_info[graphic].anim_frames - 1;
2119 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2120 else if (mask_mode == USE_MASKING)
2121 DrawGraphicThruMask(x, y, graphic, frame);
2123 DrawGraphic(x, y, graphic, frame);
2126 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2127 int cut_mode, int mask_mode)
2129 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2130 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2131 cut_mode, mask_mode);
2134 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2137 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2140 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2143 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2146 void DrawLevelElementThruMask(int x, int y, int element)
2148 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2151 void DrawLevelFieldThruMask(int x, int y)
2153 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2156 // !!! implementation of quicksand is totally broken !!!
2157 #define IS_CRUMBLED_TILE(x, y, e) \
2158 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2159 !IS_MOVING(x, y) || \
2160 (e) == EL_QUICKSAND_EMPTYING || \
2161 (e) == EL_QUICKSAND_FAST_EMPTYING))
2163 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2168 int width, height, cx, cy;
2169 int sx = SCREENX(x), sy = SCREENY(y);
2170 int crumbled_border_size = graphic_info[graphic].border_size;
2171 int crumbled_tile_size = graphic_info[graphic].tile_size;
2172 int crumbled_border_size_var =
2173 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2176 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2178 for (i = 1; i < 4; i++)
2180 int dxx = (i & 1 ? dx : 0);
2181 int dyy = (i & 2 ? dy : 0);
2184 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2187 // check if neighbour field is of same crumble type
2188 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2189 graphic_info[graphic].class ==
2190 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2192 // return if check prevents inner corner
2193 if (same == (dxx == dx && dyy == dy))
2197 // if we reach this point, we have an inner corner
2199 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2201 width = crumbled_border_size_var;
2202 height = crumbled_border_size_var;
2203 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2204 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2206 if (game.use_masked_elements)
2208 int graphic0 = el2img(EL_EMPTY);
2209 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2210 Bitmap *src_bitmap0;
2213 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2215 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2217 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2219 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2221 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2224 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2226 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2229 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2234 int width, height, bx, by, cx, cy;
2235 int sx = SCREENX(x), sy = SCREENY(y);
2236 int crumbled_border_size = graphic_info[graphic].border_size;
2237 int crumbled_tile_size = graphic_info[graphic].tile_size;
2238 int crumbled_border_size_var =
2239 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2240 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2243 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2245 // only needed when using masked elements
2246 int graphic0 = el2img(EL_EMPTY);
2247 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2248 Bitmap *src_bitmap0;
2251 if (game.use_masked_elements)
2252 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2254 // draw simple, sloppy, non-corner-accurate crumbled border
2256 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2257 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2258 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2259 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2261 if (game.use_masked_elements)
2263 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2265 FX + sx * TILEX_VAR + cx,
2266 FY + sy * TILEY_VAR + cy);
2268 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2270 FX + sx * TILEX_VAR + cx,
2271 FY + sy * TILEY_VAR + cy);
2274 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2276 FX + sx * TILEX_VAR + cx,
2277 FY + sy * TILEY_VAR + cy);
2279 // (remaining middle border part must be at least as big as corner part)
2280 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2281 crumbled_border_size_var >= TILESIZE_VAR / 3)
2284 // correct corners of crumbled border, if needed
2286 for (i = -1; i <= 1; i += 2)
2288 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2289 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2290 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2293 // check if neighbour field is of same crumble type
2294 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2295 graphic_info[graphic].class ==
2296 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2298 // no crumbled corner, but continued crumbled border
2300 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2301 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2302 int b1 = (i == 1 ? crumbled_border_size_var :
2303 TILESIZE_VAR - 2 * crumbled_border_size_var);
2305 width = crumbled_border_size_var;
2306 height = crumbled_border_size_var;
2308 if (dir == 1 || dir == 2)
2323 if (game.use_masked_elements)
2325 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2327 FX + sx * TILEX_VAR + cx,
2328 FY + sy * TILEY_VAR + cy);
2330 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2332 FX + sx * TILEX_VAR + cx,
2333 FY + sy * TILEY_VAR + cy);
2336 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2338 FX + sx * TILEX_VAR + cx,
2339 FY + sy * TILEY_VAR + cy);
2344 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2346 int sx = SCREENX(x), sy = SCREENY(y);
2349 struct XY *xy = xy_topdown;
2351 if (!IN_LEV_FIELD(x, y))
2354 element = TILE_GFX_ELEMENT(x, y);
2356 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2358 if (!IN_SCR_FIELD(sx, sy))
2361 // crumble field borders towards direct neighbour fields
2362 for (i = 0; i < 4; i++)
2364 int xx = x + xy[i].x;
2365 int yy = y + xy[i].y;
2367 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2370 // check if neighbour field is of same crumble type
2371 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2372 graphic_info[graphic].class ==
2373 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2376 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2379 // crumble inner field corners towards corner neighbour fields
2380 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2381 graphic_info[graphic].anim_frames == 2)
2383 for (i = 0; i < 4; i++)
2385 int dx = (i & 1 ? +1 : -1);
2386 int dy = (i & 2 ? +1 : -1);
2388 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2392 MarkTileDirty(sx, sy);
2394 else // center field is not crumbled -- crumble neighbour fields
2396 // crumble field borders of direct neighbour fields
2397 for (i = 0; i < 4; i++)
2399 int xx = x + xy[i].x;
2400 int yy = y + xy[i].y;
2401 int sxx = sx + xy[i].x;
2402 int syy = sy + xy[i].y;
2404 if (!IN_LEV_FIELD(xx, yy) ||
2405 !IN_SCR_FIELD(sxx, syy))
2408 // do not crumble fields that are being digged or snapped
2409 if (Tile[xx][yy] == EL_EMPTY ||
2410 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2413 element = TILE_GFX_ELEMENT(xx, yy);
2415 if (!IS_CRUMBLED_TILE(xx, yy, element))
2418 graphic = el_act2crm(element, ACTION_DEFAULT);
2420 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2422 MarkTileDirty(sxx, syy);
2425 // crumble inner field corners of corner neighbour fields
2426 for (i = 0; i < 4; i++)
2428 int dx = (i & 1 ? +1 : -1);
2429 int dy = (i & 2 ? +1 : -1);
2435 if (!IN_LEV_FIELD(xx, yy) ||
2436 !IN_SCR_FIELD(sxx, syy))
2439 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2442 element = TILE_GFX_ELEMENT(xx, yy);
2444 if (!IS_CRUMBLED_TILE(xx, yy, element))
2447 graphic = el_act2crm(element, ACTION_DEFAULT);
2449 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2450 graphic_info[graphic].anim_frames == 2)
2451 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2453 MarkTileDirty(sxx, syy);
2458 void DrawLevelFieldCrumbled(int x, int y)
2462 if (!IN_LEV_FIELD(x, y))
2465 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2466 GfxElement[x][y] != EL_UNDEFINED &&
2467 GFX_CRUMBLED(GfxElement[x][y]))
2469 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2474 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2476 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2479 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2482 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2483 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2484 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2485 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2486 int sx = SCREENX(x), sy = SCREENY(y);
2488 DrawScreenGraphic(sx, sy, graphic1, frame1);
2489 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2492 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2494 int sx = SCREENX(x), sy = SCREENY(y);
2495 struct XY *xy = xy_topdown;
2498 // crumble direct neighbour fields (required for field borders)
2499 for (i = 0; i < 4; i++)
2501 int xx = x + xy[i].x;
2502 int yy = y + xy[i].y;
2503 int sxx = sx + xy[i].x;
2504 int syy = sy + xy[i].y;
2506 if (!IN_LEV_FIELD(xx, yy) ||
2507 !IN_SCR_FIELD(sxx, syy) ||
2508 !GFX_CRUMBLED(Tile[xx][yy]) ||
2512 DrawLevelField(xx, yy);
2515 // crumble corner neighbour fields (required for inner field corners)
2516 for (i = 0; i < 4; i++)
2518 int dx = (i & 1 ? +1 : -1);
2519 int dy = (i & 2 ? +1 : -1);
2525 if (!IN_LEV_FIELD(xx, yy) ||
2526 !IN_SCR_FIELD(sxx, syy) ||
2527 !GFX_CRUMBLED(Tile[xx][yy]) ||
2531 int element = TILE_GFX_ELEMENT(xx, yy);
2532 int graphic = el_act2crm(element, ACTION_DEFAULT);
2534 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2535 graphic_info[graphic].anim_frames == 2)
2536 DrawLevelField(xx, yy);
2540 static int getBorderElement(int x, int y)
2544 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2545 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2546 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2547 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2548 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2549 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2550 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2552 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2553 int steel_position = (x == -1 && y == -1 ? 0 :
2554 x == lev_fieldx && y == -1 ? 1 :
2555 x == -1 && y == lev_fieldy ? 2 :
2556 x == lev_fieldx && y == lev_fieldy ? 3 :
2557 x == -1 || x == lev_fieldx ? 4 :
2558 y == -1 || y == lev_fieldy ? 5 : 6);
2560 return border[steel_position][steel_type];
2563 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2565 if (game.use_masked_elements)
2567 if (graphic != el2img(EL_EMPTY))
2568 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2570 DrawGraphicThruMask(x, y, graphic, frame);
2574 DrawGraphic(x, y, graphic, frame);
2578 void DrawLevelGraphic(int x, int y, int graphic, int frame)
2580 DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2583 void DrawScreenElement(int x, int y, int element)
2585 int mask_mode = NO_MASKING;
2587 if (game.use_masked_elements)
2589 int lx = LEVELX(x), ly = LEVELY(y);
2591 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2593 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2595 mask_mode = USE_MASKING;
2599 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2600 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2603 void DrawLevelElement(int x, int y, int element)
2605 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2606 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2609 void DrawScreenField(int x, int y)
2611 int lx = LEVELX(x), ly = LEVELY(y);
2612 int element, content;
2614 if (!IN_LEV_FIELD(lx, ly))
2616 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2619 element = getBorderElement(lx, ly);
2621 DrawScreenElement(x, y, element);
2626 element = Tile[lx][ly];
2627 content = Store[lx][ly];
2629 if (IS_MOVING(lx, ly))
2631 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2632 boolean cut_mode = NO_CUTTING;
2634 if (element == EL_QUICKSAND_EMPTYING ||
2635 element == EL_QUICKSAND_FAST_EMPTYING ||
2636 element == EL_MAGIC_WALL_EMPTYING ||
2637 element == EL_BD_MAGIC_WALL_EMPTYING ||
2638 element == EL_DC_MAGIC_WALL_EMPTYING ||
2639 element == EL_AMOEBA_DROPPING)
2640 cut_mode = CUT_ABOVE;
2641 else if (element == EL_QUICKSAND_FILLING ||
2642 element == EL_QUICKSAND_FAST_FILLING ||
2643 element == EL_MAGIC_WALL_FILLING ||
2644 element == EL_BD_MAGIC_WALL_FILLING ||
2645 element == EL_DC_MAGIC_WALL_FILLING)
2646 cut_mode = CUT_BELOW;
2648 if (cut_mode == CUT_ABOVE)
2649 DrawScreenElement(x, y, element);
2651 DrawScreenElement(x, y, EL_EMPTY);
2653 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2655 int dir = MovDir[lx][ly];
2656 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2657 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2659 if (IN_SCR_FIELD(newx, newy))
2660 DrawScreenElement(newx, newy, EL_EMPTY);
2664 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2665 else if (cut_mode == NO_CUTTING)
2666 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2669 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2671 if (cut_mode == CUT_BELOW &&
2672 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2673 DrawLevelElement(lx, ly + 1, element);
2676 if (content == EL_ACID)
2678 int dir = MovDir[lx][ly];
2679 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2680 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2682 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2684 // prevent target field from being drawn again (but without masking)
2685 // (this would happen if target field is scanned after moving element)
2686 Stop[newlx][newly] = TRUE;
2689 else if (IS_BLOCKED(lx, ly))
2694 boolean cut_mode = NO_CUTTING;
2695 int element_old, content_old;
2697 Blocked2Moving(lx, ly, &oldx, &oldy);
2700 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2701 MovDir[oldx][oldy] == MV_RIGHT);
2703 element_old = Tile[oldx][oldy];
2704 content_old = Store[oldx][oldy];
2706 if (element_old == EL_QUICKSAND_EMPTYING ||
2707 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2708 element_old == EL_MAGIC_WALL_EMPTYING ||
2709 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2710 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2711 element_old == EL_AMOEBA_DROPPING)
2712 cut_mode = CUT_ABOVE;
2714 DrawScreenElement(x, y, EL_EMPTY);
2717 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2719 else if (cut_mode == NO_CUTTING)
2720 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2723 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2726 else if (IS_DRAWABLE(element))
2727 DrawScreenElement(x, y, element);
2729 DrawScreenElement(x, y, EL_EMPTY);
2732 void DrawLevelField(int x, int y)
2734 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2735 DrawScreenField(SCREENX(x), SCREENY(y));
2736 else if (IS_MOVING(x, y))
2740 Moving2Blocked(x, y, &newx, &newy);
2741 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2742 DrawScreenField(SCREENX(newx), SCREENY(newy));
2744 else if (IS_BLOCKED(x, y))
2748 Blocked2Moving(x, y, &oldx, &oldy);
2749 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2750 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2754 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2755 int (*el2img_function)(int), boolean masked,
2756 int element_bits_draw)
2758 int element_base = map_mm_wall_element(element);
2759 int element_bits = (IS_DF_WALL(element) ?
2760 element - EL_DF_WALL_START :
2761 IS_MM_WALL(element) ?
2762 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2763 int graphic = el2img_function(element_base);
2764 int tilesize_draw = tilesize / 2;
2769 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2771 for (i = 0; i < 4; i++)
2773 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2774 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2776 if (!(element_bits_draw & (1 << i)))
2779 if (element_bits & (1 << i))
2782 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2783 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2785 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2786 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2791 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2792 tilesize_draw, tilesize_draw);
2797 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2798 boolean masked, int element_bits_draw)
2800 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2801 element, tilesize, el2edimg, masked, element_bits_draw);
2804 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2805 int (*el2img_function)(int))
2807 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2811 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2814 if (IS_MM_WALL(element))
2816 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2817 element, tilesize, el2edimg, masked, 0x000f);
2821 int graphic = el2edimg(element);
2824 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2826 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2830 void DrawSizedElement(int x, int y, int element, int tilesize)
2832 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2835 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2837 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2840 void DrawMiniElement(int x, int y, int element)
2844 graphic = el2edimg(element);
2845 DrawMiniGraphic(x, y, graphic);
2848 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2851 int x = sx + scroll_x, y = sy + scroll_y;
2853 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2854 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2855 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2856 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2858 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2861 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2863 int x = sx + scroll_x, y = sy + scroll_y;
2865 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2866 DrawMiniElement(sx, sy, EL_EMPTY);
2867 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2868 DrawMiniElement(sx, sy, Tile[x][y]);
2870 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2873 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2874 int x, int y, int xsize, int ysize,
2875 int tile_width, int tile_height)
2879 int dst_x = startx + x * tile_width;
2880 int dst_y = starty + y * tile_height;
2881 int width = graphic_info[graphic].width;
2882 int height = graphic_info[graphic].height;
2883 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2884 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2885 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2886 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2887 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2888 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2889 boolean draw_masked = graphic_info[graphic].draw_masked;
2891 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2893 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2895 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2899 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2900 inner_sx + (x - 1) * tile_width % inner_width);
2901 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2902 inner_sy + (y - 1) * tile_height % inner_height);
2905 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2908 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2912 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2913 int x, int y, int xsize, int ysize,
2916 int font_width = getFontWidth(font_nr);
2917 int font_height = getFontHeight(font_nr);
2919 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2920 font_width, font_height);
2923 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2925 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2926 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2927 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2928 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2929 boolean no_delay = (tape.warp_forward);
2930 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2931 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2932 DelayCounter anim_delay = { anim_delay_value };
2933 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2934 int font_width = getFontWidth(font_nr);
2935 int font_height = getFontHeight(font_nr);
2936 int max_xsize = level.envelope[envelope_nr].xsize;
2937 int max_ysize = level.envelope[envelope_nr].ysize;
2938 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2939 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2940 int xend = max_xsize;
2941 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2942 int xstep = (xstart < xend ? 1 : 0);
2943 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2945 int end = MAX(xend - xstart, yend - ystart);
2948 for (i = start; i <= end; i++)
2950 int last_frame = end; // last frame of this "for" loop
2951 int x = xstart + i * xstep;
2952 int y = ystart + i * ystep;
2953 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2954 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2955 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2956 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2959 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2961 BlitScreenToBitmap(backbuffer);
2963 SetDrawtoField(DRAW_TO_BACKBUFFER);
2965 for (yy = 0; yy < ysize; yy++)
2966 for (xx = 0; xx < xsize; xx++)
2967 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2969 DrawTextArea(sx + font_width, sy + font_height,
2970 level.envelope[envelope_nr].text, font_nr, max_xsize,
2971 xsize - 2, ysize - 2, 0, mask_mode,
2972 level.envelope[envelope_nr].autowrap,
2973 level.envelope[envelope_nr].centered, FALSE);
2975 redraw_mask |= REDRAW_FIELD;
2978 SkipUntilDelayReached(&anim_delay, &i, last_frame);
2981 ClearAutoRepeatKeyEvents();
2984 void ShowEnvelope(int envelope_nr)
2986 int element = EL_ENVELOPE_1 + envelope_nr;
2987 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2988 int sound_opening = element_info[element].sound[ACTION_OPENING];
2989 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2990 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2991 boolean no_delay = (tape.warp_forward);
2992 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2993 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2994 int anim_mode = graphic_info[graphic].anim_mode;
2995 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2996 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2997 boolean overlay_enabled = GetOverlayEnabled();
2999 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3001 SetOverlayEnabled(FALSE);
3004 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3006 if (anim_mode == ANIM_DEFAULT)
3007 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
3009 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
3012 Delay_WithScreenUpdates(wait_delay_value);
3014 WaitForEventToContinue();
3017 SetOverlayEnabled(overlay_enabled);
3019 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3021 if (anim_mode != ANIM_NONE)
3022 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
3024 if (anim_mode == ANIM_DEFAULT)
3025 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
3027 game.envelope_active = FALSE;
3029 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3031 redraw_mask |= REDRAW_FIELD;
3035 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
3036 int xsize, int ysize)
3038 if (!global.use_envelope_request)
3041 if (request.bitmap == NULL ||
3042 xsize > request.xsize ||
3043 ysize > request.ysize)
3045 if (request.bitmap != NULL)
3046 FreeBitmap(request.bitmap);
3048 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
3050 SDL_Surface *surface = request.bitmap->surface;
3052 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
3053 Fail("SDLGetNativeSurface() failed");
3056 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3058 // create masked surface for request bitmap, if needed
3059 if (graphic_info[IMG_BACKGROUND_REQUEST].draw_masked)
3061 SDL_Surface *surface = request.bitmap->surface;
3062 SDL_Surface *surface_masked = request.bitmap->surface_masked;
3064 SDLBlitSurface(surface, surface_masked, 0, 0, xsize, ysize, 0, 0);
3065 SDL_SetColorKey(surface_masked, SET_TRANSPARENT_PIXEL,
3066 SDL_MapRGB(surface_masked->format, 0x00, 0x00, 0x00));
3069 SDLFreeBitmapTextures(request.bitmap);
3070 SDLCreateBitmapTextures(request.bitmap);
3072 ResetBitmapAlpha(request.bitmap);
3074 // set envelope request run-time values
3077 request.xsize = xsize;
3078 request.ysize = ysize;
3081 void DrawEnvelopeRequestToScreen(int drawing_target)
3083 if (global.use_envelope_request &&
3084 game.request_active &&
3085 drawing_target == DRAW_TO_SCREEN)
3087 struct GraphicInfo *g = &graphic_info[IMG_BACKGROUND_REQUEST];
3089 SetBitmapAlphaNextBlit(request.bitmap, g->alpha);
3092 BlitToScreenMasked(request.bitmap, 0, 0, request.xsize, request.ysize,
3093 request.sx, request.sy);
3095 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3096 request.sx, request.sy);
3100 static void setRequestBasePosition(int *x, int *y)
3102 int sx_base, sy_base;
3104 if (request.x != -1)
3105 sx_base = request.x;
3106 else if (request.align == ALIGN_LEFT)
3108 else if (request.align == ALIGN_RIGHT)
3109 sx_base = SX + SXSIZE;
3111 sx_base = SX + SXSIZE / 2;
3113 if (request.y != -1)
3114 sy_base = request.y;
3115 else if (request.valign == VALIGN_TOP)
3117 else if (request.valign == VALIGN_BOTTOM)
3118 sy_base = SY + SYSIZE;
3120 sy_base = SY + SYSIZE / 2;
3126 static void setRequestPositionExt(int *x, int *y, int width, int height,
3127 boolean add_border_size)
3129 int border_size = request.border_size;
3130 int sx_base, sy_base;
3133 setRequestBasePosition(&sx_base, &sy_base);
3135 if (request.align == ALIGN_LEFT)
3137 else if (request.align == ALIGN_RIGHT)
3138 sx = sx_base - width;
3140 sx = sx_base - width / 2;
3142 if (request.valign == VALIGN_TOP)
3144 else if (request.valign == VALIGN_BOTTOM)
3145 sy = sy_base - height;
3147 sy = sy_base - height / 2;
3149 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3150 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3152 if (add_border_size)
3162 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3164 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3167 static void DrawEnvelopeRequestText(int sx, int sy, char *text)
3169 char *text_final = text;
3170 char *text_door_style = NULL;
3171 int graphic = IMG_BACKGROUND_REQUEST;
3172 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3173 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3174 int font_nr = FONT_REQUEST;
3175 int font_width = getFontWidth(font_nr);
3176 int font_height = getFontHeight(font_nr);
3177 int border_size = request.border_size;
3178 int line_spacing = request.line_spacing;
3179 int line_height = font_height + line_spacing;
3180 int max_text_width = request.width - 2 * border_size;
3181 int max_text_height = request.height - 2 * border_size;
3182 int line_length = max_text_width / font_width;
3183 int max_lines = max_text_height / line_height;
3184 int text_width = line_length * font_width;
3185 int sx_offset = border_size;
3186 int sy_offset = border_size;
3188 // force DOOR font inside door area
3189 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3191 if (request.centered)
3192 sx_offset = (request.width - text_width) / 2;
3194 if (request.wrap_single_words && !request.autowrap)
3196 char *src_text_ptr, *dst_text_ptr;
3198 if (maxWordLengthInRequestString(text) > line_length)
3200 font_nr = FONT_REQUEST_NARROW;
3201 font_width = getFontWidth(font_nr);
3202 line_length = max_text_width / font_width;
3205 text_door_style = checked_malloc(2 * strlen(text) + 1);
3207 src_text_ptr = text;
3208 dst_text_ptr = text_door_style;
3210 while (*src_text_ptr)
3212 if (*src_text_ptr == ' ' ||
3213 *src_text_ptr == '?' ||
3214 *src_text_ptr == '!')
3215 *dst_text_ptr++ = '\n';
3217 if (*src_text_ptr != ' ')
3218 *dst_text_ptr++ = *src_text_ptr;
3223 *dst_text_ptr = '\0';
3225 text_final = text_door_style;
3228 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3229 line_length, -1, max_lines, line_spacing, mask_mode,
3230 request.autowrap, request.centered, FALSE);
3232 if (text_door_style)
3233 free(text_door_style);
3238 static void DrawEnvelopeRequest(char *text, unsigned int req_state)
3240 DrawBuffer *drawto_last = drawto;
3241 int graphic = IMG_BACKGROUND_REQUEST;
3242 int width = request.width;
3243 int height = request.height;
3244 int tile_size = MAX(request.step_offset, 1);
3245 int x_steps = width / tile_size;
3246 int y_steps = height / tile_size;
3250 setRequestPosition(&sx, &sy, FALSE);
3252 // draw complete envelope request to temporary bitmap
3253 drawto = bitmap_db_store_1;
3255 ClearRectangle(drawto, sx, sy, width, height);
3257 for (y = 0; y < y_steps; y++)
3258 for (x = 0; x < x_steps; x++)
3259 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3260 x, y, x_steps, y_steps,
3261 tile_size, tile_size);
3263 // write text for request
3264 DrawEnvelopeRequestText(sx, sy, text);
3266 MapToolButtons(req_state);
3268 // restore pointer to drawing buffer
3269 drawto = drawto_last;
3271 // prepare complete envelope request from temporary bitmap
3272 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy, width, height);
3275 static void AnimateEnvelopeRequest(int anim_mode, int action)
3277 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
3278 int delay_value_normal = request.step_delay;
3279 int delay_value_fast = delay_value_normal / 2;
3280 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3281 boolean no_delay = (tape.warp_forward);
3282 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3283 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value) / 2);
3284 DelayCounter anim_delay = { anim_delay_value };
3286 int tile_size = MAX(request.step_offset, 1);
3287 int max_xsize = request.width / tile_size;
3288 int max_ysize = request.height / tile_size;
3289 int max_xsize_inner = max_xsize - 2;
3290 int max_ysize_inner = max_ysize - 2;
3292 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3293 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3294 int xend = max_xsize_inner;
3295 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3296 int xstep = (xstart < xend ? 1 : 0);
3297 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3299 int end = MAX(xend - xstart, yend - ystart);
3302 if (setup.quick_doors)
3309 for (i = start; i <= end; i++)
3311 int last_frame = end; // last frame of this "for" loop
3312 int x = xstart + i * xstep;
3313 int y = ystart + i * ystep;
3314 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3315 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3316 int xsize_size_left = (xsize - 1) * tile_size;
3317 int ysize_size_top = (ysize - 1) * tile_size;
3318 int max_xsize_pos = (max_xsize - 1) * tile_size;
3319 int max_ysize_pos = (max_ysize - 1) * tile_size;
3320 int width = xsize * tile_size;
3321 int height = ysize * tile_size;
3327 HandleGameActions();
3329 setRequestPosition(&src_x, &src_y, FALSE);
3330 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3332 for (yy = 0; yy < 2; yy++)
3334 for (xx = 0; xx < 2; xx++)
3336 int src_xx = src_x + xx * max_xsize_pos;
3337 int src_yy = src_y + yy * max_ysize_pos;
3338 int dst_xx = dst_x + xx * xsize_size_left;
3339 int dst_yy = dst_y + yy * ysize_size_top;
3340 int xx_size = (xx ? tile_size : xsize_size_left);
3341 int yy_size = (yy ? tile_size : ysize_size_top);
3343 // draw partial (animated) envelope request to temporary bitmap
3344 BlitBitmap(bitmap_db_store_1, bitmap_db_store_2,
3345 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3349 // prepare partial (animated) envelope request from temporary bitmap
3350 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, dst_x, dst_y,
3353 redraw_mask |= REDRAW_FIELD;
3357 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3360 ClearAutoRepeatKeyEvents();
3363 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3365 int graphic = IMG_BACKGROUND_REQUEST;
3366 int sound_opening = SND_REQUEST_OPENING;
3367 int sound_closing = SND_REQUEST_CLOSING;
3368 int anim_mode_1 = request.anim_mode; // (higher priority)
3369 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3370 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3371 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3372 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3374 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3376 if (action == ACTION_OPENING)
3378 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3380 if (anim_mode == ANIM_DEFAULT)
3381 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3383 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3387 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3389 if (anim_mode != ANIM_NONE)
3390 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3392 if (anim_mode == ANIM_DEFAULT)
3393 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3396 game.envelope_active = FALSE;
3399 static Bitmap *GetPreviewTileBitmap(Bitmap *bitmap)
3401 if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
3402 return GetPreviewTileBitmap_BD(bitmap);
3407 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3409 if (IS_MM_WALL(element))
3411 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3417 int graphic = el2preimg(element);
3419 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3421 // for BD style levels, maybe use bitmap with level-specific colors
3422 src_bitmap = GetPreviewTileBitmap(src_bitmap);
3424 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3429 void DrawLevel(int draw_background_mask)
3433 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3434 SetDrawBackgroundMask(draw_background_mask);
3438 for (x = BX1; x <= BX2; x++)
3439 for (y = BY1; y <= BY2; y++)
3440 DrawScreenField(x, y);
3442 redraw_mask |= REDRAW_FIELD;
3445 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3450 for (x = 0; x < size_x; x++)
3451 for (y = 0; y < size_y; y++)
3452 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3454 redraw_mask |= REDRAW_FIELD;
3457 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3461 for (x = 0; x < size_x; x++)
3462 for (y = 0; y < size_y; y++)
3463 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3465 redraw_mask |= REDRAW_FIELD;
3468 static int getPreviewLevelWidth(void)
3470 if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
3471 return (level.native_bd_level->cave->x2 - level.native_bd_level->cave->x1 + 1);
3476 static int getPreviewLevelHeight(void)
3478 if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
3479 return (level.native_bd_level->cave->y2 - level.native_bd_level->cave->y1 + 1);
3484 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3486 boolean show_level_border = (BorderElement != EL_EMPTY);
3487 int level_xsize = getPreviewLevelWidth() + (show_level_border ? 2 : 0);
3488 int level_ysize = getPreviewLevelHeight() + (show_level_border ? 2 : 0);
3489 int tile_size = preview.tile_size;
3490 int preview_width = preview.xsize * tile_size;
3491 int preview_height = preview.ysize * tile_size;
3492 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3493 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3494 int real_preview_width = real_preview_xsize * tile_size;
3495 int real_preview_height = real_preview_ysize * tile_size;
3496 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3497 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3500 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3503 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3505 dst_x += (preview_width - real_preview_width) / 2;
3506 dst_y += (preview_height - real_preview_height) / 2;
3508 for (x = 0; x < real_preview_xsize; x++)
3510 for (y = 0; y < real_preview_ysize; y++)
3512 int lx = from_x + x + (show_level_border ? -1 : 0);
3513 int ly = from_y + y + (show_level_border ? -1 : 0);
3514 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3515 getBorderElement(lx, ly));
3517 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3518 element, tile_size);
3522 redraw_mask |= REDRAW_FIELD;
3525 #define MICROLABEL_EMPTY 0
3526 #define MICROLABEL_LEVEL_NAME 1
3527 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3528 #define MICROLABEL_LEVEL_AUTHOR 3
3529 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3530 #define MICROLABEL_IMPORTED_FROM 5
3531 #define MICROLABEL_IMPORTED_BY_HEAD 6
3532 #define MICROLABEL_IMPORTED_BY 7
3534 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3536 int max_text_width = SXSIZE;
3537 int font_width = getFontWidth(font_nr);
3539 if (pos->align == ALIGN_CENTER)
3540 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3541 else if (pos->align == ALIGN_RIGHT)
3542 max_text_width = pos->x;
3544 max_text_width = SXSIZE - pos->x;
3546 return max_text_width / font_width;
3549 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3551 char label_text[MAX_OUTPUT_LINESIZE + 1];
3552 int max_len_label_text;
3553 int font_nr = pos->font;
3556 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3559 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3560 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3561 mode == MICROLABEL_IMPORTED_BY_HEAD)
3562 font_nr = pos->font_alt;
3564 max_len_label_text = getMaxTextLength(pos, font_nr);
3566 if (pos->size != -1)
3567 max_len_label_text = pos->size;
3569 for (i = 0; i < max_len_label_text; i++)
3570 label_text[i] = ' ';
3571 label_text[max_len_label_text] = '\0';
3573 if (strlen(label_text) > 0)
3574 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3577 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3578 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3579 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3580 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3581 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3582 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3583 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3584 max_len_label_text);
3585 label_text[max_len_label_text] = '\0';
3587 if (strlen(label_text) > 0)
3588 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3590 redraw_mask |= REDRAW_FIELD;
3593 static void DrawPreviewLevelLabel(int mode)
3595 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3598 static void DrawPreviewLevelInfo(int mode)
3600 if (mode == MICROLABEL_LEVEL_NAME)
3601 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3602 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3603 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3606 static void DrawPreviewLevelExt(boolean restart)
3608 static DelayCounter scroll_delay = { 0 };
3609 static DelayCounter label_delay = { 0 };
3610 static int from_x, from_y, scroll_direction;
3611 static int label_state, label_counter;
3612 boolean show_level_border = (BorderElement != EL_EMPTY);
3613 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3614 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3616 scroll_delay.value = preview.step_delay;
3617 label_delay.value = MICROLEVEL_LABEL_DELAY;
3624 if (preview.anim_mode == ANIM_CENTERED)
3626 if (level_xsize > preview.xsize)
3627 from_x = (level_xsize - preview.xsize) / 2;
3628 if (level_ysize > preview.ysize)
3629 from_y = (level_ysize - preview.ysize) / 2;
3632 from_x += preview.xoffset;
3633 from_y += preview.yoffset;
3635 scroll_direction = MV_RIGHT;
3639 DrawPreviewLevelPlayfield(from_x, from_y);
3640 DrawPreviewLevelLabel(label_state);
3642 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3643 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3645 // initialize delay counters
3646 ResetDelayCounter(&scroll_delay);
3647 ResetDelayCounter(&label_delay);
3649 if (leveldir_current->name)
3651 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3652 char label_text[MAX_OUTPUT_LINESIZE + 1];
3653 int font_nr = pos->font;
3654 int max_len_label_text = getMaxTextLength(pos, font_nr);
3656 if (pos->size != -1)
3657 max_len_label_text = pos->size;
3659 strncpy(label_text, leveldir_current->name, max_len_label_text);
3660 label_text[max_len_label_text] = '\0';
3662 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3663 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3669 // scroll preview level, if needed
3670 if (preview.anim_mode != ANIM_NONE &&
3671 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3672 DelayReached(&scroll_delay))
3674 switch (scroll_direction)
3679 from_x -= preview.step_offset;
3680 from_x = (from_x < 0 ? 0 : from_x);
3683 scroll_direction = MV_UP;
3687 if (from_x < level_xsize - preview.xsize)
3689 from_x += preview.step_offset;
3690 from_x = (from_x > level_xsize - preview.xsize ?
3691 level_xsize - preview.xsize : from_x);
3694 scroll_direction = MV_DOWN;
3700 from_y -= preview.step_offset;
3701 from_y = (from_y < 0 ? 0 : from_y);
3704 scroll_direction = MV_RIGHT;
3708 if (from_y < level_ysize - preview.ysize)
3710 from_y += preview.step_offset;
3711 from_y = (from_y > level_ysize - preview.ysize ?
3712 level_ysize - preview.ysize : from_y);
3715 scroll_direction = MV_LEFT;
3722 DrawPreviewLevelPlayfield(from_x, from_y);
3725 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3726 // redraw micro level label, if needed
3727 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3728 !strEqual(level.author, ANONYMOUS_NAME) &&
3729 !strEqual(level.author, leveldir_current->name) &&
3730 DelayReached(&label_delay))
3732 int max_label_counter = 23;
3734 if (leveldir_current->imported_from != NULL &&
3735 strlen(leveldir_current->imported_from) > 0)
3736 max_label_counter += 14;
3737 if (leveldir_current->imported_by != NULL &&
3738 strlen(leveldir_current->imported_by) > 0)
3739 max_label_counter += 14;
3741 label_counter = (label_counter + 1) % max_label_counter;
3742 label_state = (label_counter >= 0 && label_counter <= 7 ?
3743 MICROLABEL_LEVEL_NAME :
3744 label_counter >= 9 && label_counter <= 12 ?
3745 MICROLABEL_LEVEL_AUTHOR_HEAD :
3746 label_counter >= 14 && label_counter <= 21 ?
3747 MICROLABEL_LEVEL_AUTHOR :
3748 label_counter >= 23 && label_counter <= 26 ?
3749 MICROLABEL_IMPORTED_FROM_HEAD :
3750 label_counter >= 28 && label_counter <= 35 ?
3751 MICROLABEL_IMPORTED_FROM :
3752 label_counter >= 37 && label_counter <= 40 ?
3753 MICROLABEL_IMPORTED_BY_HEAD :
3754 label_counter >= 42 && label_counter <= 49 ?
3755 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3757 if (leveldir_current->imported_from == NULL &&
3758 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3759 label_state == MICROLABEL_IMPORTED_FROM))
3760 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3761 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3763 DrawPreviewLevelLabel(label_state);
3767 void DrawPreviewPlayers(void)
3769 if (game_status != GAME_MODE_MAIN)
3772 // do not draw preview players if level preview redefined, but players aren't
3773 if (preview.redefined && !menu.main.preview_players.redefined)
3776 boolean player_found[MAX_PLAYERS];
3777 int num_players = 0;
3780 for (i = 0; i < MAX_PLAYERS; i++)
3781 player_found[i] = FALSE;
3783 // check which players can be found in the level (simple approach)
3784 for (x = 0; x < lev_fieldx; x++)
3786 for (y = 0; y < lev_fieldy; y++)
3788 int element = level.field[x][y];
3790 if (IS_PLAYER_ELEMENT(element))
3792 int player_nr = GET_PLAYER_NR(element);
3794 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3796 if (!player_found[player_nr])
3799 player_found[player_nr] = TRUE;
3804 struct TextPosInfo *pos = &menu.main.preview_players;
3805 int tile_size = pos->tile_size;
3806 int border_size = pos->border_size;
3807 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3808 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3809 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3810 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3811 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3812 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3813 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3814 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3815 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3816 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3817 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3818 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3820 // clear area in which the players will be drawn
3821 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3822 max_players_width, max_players_height);
3824 if (!network.enabled && !setup.team_mode)
3827 // only draw players if level is suited for team mode
3828 if (num_players < 2)
3831 // draw all players that were found in the level
3832 for (i = 0; i < MAX_PLAYERS; i++)
3834 if (player_found[i])
3836 int graphic = el2img(EL_PLAYER_1 + i);
3838 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3840 xpos += player_xoffset;
3841 ypos += player_yoffset;
3846 static void PreparePreviewTileBitmap(void)
3848 // check if special preview bitmap with level-specific colors should be created
3849 if (level.game_engine_type != GAME_ENGINE_TYPE_BD)
3852 // use original sized bitmap (else reduced color palette is lost by downscaling)
3853 int original_tilesize = MAX(MINI_TILESIZE, preview.tile_size);
3854 int scale_down_factor = original_tilesize / preview.tile_size;
3857 int element_template = EL_BD_GAME_GRAPHICS_COLOR_TEMPLATE;
3858 int graphic_template = el2preimg(element_template);
3859 int element_default = EL_BD_ROCK;
3860 int graphic_default = el2preimg(element_default);
3862 // create special preview bitmap and scale it down to preview tile size
3863 getSizedGraphicSource(graphic_template, 0, original_tilesize, &src_bitmap, &src_x, &src_y);
3864 PreparePreviewTileBitmap_BD(src_bitmap, scale_down_factor);
3866 // force using special preview bitmap to replace original preview bitmap
3867 getSizedGraphicSource(graphic_default, 0, preview.tile_size, &src_bitmap, &src_x, &src_y);
3868 SetPreviewTileBitmapReference_BD(src_bitmap);
3871 void DrawPreviewLevelInitial(void)
3873 PreparePreviewTileBitmap(); // only needed for native BD style levels
3875 DrawPreviewLevelExt(TRUE);
3876 DrawPreviewPlayers();
3879 void DrawPreviewLevelAnimation(void)
3881 DrawPreviewLevelExt(FALSE);
3884 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3885 int border_size, int font_nr)
3887 int graphic = el2img(EL_PLAYER_1 + player_nr);
3888 int font_height = getFontHeight(font_nr);
3889 int player_height = MAX(tile_size, font_height);
3890 int xoffset_text = tile_size + border_size;
3891 int yoffset_text = (player_height - font_height) / 2;
3892 int yoffset_graphic = (player_height - tile_size) / 2;
3893 char *player_name = getNetworkPlayerName(player_nr + 1);
3895 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3897 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3900 static void DrawNetworkPlayersExt(boolean force)
3902 if (game_status != GAME_MODE_MAIN)
3905 if (!network.connected && !force)
3908 // do not draw network players if level preview redefined, but players aren't
3909 if (preview.redefined && !menu.main.network_players.redefined)
3912 int num_players = 0;
3915 for (i = 0; i < MAX_PLAYERS; i++)
3916 if (stored_player[i].connected_network)
3919 struct TextPosInfo *pos = &menu.main.network_players;
3920 int tile_size = pos->tile_size;
3921 int border_size = pos->border_size;
3922 int xoffset_text = tile_size + border_size;
3923 int font_nr = pos->font;
3924 int font_width = getFontWidth(font_nr);
3925 int font_height = getFontHeight(font_nr);
3926 int player_height = MAX(tile_size, font_height);
3927 int player_yoffset = player_height + border_size;
3928 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3929 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3930 int all_players_height = num_players * player_yoffset - border_size;
3931 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3932 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3933 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3935 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3936 max_players_width, max_players_height);
3938 // first draw local network player ...
3939 for (i = 0; i < MAX_PLAYERS; i++)
3941 if (stored_player[i].connected_network &&
3942 stored_player[i].connected_locally)
3944 char *player_name = getNetworkPlayerName(i + 1);
3945 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3946 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3948 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3950 ypos += player_yoffset;
3954 // ... then draw all other network players
3955 for (i = 0; i < MAX_PLAYERS; i++)
3957 if (stored_player[i].connected_network &&
3958 !stored_player[i].connected_locally)
3960 char *player_name = getNetworkPlayerName(i + 1);
3961 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3962 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3964 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3966 ypos += player_yoffset;
3971 void DrawNetworkPlayers(void)
3973 DrawNetworkPlayersExt(FALSE);
3976 void ClearNetworkPlayers(void)
3978 DrawNetworkPlayersExt(TRUE);
3981 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3982 int graphic, int lx, int ly,
3985 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3987 if (mask_mode == USE_MASKING)
3988 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3990 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3993 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3994 int graphic, int sync_frame, int mask_mode)
3996 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3998 if (mask_mode == USE_MASKING)
3999 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
4001 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
4004 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
4005 int graphic, int sync_frame, int tilesize,
4008 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4010 if (mask_mode == USE_MASKING)
4011 DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
4013 DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
4016 static void DrawGraphicAnimation(int x, int y, int graphic)
4018 int lx = LEVELX(x), ly = LEVELY(y);
4019 int mask_mode = NO_MASKING;
4021 if (!IN_SCR_FIELD(x, y))
4024 if (game.use_masked_elements)
4026 if (Tile[lx][ly] != EL_EMPTY)
4028 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
4030 mask_mode = USE_MASKING;
4034 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
4035 graphic, lx, ly, mask_mode);
4037 MarkTileDirty(x, y);
4040 void DrawFixedGraphicAnimation(int x, int y, int graphic)
4042 int lx = LEVELX(x), ly = LEVELY(y);
4043 int mask_mode = NO_MASKING;
4045 if (!IN_SCR_FIELD(x, y))
4048 if (game.use_masked_elements)
4050 if (Tile[lx][ly] != EL_EMPTY)
4052 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
4054 mask_mode = USE_MASKING;
4058 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
4059 graphic, lx, ly, mask_mode);
4061 MarkTileDirty(x, y);
4064 void DrawLevelGraphicAnimation(int x, int y, int graphic)
4066 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4069 void DrawLevelElementAnimation(int x, int y, int element)
4071 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4073 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4076 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
4078 int sx = SCREENX(x), sy = SCREENY(y);
4080 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4083 if (Tile[x][y] == EL_EMPTY)
4084 graphic = el2img(GfxElementEmpty[x][y]);
4086 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4089 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4092 DrawGraphicAnimation(sx, sy, graphic);
4095 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4096 DrawLevelFieldCrumbled(x, y);
4098 if (GFX_CRUMBLED(Tile[x][y]))
4099 DrawLevelFieldCrumbled(x, y);
4103 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4105 int sx = SCREENX(x), sy = SCREENY(y);
4108 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4111 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4113 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4116 DrawGraphicAnimation(sx, sy, graphic);
4118 if (GFX_CRUMBLED(element))
4119 DrawLevelFieldCrumbled(x, y);
4122 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4124 if (player->use_murphy)
4126 // this works only because currently only one player can be "murphy" ...
4127 static int last_horizontal_dir = MV_LEFT;
4128 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4130 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4131 last_horizontal_dir = move_dir;
4133 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4135 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4137 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4143 return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4146 static boolean equalGraphics(int graphic1, int graphic2)
4148 struct GraphicInfo *g1 = &graphic_info[graphic1];
4149 struct GraphicInfo *g2 = &graphic_info[graphic2];
4151 return (g1->bitmap == g2->bitmap &&
4152 g1->src_x == g2->src_x &&
4153 g1->src_y == g2->src_y &&
4154 g1->anim_frames == g2->anim_frames &&
4155 g1->anim_delay == g2->anim_delay &&
4156 g1->anim_mode == g2->anim_mode);
4159 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4163 DRAW_PLAYER_STAGE_INIT = 0,
4164 DRAW_PLAYER_STAGE_LAST_FIELD,
4165 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4166 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4167 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4168 DRAW_PLAYER_STAGE_PLAYER,
4170 DRAW_PLAYER_STAGE_PLAYER,
4171 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4173 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4174 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4176 NUM_DRAW_PLAYER_STAGES
4179 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4181 static int static_last_player_graphic[MAX_PLAYERS];
4182 static int static_last_player_frame[MAX_PLAYERS];
4183 static boolean static_player_is_opaque[MAX_PLAYERS];
4184 static boolean draw_player[MAX_PLAYERS];
4185 int pnr = player->index_nr;
4187 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4189 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4190 static_last_player_frame[pnr] = player->Frame;
4191 static_player_is_opaque[pnr] = FALSE;
4193 draw_player[pnr] = TRUE;
4196 if (!draw_player[pnr])
4200 if (!IN_LEV_FIELD(player->jx, player->jy))
4202 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4203 Debug("draw:DrawPlayerExt", "This should never happen!");
4205 draw_player[pnr] = FALSE;
4211 int last_player_graphic = static_last_player_graphic[pnr];
4212 int last_player_frame = static_last_player_frame[pnr];
4213 boolean player_is_opaque = static_player_is_opaque[pnr];
4215 int jx = player->jx;
4216 int jy = player->jy;
4217 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4218 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4219 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4220 int last_jx = (player->is_moving ? jx - dx : jx);
4221 int last_jy = (player->is_moving ? jy - dy : jy);
4222 int next_jx = jx + dx;
4223 int next_jy = jy + dy;
4224 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4225 int sx = SCREENX(jx);
4226 int sy = SCREENY(jy);
4227 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4228 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4229 int element = Tile[jx][jy];
4230 int last_element = Tile[last_jx][last_jy];
4231 int action = (player->is_pushing ? ACTION_PUSHING :
4232 player->is_digging ? ACTION_DIGGING :
4233 player->is_collecting ? ACTION_COLLECTING :
4234 player->is_moving ? ACTION_MOVING :
4235 player->is_snapping ? ACTION_SNAPPING :
4236 player->is_dropping ? ACTION_DROPPING :
4237 player->is_waiting ? player->action_waiting :
4240 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4242 // ------------------------------------------------------------------------
4243 // initialize drawing the player
4244 // ------------------------------------------------------------------------
4246 draw_player[pnr] = FALSE;
4248 // GfxElement[][] is set to the element the player is digging or collecting;
4249 // remove also for off-screen player if the player is not moving anymore
4250 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4251 GfxElement[jx][jy] = EL_UNDEFINED;
4253 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4256 if (element == EL_EXPLOSION)
4259 InitPlayerGfxAnimation(player, action, move_dir);
4261 draw_player[pnr] = TRUE;
4263 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4265 // ------------------------------------------------------------------------
4266 // draw things in the field the player is leaving, if needed
4267 // ------------------------------------------------------------------------
4269 if (!IN_SCR_FIELD(sx, sy))
4270 draw_player[pnr] = FALSE;
4272 if (!player->is_moving)
4275 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4277 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4279 if (last_element == EL_DYNAMITE_ACTIVE ||
4280 last_element == EL_EM_DYNAMITE_ACTIVE ||
4281 last_element == EL_SP_DISK_RED_ACTIVE)
4282 DrawDynamite(last_jx, last_jy);
4284 DrawLevelFieldThruMask(last_jx, last_jy);
4286 else if (last_element == EL_DYNAMITE_ACTIVE ||
4287 last_element == EL_EM_DYNAMITE_ACTIVE ||
4288 last_element == EL_SP_DISK_RED_ACTIVE)
4289 DrawDynamite(last_jx, last_jy);
4291 DrawLevelField(last_jx, last_jy);
4293 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4295 // ------------------------------------------------------------------------
4296 // draw things behind the player, if needed
4297 // ------------------------------------------------------------------------
4301 DrawLevelElement(jx, jy, Back[jx][jy]);
4306 if (IS_ACTIVE_BOMB(element))
4308 DrawLevelElement(jx, jy, EL_EMPTY);
4313 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4315 int old_element = GfxElement[jx][jy];
4316 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4317 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4319 if (GFX_CRUMBLED(old_element))
4320 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4322 DrawScreenGraphic(sx, sy, old_graphic, frame);
4324 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4325 static_player_is_opaque[pnr] = TRUE;
4329 GfxElement[jx][jy] = EL_UNDEFINED;
4331 // make sure that pushed elements are drawn with correct frame rate
4332 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4334 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4335 GfxFrame[jx][jy] = player->StepFrame;
4337 DrawLevelField(jx, jy);
4340 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4342 // ------------------------------------------------------------------------
4343 // draw things the player is pushing, if needed
4344 // ------------------------------------------------------------------------
4346 if (!player->is_pushing || !player->is_moving)
4349 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4352 int gfx_frame = GfxFrame[jx][jy];
4354 if (!IS_MOVING(jx, jy)) // push movement already finished
4356 element = Tile[next_jx][next_jy];
4357 gfx_frame = GfxFrame[next_jx][next_jy];
4360 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4361 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4362 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4364 // draw background element under pushed element (like the Sokoban field)
4365 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4367 // this allows transparent pushing animation over non-black background
4370 DrawLevelElement(jx, jy, Back[jx][jy]);
4372 DrawLevelElement(jx, jy, EL_EMPTY);
4375 if (Back[next_jx][next_jy])
4376 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4378 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4380 int px = SCREENX(jx), py = SCREENY(jy);
4381 int pxx = (TILEX - ABS(sxx)) * dx;
4382 int pyy = (TILEY - ABS(syy)) * dy;
4385 // do not draw (EM style) pushing animation when pushing is finished
4386 // (two-tile animations usually do not contain start and end frame)
4387 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4388 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4390 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4392 // masked drawing is needed for EMC style (double) movement graphics
4393 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4394 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4397 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4399 // ------------------------------------------------------------------------
4400 // draw player himself
4401 // ------------------------------------------------------------------------
4403 int graphic = getPlayerGraphic(player, move_dir);
4405 // in the case of changed player action or direction, prevent the current
4406 // animation frame from being restarted for identical animations
4407 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4408 player->Frame = last_player_frame;
4410 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4412 if (player_is_opaque)
4413 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4415 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4417 if (SHIELD_ON(player))
4419 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4420 IMG_SHIELD_NORMAL_ACTIVE);
4421 frame = getGraphicAnimationFrame(graphic, -1);
4423 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4426 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4428 // ------------------------------------------------------------------------
4429 // draw things in front of player (active dynamite or dynabombs)
4430 // ------------------------------------------------------------------------
4432 if (IS_ACTIVE_BOMB(element))
4434 int graphic = el2img(element);
4435 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4437 if (game.emulation == EMU_SUPAPLEX)
4438 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4440 DrawGraphicThruMask(sx, sy, graphic, frame);
4443 if (player_is_moving && last_element == EL_EXPLOSION)
4445 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4446 GfxElement[last_jx][last_jy] : EL_EMPTY);
4447 int graphic = el_act2img(element, ACTION_EXPLODING);
4448 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4449 int phase = ExplodePhase[last_jx][last_jy] - 1;
4450 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4453 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4456 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4458 // ------------------------------------------------------------------------
4459 // draw elements the player is just walking/passing through/under
4460 // ------------------------------------------------------------------------
4462 if (player_is_moving)
4464 // handle the field the player is leaving ...
4465 if (IS_ACCESSIBLE_INSIDE(last_element))
4466 DrawLevelField(last_jx, last_jy);
4467 else if (IS_ACCESSIBLE_UNDER(last_element))
4468 DrawLevelFieldThruMask(last_jx, last_jy);
4471 // do not redraw accessible elements if the player is just pushing them
4472 if (!player_is_moving || !player->is_pushing)
4474 // ... and the field the player is entering
4475 if (IS_ACCESSIBLE_INSIDE(element))
4476 DrawLevelField(jx, jy);
4477 else if (IS_ACCESSIBLE_UNDER(element))
4478 DrawLevelFieldThruMask(jx, jy);
4481 MarkTileDirty(sx, sy);
4485 void DrawPlayer(struct PlayerInfo *player)
4489 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4490 DrawPlayerExt(player, i);
4493 void DrawAllPlayers(void)
4497 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4498 for (j = 0; j < MAX_PLAYERS; j++)
4499 if (stored_player[j].active)
4500 DrawPlayerExt(&stored_player[j], i);
4503 void DrawPlayerField(int x, int y)
4505 if (!IS_PLAYER(x, y))
4508 DrawPlayer(PLAYERINFO(x, y));
4511 // ----------------------------------------------------------------------------
4513 void WaitForEventToContinue(void)
4515 boolean first_wait = TRUE;
4516 boolean still_wait = TRUE;
4518 if (program.headless)
4521 // simulate releasing mouse button over last gadget, if still pressed
4523 HandleGadgets(-1, -1, 0);
4525 button_status = MB_RELEASED;
4528 ClearPlayerAction();
4534 if (NextValidEvent(&event))
4538 case EVENT_BUTTONPRESS:
4539 case EVENT_FINGERPRESS:
4543 case EVENT_BUTTONRELEASE:
4544 case EVENT_FINGERRELEASE:
4545 still_wait = first_wait;
4548 case EVENT_KEYPRESS:
4549 case SDL_CONTROLLERBUTTONDOWN:
4550 case SDL_JOYBUTTONDOWN:
4555 HandleOtherEvents(&event);
4559 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4564 if (!PendingEvent())
4569 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4571 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4572 int draw_buffer_last = GetDrawtoField();
4573 int width = request.width;
4574 int height = request.height;
4578 setRequestPosition(&sx, &sy, FALSE);
4580 button_status = MB_RELEASED;
4582 request_gadget_id = -1;
4589 SetDrawtoField(draw_buffer_game);
4591 HandleGameActions();
4593 SetDrawtoField(DRAW_TO_BACKBUFFER);
4600 while (NextValidEvent(&event))
4604 case EVENT_BUTTONPRESS:
4605 case EVENT_BUTTONRELEASE:
4606 case EVENT_MOTIONNOTIFY:
4608 DrawBuffer *drawto_last = drawto;
4611 if (event.type == EVENT_MOTIONNOTIFY)
4616 motion_status = TRUE;
4617 mx = ((MotionEvent *) &event)->x;
4618 my = ((MotionEvent *) &event)->y;
4622 motion_status = FALSE;
4623 mx = ((ButtonEvent *) &event)->x;
4624 my = ((ButtonEvent *) &event)->y;
4625 if (event.type == EVENT_BUTTONPRESS)
4626 button_status = ((ButtonEvent *) &event)->button;
4628 button_status = MB_RELEASED;
4631 if (global.use_envelope_request)
4633 // draw changed button states to temporary bitmap
4634 drawto = bitmap_db_store_1;
4637 // this sets 'request_gadget_id'
4638 HandleGadgets(mx, my, button_status);
4640 if (global.use_envelope_request)
4642 // restore pointer to drawing buffer
4643 drawto = drawto_last;
4645 // prepare complete envelope request from temporary bitmap
4646 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy,
4650 switch (request_gadget_id)
4652 case TOOL_CTRL_ID_YES:
4653 case TOOL_CTRL_ID_TOUCH_YES:
4656 case TOOL_CTRL_ID_NO:
4657 case TOOL_CTRL_ID_TOUCH_NO:
4660 case TOOL_CTRL_ID_CONFIRM:
4661 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4662 result = TRUE | FALSE;
4665 case TOOL_CTRL_ID_PLAYER_1:
4668 case TOOL_CTRL_ID_PLAYER_2:
4671 case TOOL_CTRL_ID_PLAYER_3:
4674 case TOOL_CTRL_ID_PLAYER_4:
4682 // only needed to handle clickable pointer animations here
4683 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4688 case SDL_WINDOWEVENT:
4689 HandleWindowEvent((WindowEvent *) &event);
4692 case SDL_APP_WILLENTERBACKGROUND:
4693 case SDL_APP_DIDENTERBACKGROUND:
4694 case SDL_APP_WILLENTERFOREGROUND:
4695 case SDL_APP_DIDENTERFOREGROUND:
4696 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4699 case EVENT_KEYPRESS:
4701 Key key = GetEventKey((KeyEvent *)&event);
4706 if (req_state & REQ_CONFIRM)
4715 #if defined(KSYM_Rewind)
4716 case KSYM_Rewind: // for Amazon Fire TV remote
4725 #if defined(KSYM_FastForward)
4726 case KSYM_FastForward: // for Amazon Fire TV remote
4732 HandleKeysDebug(key, KEY_PRESSED);
4736 if (req_state & REQ_PLAYER)
4738 int old_player_nr = setup.network_player_nr;
4741 result = old_player_nr + 1;
4746 result = old_player_nr + 1;
4777 case EVENT_FINGERRELEASE:
4778 case EVENT_KEYRELEASE:
4779 ClearPlayerAction();
4782 case SDL_CONTROLLERBUTTONDOWN:
4783 switch (event.cbutton.button)
4785 case SDL_CONTROLLER_BUTTON_A:
4786 case SDL_CONTROLLER_BUTTON_X:
4787 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4788 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4792 case SDL_CONTROLLER_BUTTON_B:
4793 case SDL_CONTROLLER_BUTTON_Y:
4794 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4795 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4796 case SDL_CONTROLLER_BUTTON_BACK:
4801 if (req_state & REQ_PLAYER)
4803 int old_player_nr = setup.network_player_nr;
4806 result = old_player_nr + 1;
4808 switch (event.cbutton.button)
4810 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4811 case SDL_CONTROLLER_BUTTON_Y:
4815 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4816 case SDL_CONTROLLER_BUTTON_B:
4820 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4821 case SDL_CONTROLLER_BUTTON_A:
4825 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4826 case SDL_CONTROLLER_BUTTON_X:
4837 case SDL_CONTROLLERBUTTONUP:
4838 HandleJoystickEvent(&event);
4839 ClearPlayerAction();
4843 HandleOtherEvents(&event);
4848 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4850 int joy = AnyJoystick();
4852 if (joy & JOY_BUTTON_1)
4854 else if (joy & JOY_BUTTON_2)
4857 else if (AnyJoystick())
4859 int joy = AnyJoystick();
4861 if (req_state & REQ_PLAYER)
4865 else if (joy & JOY_RIGHT)
4867 else if (joy & JOY_DOWN)
4869 else if (joy & JOY_LEFT)
4877 SetDrawtoField(draw_buffer_last);
4882 static void DoRequestBefore(void)
4884 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4886 // when showing request dialog after game ended, deactivate game panel
4888 game.panel.active = FALSE;
4890 if (game_status == GAME_MODE_PLAYING)
4891 BlitScreenToBitmap(backbuffer);
4893 // disable deactivated drawing when quick-loading level tape recording
4894 if (tape.playing && tape.deactivate_display)
4895 TapeDeactivateDisplayOff(TRUE);
4897 SetMouseCursor(CURSOR_DEFAULT);
4899 // pause network game while waiting for request to answer
4900 if (network.enabled &&
4901 game_status == GAME_MODE_PLAYING &&
4902 !game.all_players_gone)
4903 SendToServer_PausePlaying();
4905 // simulate releasing mouse button over last gadget, if still pressed
4907 HandleGadgets(-1, -1, 0);
4912 static void DoRequestAfter(void)
4916 if (game_status == GAME_MODE_PLAYING)
4918 SetPanelBackground();
4919 SetDrawBackgroundMask(REDRAW_DOOR_1);
4923 SetDrawBackgroundMask(REDRAW_FIELD);
4926 // continue network game after request
4927 if (network.enabled &&
4928 game_status == GAME_MODE_PLAYING &&
4929 !game.all_players_gone)
4930 SendToServer_ContinuePlaying();
4932 // restore deactivated drawing when quick-loading level tape recording
4933 if (tape.playing && tape.deactivate_display)
4934 TapeDeactivateDisplayOn();
4937 static void setRequestDoorTextProperties(char *text,
4942 int *set_max_line_length)
4944 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
4945 struct TextPosInfo *pos = &request.button.confirm;
4946 int button_ypos = pos->y;
4947 int font_nr = FONT_TEXT_2;
4948 int font_width = getFontWidth(font_nr);
4949 int font_height = getFontHeight(font_nr);
4950 int line_height = font_height + line_spacing;
4951 int max_text_width = vp_door_1->width;
4952 int max_text_height = button_ypos - 2 * text_spacing;
4953 int max_line_length = max_text_width / font_width;
4954 int max_lines = max_text_height / line_height;
4956 if (maxWordLengthInRequestString(text) > max_line_length)
4958 font_nr = FONT_TEXT_1;
4959 font_width = getFontWidth(font_nr);
4960 max_line_length = max_text_width / font_width;
4963 *set_font_nr = font_nr;
4964 *set_max_lines = max_lines;
4965 *set_max_line_length = max_line_length;
4968 static void DrawRequestDoorText(char *text)
4970 char *text_ptr = text;
4971 int text_spacing = 8;
4972 int line_spacing = 2;
4973 int max_request_lines;
4974 int max_request_line_len;
4978 // force DOOR font inside door area
4979 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4981 setRequestDoorTextProperties(text, text_spacing, line_spacing, &font_nr,
4982 &max_request_lines, &max_request_line_len);
4984 for (text_ptr = text, ty = 0; ty < max_request_lines; ty++)
4986 char text_line[max_request_line_len + 1];
4992 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4994 tc = *(text_ptr + tx);
4995 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4999 if ((tc == '?' || tc == '!') && tl == 0)
5009 strncpy(text_line, text_ptr, tl);
5012 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
5013 DY + text_spacing + ty * (getFontHeight(font_nr) + line_spacing),
5014 text_line, font_nr);
5016 text_ptr += tl + (tc == ' ' ? 1 : 0);
5022 static int RequestDoor(char *text, unsigned int req_state)
5024 unsigned int old_door_state = GetDoorState();
5025 int draw_buffer_last = GetDrawtoField();
5028 if (old_door_state & DOOR_OPEN_1)
5030 CloseDoor(DOOR_CLOSE_1);
5032 // save old door content
5033 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5034 0, 0, DXSIZE, DYSIZE, DXSIZE, 0);
5037 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5038 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5040 // clear door drawing field
5041 DrawBackground(DX, DY, DXSIZE, DYSIZE);
5043 // write text for request
5044 DrawRequestDoorText(text);
5046 MapToolButtons(req_state);
5048 // copy request gadgets to door backbuffer
5049 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
5051 OpenDoor(DOOR_OPEN_1);
5053 // ---------- handle request buttons ----------
5054 result = RequestHandleEvents(req_state, draw_buffer_last);
5058 if (!(req_state & REQ_STAY_OPEN))
5060 CloseDoor(DOOR_CLOSE_1);
5062 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
5063 (req_state & REQ_REOPEN))
5064 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
5070 static int RequestEnvelope(char *text, unsigned int req_state)
5072 int draw_buffer_last = GetDrawtoField();
5075 DrawEnvelopeRequest(text, req_state);
5076 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5078 // ---------- handle request buttons ----------
5079 result = RequestHandleEvents(req_state, draw_buffer_last);
5083 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5088 int Request(char *text, unsigned int req_state)
5090 boolean overlay_enabled = GetOverlayEnabled();
5093 game.request_active = TRUE;
5095 SetOverlayEnabled(FALSE);
5099 if (global.use_envelope_request)
5100 result = RequestEnvelope(text, req_state);
5102 result = RequestDoor(text, req_state);
5106 SetOverlayEnabled(overlay_enabled);
5108 game.request_active = FALSE;
5113 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5115 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5116 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5119 if (dpo1->sort_priority != dpo2->sort_priority)
5120 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5122 compare_result = dpo1->nr - dpo2->nr;
5124 return compare_result;
5127 void InitGraphicCompatibilityInfo_Doors(void)
5133 struct DoorInfo *door;
5137 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5138 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5140 { -1, -1, -1, NULL }
5142 struct Rect door_rect_list[] =
5144 { DX, DY, DXSIZE, DYSIZE },
5145 { VX, VY, VXSIZE, VYSIZE }
5149 for (i = 0; doors[i].door_token != -1; i++)
5151 int door_token = doors[i].door_token;
5152 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5153 int part_1 = doors[i].part_1;
5154 int part_8 = doors[i].part_8;
5155 int part_2 = part_1 + 1;
5156 int part_3 = part_1 + 2;
5157 struct DoorInfo *door = doors[i].door;
5158 struct Rect *door_rect = &door_rect_list[door_index];
5159 boolean door_gfx_redefined = FALSE;
5161 // check if any door part graphic definitions have been redefined
5163 for (j = 0; door_part_controls[j].door_token != -1; j++)
5165 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5166 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5168 if (dpc->door_token == door_token && fi->redefined)
5169 door_gfx_redefined = TRUE;
5172 // check for old-style door graphic/animation modifications
5174 if (!door_gfx_redefined)
5176 if (door->anim_mode & ANIM_STATIC_PANEL)
5178 door->panel.step_xoffset = 0;
5179 door->panel.step_yoffset = 0;
5182 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5184 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5185 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5186 int num_door_steps, num_panel_steps;
5188 // remove door part graphics other than the two default wings
5190 for (j = 0; door_part_controls[j].door_token != -1; j++)
5192 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5193 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5195 if (dpc->graphic >= part_3 &&
5196 dpc->graphic <= part_8)
5200 // set graphics and screen positions of the default wings
5202 g_part_1->width = door_rect->width;
5203 g_part_1->height = door_rect->height;
5204 g_part_2->width = door_rect->width;
5205 g_part_2->height = door_rect->height;
5206 g_part_2->src_x = door_rect->width;
5207 g_part_2->src_y = g_part_1->src_y;
5209 door->part_2.x = door->part_1.x;
5210 door->part_2.y = door->part_1.y;
5212 if (door->width != -1)
5214 g_part_1->width = door->width;
5215 g_part_2->width = door->width;
5217 // special treatment for graphics and screen position of right wing
5218 g_part_2->src_x += door_rect->width - door->width;
5219 door->part_2.x += door_rect->width - door->width;
5222 if (door->height != -1)
5224 g_part_1->height = door->height;
5225 g_part_2->height = door->height;
5227 // special treatment for graphics and screen position of bottom wing
5228 g_part_2->src_y += door_rect->height - door->height;
5229 door->part_2.y += door_rect->height - door->height;
5232 // set animation delays for the default wings and panels
5234 door->part_1.step_delay = door->step_delay;
5235 door->part_2.step_delay = door->step_delay;
5236 door->panel.step_delay = door->step_delay;
5238 // set animation draw order for the default wings
5240 door->part_1.sort_priority = 2; // draw left wing over ...
5241 door->part_2.sort_priority = 1; // ... right wing
5243 // set animation draw offset for the default wings
5245 if (door->anim_mode & ANIM_HORIZONTAL)
5247 door->part_1.step_xoffset = door->step_offset;
5248 door->part_1.step_yoffset = 0;
5249 door->part_2.step_xoffset = door->step_offset * -1;
5250 door->part_2.step_yoffset = 0;
5252 num_door_steps = g_part_1->width / door->step_offset;
5254 else // ANIM_VERTICAL
5256 door->part_1.step_xoffset = 0;
5257 door->part_1.step_yoffset = door->step_offset;
5258 door->part_2.step_xoffset = 0;
5259 door->part_2.step_yoffset = door->step_offset * -1;
5261 num_door_steps = g_part_1->height / door->step_offset;
5264 // set animation draw offset for the default panels
5266 if (door->step_offset > 1)
5268 num_panel_steps = 2 * door_rect->height / door->step_offset;
5269 door->panel.start_step = num_panel_steps - num_door_steps;
5270 door->panel.start_step_closing = door->panel.start_step;
5274 num_panel_steps = door_rect->height / door->step_offset;
5275 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5276 door->panel.start_step_closing = door->panel.start_step;
5277 door->panel.step_delay *= 2;
5284 void InitDoors(void)
5288 for (i = 0; door_part_controls[i].door_token != -1; i++)
5290 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5291 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5293 // initialize "start_step_opening" and "start_step_closing", if needed
5294 if (dpc->pos->start_step_opening == 0 &&
5295 dpc->pos->start_step_closing == 0)
5297 // dpc->pos->start_step_opening = dpc->pos->start_step;
5298 dpc->pos->start_step_closing = dpc->pos->start_step;
5301 // fill structure for door part draw order (sorted below)
5303 dpo->sort_priority = dpc->pos->sort_priority;
5306 // sort door part controls according to sort_priority and graphic number
5307 qsort(door_part_order, MAX_DOOR_PARTS,
5308 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5311 unsigned int OpenDoor(unsigned int door_state)
5313 if (door_state & DOOR_COPY_BACK)
5315 if (door_state & DOOR_OPEN_1)
5316 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5317 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5319 if (door_state & DOOR_OPEN_2)
5320 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5321 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5323 door_state &= ~DOOR_COPY_BACK;
5326 return MoveDoor(door_state);
5329 unsigned int CloseDoor(unsigned int door_state)
5331 unsigned int old_door_state = GetDoorState();
5333 if (!(door_state & DOOR_NO_COPY_BACK))
5335 if (old_door_state & DOOR_OPEN_1)
5336 BlitBitmap(backbuffer, bitmap_db_door_1,
5337 DX, DY, DXSIZE, DYSIZE, 0, 0);
5339 if (old_door_state & DOOR_OPEN_2)
5340 BlitBitmap(backbuffer, bitmap_db_door_2,
5341 VX, VY, VXSIZE, VYSIZE, 0, 0);
5343 door_state &= ~DOOR_NO_COPY_BACK;
5346 return MoveDoor(door_state);
5349 unsigned int GetDoorState(void)
5351 return MoveDoor(DOOR_GET_STATE);
5354 unsigned int SetDoorState(unsigned int door_state)
5356 return MoveDoor(door_state | DOOR_SET_STATE);
5359 static int euclid(int a, int b)
5361 return (b ? euclid(b, a % b) : a);
5364 unsigned int MoveDoor(unsigned int door_state)
5366 struct Rect door_rect_list[] =
5368 { DX, DY, DXSIZE, DYSIZE },
5369 { VX, VY, VXSIZE, VYSIZE }
5371 static int door1 = DOOR_CLOSE_1;
5372 static int door2 = DOOR_CLOSE_2;
5373 DelayCounter door_delay = { 0 };
5376 if (door_state == DOOR_GET_STATE)
5377 return (door1 | door2);
5379 if (door_state & DOOR_SET_STATE)
5381 if (door_state & DOOR_ACTION_1)
5382 door1 = door_state & DOOR_ACTION_1;
5383 if (door_state & DOOR_ACTION_2)
5384 door2 = door_state & DOOR_ACTION_2;
5386 return (door1 | door2);
5389 if (!(door_state & DOOR_FORCE_REDRAW))
5391 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5392 door_state &= ~DOOR_OPEN_1;
5393 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5394 door_state &= ~DOOR_CLOSE_1;
5395 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5396 door_state &= ~DOOR_OPEN_2;
5397 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5398 door_state &= ~DOOR_CLOSE_2;
5401 if (global.autoplay_leveldir)
5403 door_state |= DOOR_NO_DELAY;
5404 door_state &= ~DOOR_CLOSE_ALL;
5407 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5408 door_state |= DOOR_NO_DELAY;
5410 if (door_state & DOOR_ACTION)
5412 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
5413 boolean door_panel_drawn[NUM_DOORS];
5414 boolean panel_has_doors[NUM_DOORS];
5415 boolean door_part_skip[MAX_DOOR_PARTS];
5416 boolean door_part_done[MAX_DOOR_PARTS];
5417 boolean door_part_done_all;
5418 int num_steps[MAX_DOOR_PARTS];
5419 int max_move_delay = 0; // delay for complete animations of all doors
5420 int max_step_delay = 0; // delay (ms) between two animation frames
5421 int num_move_steps = 0; // number of animation steps for all doors
5422 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5423 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5427 for (i = 0; i < NUM_DOORS; i++)
5428 panel_has_doors[i] = FALSE;
5430 for (i = 0; i < MAX_DOOR_PARTS; i++)
5432 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5433 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5434 int door_token = dpc->door_token;
5436 door_part_done[i] = FALSE;
5437 door_part_skip[i] = (!(door_state & door_token) ||
5441 for (i = 0; i < MAX_DOOR_PARTS; i++)
5443 int nr = door_part_order[i].nr;
5444 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5445 struct DoorPartPosInfo *pos = dpc->pos;
5446 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5447 int door_token = dpc->door_token;
5448 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5449 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5450 int step_xoffset = ABS(pos->step_xoffset);
5451 int step_yoffset = ABS(pos->step_yoffset);
5452 int step_delay = pos->step_delay;
5453 int current_door_state = door_state & door_token;
5454 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5455 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5456 boolean part_opening = (is_panel ? door_closing : door_opening);
5457 int start_step = (part_opening ? pos->start_step_opening :
5458 pos->start_step_closing);
5459 float move_xsize = (step_xoffset ? g->width : 0);
5460 float move_ysize = (step_yoffset ? g->height : 0);
5461 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5462 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5463 int move_steps = (move_xsteps && move_ysteps ?
5464 MIN(move_xsteps, move_ysteps) :
5465 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5466 int move_delay = move_steps * step_delay;
5468 if (door_part_skip[nr])
5471 max_move_delay = MAX(max_move_delay, move_delay);
5472 max_step_delay = (max_step_delay == 0 ? step_delay :
5473 euclid(max_step_delay, step_delay));
5474 num_steps[nr] = move_steps;
5478 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5480 panel_has_doors[door_index] = TRUE;
5484 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5486 num_move_steps = max_move_delay / max_step_delay;
5487 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5489 door_delay.value = max_step_delay;
5491 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5493 start = num_move_steps - 1;
5497 // opening door sound has priority over simultaneously closing door
5498 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5500 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5502 if (door_state & DOOR_OPEN_1)
5503 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5504 if (door_state & DOOR_OPEN_2)
5505 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5507 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5509 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5511 if (door_state & DOOR_CLOSE_1)
5512 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5513 if (door_state & DOOR_CLOSE_2)
5514 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5518 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5520 game.any_door_active = TRUE;
5522 for (k = start; k < num_move_steps; k++)
5524 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5526 door_part_done_all = TRUE;
5528 for (i = 0; i < NUM_DOORS; i++)
5529 door_panel_drawn[i] = FALSE;
5531 for (i = 0; i < MAX_DOOR_PARTS; i++)
5533 int nr = door_part_order[i].nr;
5534 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5535 struct DoorPartPosInfo *pos = dpc->pos;
5536 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5537 int door_token = dpc->door_token;
5538 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5539 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5540 boolean is_panel_and_door_has_closed = FALSE;
5541 struct Rect *door_rect = &door_rect_list[door_index];
5542 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5544 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5545 int current_door_state = door_state & door_token;
5546 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5547 boolean door_closing = !door_opening;
5548 boolean part_opening = (is_panel ? door_closing : door_opening);
5549 boolean part_closing = !part_opening;
5550 int start_step = (part_opening ? pos->start_step_opening :
5551 pos->start_step_closing);
5552 int step_delay = pos->step_delay;
5553 int step_factor = step_delay / max_step_delay;
5554 int k1 = (step_factor ? k / step_factor + 1 : k);
5555 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5556 int kk = MAX(0, k2);
5559 int src_x, src_y, src_xx, src_yy;
5560 int dst_x, dst_y, dst_xx, dst_yy;
5563 if (door_part_skip[nr])
5566 if (!(door_state & door_token))
5574 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5575 int kk_door = MAX(0, k2_door);
5576 int sync_frame = kk_door * door_delay.value;
5577 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5579 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5580 &g_src_x, &g_src_y);
5585 if (!door_panel_drawn[door_index])
5587 ClearRectangleOnBackground(drawto, door_rect->x, door_rect->y,
5588 door_rect->width, door_rect->height);
5590 door_panel_drawn[door_index] = TRUE;
5593 // draw opening or closing door parts
5595 if (pos->step_xoffset < 0) // door part on right side
5598 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5601 if (dst_xx + width > door_rect->width)
5602 width = door_rect->width - dst_xx;
5604 else // door part on left side
5607 dst_xx = pos->x - kk * pos->step_xoffset;
5611 src_xx = ABS(dst_xx);
5615 width = g->width - src_xx;
5617 if (width > door_rect->width)
5618 width = door_rect->width;
5620 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5623 if (pos->step_yoffset < 0) // door part on bottom side
5626 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5629 if (dst_yy + height > door_rect->height)
5630 height = door_rect->height - dst_yy;
5632 else // door part on top side
5635 dst_yy = pos->y - kk * pos->step_yoffset;
5639 src_yy = ABS(dst_yy);
5643 height = g->height - src_yy;
5646 src_x = g_src_x + src_xx;
5647 src_y = g_src_y + src_yy;
5649 dst_x = door_rect->x + dst_xx;
5650 dst_y = door_rect->y + dst_yy;
5652 is_panel_and_door_has_closed =
5655 panel_has_doors[door_index] &&
5656 k >= num_move_steps_doors_only - 1);
5658 if (width >= 0 && width <= g->width &&
5659 height >= 0 && height <= g->height &&
5660 !is_panel_and_door_has_closed)
5662 if (is_panel || !pos->draw_masked)
5663 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5666 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5670 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5672 if ((part_opening && (width < 0 || height < 0)) ||
5673 (part_closing && (width >= g->width && height >= g->height)))
5674 door_part_done[nr] = TRUE;
5676 // continue door part animations, but not panel after door has closed
5677 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5678 door_part_done_all = FALSE;
5681 if (!(door_state & DOOR_NO_DELAY))
5684 HandleGameActions();
5688 SkipUntilDelayReached(&door_delay, &k, last_frame);
5690 // prevent OS (Windows) from complaining about program not responding
5694 if (door_part_done_all)
5698 if (!(door_state & DOOR_NO_DELAY))
5700 // wait for specified door action post delay
5701 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5702 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5703 else if (door_state & DOOR_ACTION_1)
5704 door_delay.value = door_1.post_delay;
5705 else if (door_state & DOOR_ACTION_2)
5706 door_delay.value = door_2.post_delay;
5708 while (!DelayReached(&door_delay))
5711 HandleGameActions();
5717 game.any_door_active = FALSE;
5720 if (door_state & DOOR_ACTION_1)
5721 door1 = door_state & DOOR_ACTION_1;
5722 if (door_state & DOOR_ACTION_2)
5723 door2 = door_state & DOOR_ACTION_2;
5725 // draw masked border over door area
5726 DrawMaskedBorder(REDRAW_DOOR_1);
5727 DrawMaskedBorder(REDRAW_DOOR_2);
5729 ClearAutoRepeatKeyEvents();
5731 return (door1 | door2);
5734 static boolean useSpecialEditorDoor(void)
5736 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5737 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5739 // do not draw special editor door if editor border defined or redefined
5740 if (graphic_info[graphic].bitmap != NULL || redefined)
5743 // do not draw special editor door if global border defined to be empty
5744 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5747 // do not draw special editor door if viewport definitions do not match
5751 EY + EYSIZE != VY + VYSIZE)
5757 void DrawSpecialEditorDoor(void)
5759 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5760 int top_border_width = gfx1->width;
5761 int top_border_height = gfx1->height;
5762 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5763 int ex = EX - outer_border;
5764 int ey = EY - outer_border;
5765 int vy = VY - outer_border;
5766 int exsize = EXSIZE + 2 * outer_border;
5768 if (!useSpecialEditorDoor())
5771 // draw bigger level editor toolbox window
5772 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5773 top_border_width, top_border_height, ex, ey - top_border_height);
5774 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5775 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5777 redraw_mask |= REDRAW_ALL;
5780 void UndrawSpecialEditorDoor(void)
5782 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5783 int top_border_width = gfx1->width;
5784 int top_border_height = gfx1->height;
5785 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5786 int ex = EX - outer_border;
5787 int ey = EY - outer_border;
5788 int ey_top = ey - top_border_height;
5789 int exsize = EXSIZE + 2 * outer_border;
5790 int eysize = EYSIZE + 2 * outer_border;
5792 if (!useSpecialEditorDoor())
5795 // draw normal tape recorder window
5796 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5798 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5799 ex, ey_top, top_border_width, top_border_height,
5801 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5802 ex, ey, exsize, eysize, ex, ey);
5806 // if screen background is set to "[NONE]", clear editor toolbox window
5807 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5808 ClearRectangle(drawto, ex, ey, exsize, eysize);
5811 redraw_mask |= REDRAW_ALL;
5815 // ---------- new tool button stuff -------------------------------------------
5820 struct TextPosInfo *pos;
5822 boolean is_touch_button;
5824 } toolbutton_info[NUM_TOOL_BUTTONS] =
5827 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5828 TOOL_CTRL_ID_YES, FALSE, "yes"
5831 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5832 TOOL_CTRL_ID_NO, FALSE, "no"
5835 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5836 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5839 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5840 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5843 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5844 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5847 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5848 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5851 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5852 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5855 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5856 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5859 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5860 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5863 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5864 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5868 void CreateToolButtons(void)
5872 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5874 int graphic = toolbutton_info[i].graphic;
5875 struct GraphicInfo *gfx = &graphic_info[graphic];
5876 struct TextPosInfo *pos = toolbutton_info[i].pos;
5877 struct GadgetInfo *gi;
5878 Bitmap *deco_bitmap = None;
5879 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5880 unsigned int event_mask = GD_EVENT_RELEASED;
5881 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5882 int base_x = (is_touch_button ? 0 : DX);
5883 int base_y = (is_touch_button ? 0 : DY);
5884 int gd_x = gfx->src_x;
5885 int gd_y = gfx->src_y;
5886 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5887 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5892 // do not use touch buttons if overlay touch buttons are disabled
5893 if (is_touch_button && !setup.touch.overlay_buttons)
5896 if (global.use_envelope_request && !is_touch_button)
5898 setRequestPosition(&base_x, &base_y, TRUE);
5900 // check if request buttons are outside of envelope and fix, if needed
5901 if (x < 0 || x + gfx->width > request.width ||
5902 y < 0 || y + gfx->height > request.height)
5904 if (id == TOOL_CTRL_ID_YES)
5907 y = request.height - 2 * request.border_size - gfx->height;
5909 else if (id == TOOL_CTRL_ID_NO)
5911 x = request.width - 2 * request.border_size - gfx->width;
5912 y = request.height - 2 * request.border_size - gfx->height;
5914 else if (id == TOOL_CTRL_ID_CONFIRM)
5916 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5917 y = request.height - 2 * request.border_size - gfx->height;
5919 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5921 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5923 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5924 y = request.height - 2 * request.border_size - gfx->height * 2;
5926 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5927 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5932 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5935 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5937 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5938 pos->size, &deco_bitmap, &deco_x, &deco_y);
5939 deco_xpos = (gfx->width - pos->size) / 2;
5940 deco_ypos = (gfx->height - pos->size) / 2;
5943 gi = CreateGadget(GDI_CUSTOM_ID, id,
5944 GDI_IMAGE_ID, graphic,
5945 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5948 GDI_WIDTH, gfx->width,
5949 GDI_HEIGHT, gfx->height,
5950 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5951 GDI_STATE, GD_BUTTON_UNPRESSED,
5952 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5953 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5954 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5955 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5956 GDI_DECORATION_SIZE, pos->size, pos->size,
5957 GDI_DECORATION_SHIFTING, 1, 1,
5958 GDI_DIRECT_DRAW, FALSE,
5959 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5960 GDI_EVENT_MASK, event_mask,
5961 GDI_CALLBACK_ACTION, HandleToolButtons,
5965 Fail("cannot create gadget");
5967 tool_gadget[id] = gi;
5971 void FreeToolButtons(void)
5975 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5976 FreeGadget(tool_gadget[i]);
5979 static void MapToolButtons(unsigned int req_state)
5981 if (req_state & REQ_ASK)
5983 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
5984 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
5985 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
5986 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
5988 else if (req_state & REQ_CONFIRM)
5990 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
5991 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
5993 else if (req_state & REQ_PLAYER)
5995 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
5996 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
5997 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
5998 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
6002 static void UnmapToolButtons(void)
6006 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
6007 UnmapGadget(tool_gadget[i]);
6010 static void HandleToolButtons(struct GadgetInfo *gi)
6012 request_gadget_id = gi->custom_id;
6015 static int getEngineElement_Ext(int element, int game_engine_type, boolean is_drawing_element)
6024 if (game_engine_type == -1)
6025 game_engine_type = level.game_engine_type;
6027 if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
6029 el_empty = EL_EMPTY;
6030 el_player = EL_BD_PLAYER;
6031 el_sand = EL_BD_SAND;
6032 el_wall = EL_BD_WALL;
6033 el_steelwall = EL_BD_STEELWALL;
6034 el_exit_closed = EL_BD_EXIT_CLOSED;
6036 else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
6038 el_empty = EL_EMPTY;
6039 el_player = EL_PLAYER_1;
6042 el_steelwall = EL_STEELWALL;
6043 el_exit_closed = EL_EM_EXIT_CLOSED;
6045 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
6047 el_empty = EL_EMPTY;
6048 el_player = EL_SP_MURPHY;
6049 el_sand = EL_SP_BASE;
6050 el_wall = EL_SP_CHIP_SINGLE;
6051 el_steelwall = EL_SP_HARDWARE_GRAY;
6052 el_exit_closed = EL_SP_EXIT_CLOSED;
6054 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
6056 el_empty = EL_EMPTY;
6057 el_player = EL_MM_MCDUFFIN_DOWN;
6059 el_wall = EL_MM_WOODEN_WALL;
6060 el_steelwall = EL_MM_STEEL_WALL;
6061 el_exit_closed = EL_MM_EXIT_CLOSED;
6063 if (is_drawing_element)
6065 el_wall = EL_MM_MIRROR_START;
6066 el_sand = EL_MM_WOODEN_WALL;
6071 el_empty = EL_EMPTY;
6072 el_player = EL_PLAYER_1;
6075 el_steelwall = EL_STEELWALL;
6076 el_exit_closed = EL_EXIT_CLOSED;
6079 return (element == EL_EMPTY ? el_empty :
6080 element == EL_PLAYER_1 ? el_player :
6081 element == EL_SAND ? el_sand :
6082 element == EL_WALL ? el_wall :
6083 element == EL_STEELWALL ? el_steelwall :
6084 element == EL_EXIT_CLOSED ? el_exit_closed : EL_EMPTY);
6087 int getEngineElement(int element)
6089 return getEngineElement_Ext(element, -1, FALSE);
6092 int getDrawingElement(int element)
6094 return getEngineElement_Ext(element, -1, TRUE);
6097 static struct Mapping_BD_to_RND_object
6100 boolean is_rnd_to_bd_mapping; // unique mapping BD <-> RND
6106 bd_object_mapping_list[] =
6108 // additional RND style elements mapped to BD style elements (must be listed first)
6124 EL_STEELWALL, -1, -1
6128 EL_BD_DIAMOND, -1, -1
6148 EL_EXIT_CLOSED, -1, -1
6151 // BD style elements with their corresponding RND style elements
6162 O_DIRT_SLOPED_UP_RIGHT, TRUE,
6163 EL_BD_SAND_SLOPED_UP_RIGHT, -1, -1
6166 O_DIRT_SLOPED_UP_LEFT, TRUE,
6167 EL_BD_SAND_SLOPED_UP_LEFT, -1, -1
6170 O_DIRT_SLOPED_DOWN_LEFT, TRUE,
6171 EL_BD_SAND_SLOPED_DOWN_LEFT, -1, -1
6174 O_DIRT_SLOPED_DOWN_RIGHT, TRUE,
6175 EL_BD_SAND_SLOPED_DOWN_RIGHT, -1, -1
6179 EL_BD_SAND_BALL, -1, -1
6182 O_DIRT_BALL_F, TRUE,
6183 EL_BD_SAND_BALL_FALLING, -1, -1
6186 O_DIRT_BALL_F, FALSE,
6187 EL_BD_SAND_BALL, ACTION_FALLING, -1
6191 EL_BD_SAND_LOOSE, -1, -1
6194 O_DIRT_LOOSE_F, TRUE,
6195 EL_BD_SAND_LOOSE_FALLING, -1, -1
6198 O_DIRT_LOOSE_F, FALSE,
6199 EL_BD_SAND_LOOSE, ACTION_FALLING, -1
6203 EL_BD_SAND_2, -1, -1
6210 O_BRICK_SLOPED_UP_RIGHT, TRUE,
6211 EL_BD_WALL_SLOPED_UP_RIGHT, -1, -1
6214 O_BRICK_SLOPED_UP_LEFT, TRUE,
6215 EL_BD_WALL_SLOPED_UP_LEFT, -1, -1
6218 O_BRICK_SLOPED_DOWN_LEFT, TRUE,
6219 EL_BD_WALL_SLOPED_DOWN_LEFT, -1, -1
6222 O_BRICK_SLOPED_DOWN_RIGHT, TRUE,
6223 EL_BD_WALL_SLOPED_DOWN_RIGHT, -1, -1
6226 O_BRICK_NON_SLOPED, TRUE,
6227 EL_BD_WALL_NON_SLOPED, -1, -1
6231 EL_BD_MAGIC_WALL, ACTION_ACTIVE, -1
6235 EL_BD_EXIT_CLOSED, -1, -1
6239 EL_BD_EXIT_OPEN, -1, -1
6242 O_PRE_INVIS_OUTBOX, TRUE,
6243 EL_BD_INVISIBLE_EXIT_CLOSED, -1, -1
6246 O_INVIS_OUTBOX, TRUE,
6247 EL_BD_INVISIBLE_EXIT_OPEN, -1, -1
6251 EL_BD_STEELWALL, -1, -1
6254 O_STEEL_SLOPED_UP_RIGHT, TRUE,
6255 EL_BD_STEELWALL_SLOPED_UP_RIGHT, -1, -1
6258 O_STEEL_SLOPED_UP_LEFT, TRUE,
6259 EL_BD_STEELWALL_SLOPED_UP_LEFT, -1, -1
6262 O_STEEL_SLOPED_DOWN_LEFT, TRUE,
6263 EL_BD_STEELWALL_SLOPED_DOWN_LEFT, -1, -1
6266 O_STEEL_SLOPED_DOWN_RIGHT, TRUE,
6267 EL_BD_STEELWALL_SLOPED_DOWN_RIGHT, -1, -1
6270 O_STEEL_EXPLODABLE, TRUE,
6271 EL_BD_STEELWALL_EXPLODABLE, -1, -1
6274 O_STEEL_EATABLE, TRUE,
6275 EL_BD_STEELWALL_DIGGABLE, -1, -1
6278 O_BRICK_EATABLE, TRUE,
6279 EL_BD_WALL_DIGGABLE, -1, -1
6287 EL_BD_ROCK_FALLING, -1, -1
6291 EL_BD_ROCK, ACTION_FALLING, -1
6294 O_FLYING_STONE, TRUE,
6295 EL_BD_FLYING_ROCK, -1, -1
6298 O_FLYING_STONE_F, TRUE,
6299 EL_BD_FLYING_ROCK_FLYING, -1, -1
6302 O_FLYING_STONE_F, FALSE,
6303 EL_BD_FLYING_ROCK, ACTION_FLYING, -1
6307 EL_BD_MEGA_ROCK, -1, -1
6310 O_MEGA_STONE_F, TRUE,
6311 EL_BD_MEGA_ROCK_FALLING, -1, -1
6314 O_MEGA_STONE_F, FALSE,
6315 EL_BD_MEGA_ROCK, ACTION_FALLING, -1
6319 EL_BD_DIAMOND, -1, -1
6323 EL_BD_DIAMOND_FALLING, -1, -1
6327 EL_BD_DIAMOND, ACTION_FALLING, -1
6330 O_FLYING_DIAMOND, TRUE,
6331 EL_BD_FLYING_DIAMOND, -1, -1
6334 O_FLYING_DIAMOND_F, TRUE,
6335 EL_BD_FLYING_DIAMOND_FLYING, -1, -1
6338 O_FLYING_DIAMOND_F, FALSE,
6339 EL_BD_FLYING_DIAMOND, ACTION_FLYING, -1
6347 EL_BD_NUT_FALLING, -1, -1
6351 EL_BD_NUT, ACTION_FALLING, -1
6354 O_BLADDER_SPENDER, TRUE,
6355 EL_BD_BLADDER_SPENDER, -1, -1
6362 O_H_EXPANDING_WALL, TRUE,
6363 EL_BD_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6366 O_V_EXPANDING_WALL, TRUE,
6367 EL_BD_EXPANDABLE_WALL_VERTICAL, -1, -1
6370 O_EXPANDING_WALL, TRUE,
6371 EL_BD_EXPANDABLE_WALL_ANY, -1, -1
6374 O_H_EXPANDING_STEEL_WALL, TRUE,
6375 EL_BD_EXPANDABLE_STEELWALL_HORIZONTAL, -1, -1
6378 O_V_EXPANDING_STEEL_WALL, TRUE,
6379 EL_BD_EXPANDABLE_STEELWALL_VERTICAL, -1, -1
6382 O_EXPANDING_STEEL_WALL, TRUE,
6383 EL_BD_EXPANDABLE_STEELWALL_ANY, -1, -1
6386 O_EXPANDING_WALL_SWITCH, TRUE,
6387 EL_BD_EXPANDABLE_WALL_SWITCH, -1, -1
6390 O_CREATURE_SWITCH, TRUE,
6391 EL_BD_CREATURE_SWITCH, -1, -1
6394 O_BITER_SWITCH, TRUE,
6395 EL_BD_BITER_SWITCH_1, -1, -1
6398 O_REPLICATOR_SWITCH, TRUE,
6399 EL_BD_REPLICATOR_SWITCH, -1, -1
6402 O_CONVEYOR_SWITCH, TRUE,
6403 EL_BD_CONVEYOR_SWITCH, -1, -1
6406 O_CONVEYOR_DIR_SWITCH, TRUE,
6407 EL_BD_CONVEYOR_DIR_SWITCH, -1, -1
6414 O_FALLING_WALL, TRUE,
6415 EL_BD_FALLING_WALL, -1, -1
6418 O_FALLING_WALL_F, TRUE,
6419 EL_BD_FALLING_WALL_FALLING, -1, -1
6422 O_FALLING_WALL_F, FALSE,
6423 EL_BD_FALLING_WALL, ACTION_FALLING, -1
6430 O_TIME_PENALTY, TRUE,
6431 EL_BD_TIME_PENALTY, -1, -1
6435 EL_BD_GRAVESTONE, -1, -1
6438 O_STONE_GLUED, TRUE,
6439 EL_BD_ROCK_GLUED, -1, -1
6442 O_DIAMOND_GLUED, TRUE,
6443 EL_BD_DIAMOND_GLUED, -1, -1
6446 O_DIAMOND_KEY, TRUE,
6447 EL_BD_DIAMOND_KEY, -1, -1
6450 O_TRAPPED_DIAMOND, TRUE,
6451 EL_BD_TRAPPED_DIAMOND, -1, -1
6459 EL_BD_SAND_GLUED, -1, -1
6475 EL_BD_GATE_1, -1, -1
6479 EL_BD_GATE_2, -1, -1
6483 EL_BD_GATE_3, -1, -1
6490 O_GRAVITY_SWITCH, TRUE,
6491 EL_BD_GRAVITY_SWITCH, -1, -1
6494 O_PNEUMATIC_HAMMER, TRUE,
6495 EL_BD_PNEUMATIC_HAMMER, -1, -1
6499 EL_BD_TELEPORTER, -1, -1
6503 EL_BD_SKELETON, -1, -1
6511 EL_BD_WATER_1, -1, -1
6519 EL_BD_WATER_2, -1, -1
6527 EL_BD_WATER_3, -1, -1
6535 EL_BD_WATER_4, -1, -1
6543 EL_BD_WATER_5, -1, -1
6551 EL_BD_WATER_6, -1, -1
6559 EL_BD_WATER_7, -1, -1
6567 EL_BD_WATER_8, -1, -1
6575 EL_BD_WATER_9, -1, -1
6583 EL_BD_WATER_10, -1, -1
6591 EL_BD_WATER_11, -1, -1
6599 EL_BD_WATER_12, -1, -1
6607 EL_BD_WATER_13, -1, -1
6615 EL_BD_WATER_14, -1, -1
6623 EL_BD_WATER_15, -1, -1
6631 EL_BD_WATER_16, -1, -1
6639 EL_BD_COW_LEFT, -1, -1
6643 EL_BD_COW_UP, -1, -1
6647 EL_BD_COW_RIGHT, -1, -1
6651 EL_BD_COW_DOWN, -1, -1
6654 O_COW_ENCLOSED_1, TRUE,
6655 EL_BD_COW_ENCLOSED_1, -1, -1
6658 O_COW_ENCLOSED_1, FALSE,
6659 EL_BD_COW_DOWN, -1, -1
6662 O_COW_ENCLOSED_2, TRUE,
6663 EL_BD_COW_ENCLOSED_2, -1, -1
6666 O_COW_ENCLOSED_2, FALSE,
6667 EL_BD_COW_DOWN, -1, -1
6670 O_COW_ENCLOSED_3, TRUE,
6671 EL_BD_COW_ENCLOSED_3, -1, -1
6674 O_COW_ENCLOSED_3, FALSE,
6675 EL_BD_COW_DOWN, -1, -1
6678 O_COW_ENCLOSED_4, TRUE,
6679 EL_BD_COW_ENCLOSED_4, -1, -1
6682 O_COW_ENCLOSED_4, FALSE,
6683 EL_BD_COW_DOWN, -1, -1
6686 O_COW_ENCLOSED_5, TRUE,
6687 EL_BD_COW_ENCLOSED_5, -1, -1
6690 O_COW_ENCLOSED_5, FALSE,
6691 EL_BD_COW_DOWN, -1, -1
6694 O_COW_ENCLOSED_6, TRUE,
6695 EL_BD_COW_ENCLOSED_6, -1, -1
6698 O_COW_ENCLOSED_6, FALSE,
6699 EL_BD_COW_DOWN, -1, -1
6702 O_COW_ENCLOSED_7, TRUE,
6703 EL_BD_COW_ENCLOSED_7, -1, -1
6706 O_COW_ENCLOSED_7, FALSE,
6707 EL_BD_COW_DOWN, -1, -1
6710 O_WALLED_DIAMOND, TRUE,
6711 EL_BD_WALL_DIAMOND, -1, -1
6714 O_WALLED_KEY_1, TRUE,
6715 EL_BD_WALL_KEY_1, -1, -1
6718 O_WALLED_KEY_2, TRUE,
6719 EL_BD_WALL_KEY_2, -1, -1
6722 O_WALLED_KEY_3, TRUE,
6723 EL_BD_WALL_KEY_3, -1, -1
6727 EL_BD_AMOEBA, -1, -1
6731 EL_BD_AMOEBA_2, -1, -1
6735 EL_BD_REPLICATOR, -1, -1
6738 O_CONVEYOR_LEFT, TRUE,
6739 EL_BD_CONVEYOR_LEFT, -1, -1
6742 O_CONVEYOR_RIGHT, TRUE,
6743 EL_BD_CONVEYOR_RIGHT, -1, -1
6755 EL_BD_VOODOO_DOLL, -1, -1
6763 EL_BD_BLADDER, -1, -1
6767 EL_BD_BLADDER_1, -1, -1
6771 EL_BD_BLADDER, -1, -1
6775 EL_BD_BLADDER_2, -1, -1
6779 EL_BD_BLADDER, -1, -1
6783 EL_BD_BLADDER_3, -1, -1
6787 EL_BD_BLADDER, -1, -1
6791 EL_BD_BLADDER_4, -1, -1
6795 EL_BD_BLADDER, -1, -1
6799 EL_BD_BLADDER_5, -1, -1
6803 EL_BD_BLADDER, -1, -1
6807 EL_BD_BLADDER_6, -1, -1
6811 EL_BD_BLADDER, -1, -1
6815 EL_BD_BLADDER_7, -1, -1
6819 EL_BD_BLADDER, -1, -1
6823 EL_BD_BLADDER_8, -1, -1
6827 EL_BD_BLADDER, -1, -1
6830 O_WAITING_STONE, TRUE,
6831 EL_BD_WAITING_ROCK, -1, -1
6834 O_CHASING_STONE, TRUE,
6835 EL_BD_CHASING_ROCK, -1, -1
6843 EL_BD_FIREFLY_LEFT, -1, -1
6847 EL_BD_FIREFLY_UP, -1, -1
6851 EL_BD_FIREFLY_RIGHT, -1, -1
6855 EL_BD_FIREFLY_DOWN, -1, -1
6858 O_ALT_FIREFLY_1, TRUE,
6859 EL_BD_FIREFLY_2_LEFT, -1, -1
6862 O_ALT_FIREFLY_2, TRUE,
6863 EL_BD_FIREFLY_2_UP, -1, -1
6866 O_ALT_FIREFLY_3, TRUE,
6867 EL_BD_FIREFLY_2_RIGHT, -1, -1
6870 O_ALT_FIREFLY_4, TRUE,
6871 EL_BD_FIREFLY_2_DOWN, -1, -1
6875 EL_BD_BUTTERFLY_LEFT, -1, -1
6879 EL_BD_BUTTERFLY_UP, -1, -1
6883 EL_BD_BUTTERFLY_RIGHT, -1, -1
6887 EL_BD_BUTTERFLY_DOWN, -1, -1
6890 O_ALT_BUTTER_1, TRUE,
6891 EL_BD_BUTTERFLY_2_LEFT, -1, -1
6894 O_ALT_BUTTER_2, TRUE,
6895 EL_BD_BUTTERFLY_2_UP, -1, -1
6898 O_ALT_BUTTER_3, TRUE,
6899 EL_BD_BUTTERFLY_2_RIGHT, -1, -1
6902 O_ALT_BUTTER_4, TRUE,
6903 EL_BD_BUTTERFLY_2_DOWN, -1, -1
6907 EL_BD_STONEFLY_LEFT, -1, -1
6911 EL_BD_STONEFLY_UP, -1, -1
6915 EL_BD_STONEFLY_RIGHT, -1, -1
6919 EL_BD_STONEFLY_DOWN, -1, -1
6923 EL_BD_BITER_UP, -1, -1
6927 EL_BD_BITER_RIGHT, -1, -1
6931 EL_BD_BITER_DOWN, -1, -1
6935 EL_BD_BITER_LEFT, -1, -1
6938 O_DRAGONFLY_1, TRUE,
6939 EL_BD_DRAGONFLY_LEFT, -1, -1
6942 O_DRAGONFLY_2, TRUE,
6943 EL_BD_DRAGONFLY_UP, -1, -1
6946 O_DRAGONFLY_3, TRUE,
6947 EL_BD_DRAGONFLY_RIGHT, -1, -1
6950 O_DRAGONFLY_4, TRUE,
6951 EL_BD_DRAGONFLY_DOWN, -1, -1
6955 EL_BD_PLAYER_GROWING_1, -1, -1
6959 EL_BD_PLAYER, ACTION_GROWING, -1
6963 EL_BD_PLAYER_GROWING_2, -1, -1
6967 EL_BD_PLAYER, ACTION_GROWING, -1
6971 EL_BD_PLAYER_GROWING_3, -1, -1
6975 EL_BD_PLAYER, ACTION_GROWING, -1
6979 EL_BD_PLAYER, -1, -1
6982 O_PLAYER_BOMB, TRUE,
6983 EL_BD_PLAYER_WITH_BOMB, -1, -1
6986 O_PLAYER_ROCKET_LAUNCHER, TRUE,
6987 EL_BD_PLAYER_WITH_ROCKET_LAUNCHER, -1, -1
6990 O_PLAYER_GLUED, TRUE,
6991 EL_BD_PLAYER_GLUED, -1, -1
6994 O_PLAYER_STIRRING, TRUE,
6995 EL_BD_PLAYER_STIRRING, -1, -1
6998 O_ROCKET_LAUNCHER, TRUE,
6999 EL_BD_ROCKET_LAUNCHER, -1, -1
7003 EL_BD_ROCKET_RIGHT, -1, -1
7007 EL_BD_ROCKET_UP, -1, -1
7011 EL_BD_ROCKET_LEFT, -1, -1
7015 EL_BD_ROCKET_DOWN, -1, -1
7022 O_BOMB_TICK_1, TRUE,
7023 EL_BD_BOMB_TICKING_1, -1, -1
7026 O_BOMB_TICK_1, FALSE,
7027 EL_BD_BOMB, ACTION_ACTIVE, -1
7030 O_BOMB_TICK_2, TRUE,
7031 EL_BD_BOMB_TICKING_2, -1, -1
7034 O_BOMB_TICK_2, FALSE,
7035 EL_BD_BOMB, ACTION_ACTIVE, -1
7038 O_BOMB_TICK_3, TRUE,
7039 EL_BD_BOMB_TICKING_3, -1, -1
7042 O_BOMB_TICK_3, FALSE,
7043 EL_BD_BOMB, ACTION_ACTIVE, -1
7046 O_BOMB_TICK_4, TRUE,
7047 EL_BD_BOMB_TICKING_4, -1, -1
7050 O_BOMB_TICK_4, FALSE,
7051 EL_BD_BOMB, ACTION_ACTIVE, -1
7054 O_BOMB_TICK_5, TRUE,
7055 EL_BD_BOMB_TICKING_5, -1, -1
7058 O_BOMB_TICK_5, FALSE,
7059 EL_BD_BOMB, ACTION_ACTIVE, -1
7062 O_BOMB_TICK_6, TRUE,
7063 EL_BD_BOMB_TICKING_6, -1, -1
7066 O_BOMB_TICK_6, FALSE,
7067 EL_BD_BOMB, ACTION_ACTIVE, -1
7070 O_BOMB_TICK_7, TRUE,
7071 EL_BD_BOMB_TICKING_7, -1, -1
7074 O_BOMB_TICK_7, FALSE,
7075 EL_BD_BOMB, ACTION_ACTIVE, -1
7079 EL_BD_NITRO_PACK, -1, -1
7082 O_NITRO_PACK_F, TRUE,
7083 EL_BD_NITRO_PACK_FALLING, -1, -1
7086 O_NITRO_PACK_F, FALSE,
7087 EL_BD_NITRO_PACK, ACTION_FALLING, -1
7090 O_PRE_CLOCK_1, TRUE,
7091 EL_BD_CLOCK_GROWING_1, -1, -1
7094 O_PRE_CLOCK_1, FALSE,
7095 EL_BD_CLOCK, ACTION_GROWING, -1
7098 O_PRE_CLOCK_2, TRUE,
7099 EL_BD_CLOCK_GROWING_2, -1, -1
7102 O_PRE_CLOCK_2, FALSE,
7103 EL_BD_CLOCK, ACTION_GROWING, -1
7106 O_PRE_CLOCK_3, TRUE,
7107 EL_BD_CLOCK_GROWING_3, -1, -1
7110 O_PRE_CLOCK_3, FALSE,
7111 EL_BD_CLOCK, ACTION_GROWING, -1
7114 O_PRE_CLOCK_4, TRUE,
7115 EL_BD_CLOCK_GROWING_4, -1, -1
7118 O_PRE_CLOCK_4, FALSE,
7119 EL_BD_CLOCK, ACTION_GROWING, -1
7123 EL_BD_DIAMOND_GROWING_1, -1, -1
7127 EL_BD_DIAMOND, ACTION_GROWING, -1
7131 EL_BD_DIAMOND_GROWING_2, -1, -1
7135 EL_BD_DIAMOND, ACTION_GROWING, -1
7139 EL_BD_DIAMOND_GROWING_3, -1, -1
7143 EL_BD_DIAMOND, ACTION_GROWING, -1
7147 EL_BD_DIAMOND_GROWING_4, -1, -1
7151 EL_BD_DIAMOND, ACTION_GROWING, -1
7155 EL_BD_DIAMOND_GROWING_5, -1, -1
7159 EL_BD_DIAMOND, ACTION_GROWING, -1
7163 EL_BD_EXPLODING_1, -1, -1
7167 EL_DEFAULT, ACTION_EXPLODING, -1
7171 EL_BD_EXPLODING_2, -1, -1
7175 EL_DEFAULT, ACTION_EXPLODING, -1
7179 EL_BD_EXPLODING_3, -1, -1
7183 EL_DEFAULT, ACTION_EXPLODING, -1
7187 EL_BD_EXPLODING_4, -1, -1
7191 EL_DEFAULT, ACTION_EXPLODING, -1
7195 EL_BD_EXPLODING_5, -1, -1
7199 EL_DEFAULT, ACTION_EXPLODING, -1
7202 O_PRE_STONE_1, TRUE,
7203 EL_BD_ROCK_GROWING_1, -1, -1
7206 O_PRE_STONE_1, FALSE,
7207 EL_BD_ROCK, ACTION_GROWING, -1
7210 O_PRE_STONE_2, TRUE,
7211 EL_BD_ROCK_GROWING_2, -1, -1
7214 O_PRE_STONE_2, FALSE,
7215 EL_BD_ROCK, ACTION_GROWING, -1
7218 O_PRE_STONE_3, TRUE,
7219 EL_BD_ROCK_GROWING_3, -1, -1
7222 O_PRE_STONE_3, FALSE,
7223 EL_BD_ROCK, ACTION_GROWING, -1
7226 O_PRE_STONE_4, TRUE,
7227 EL_BD_ROCK_GROWING_4, -1, -1
7230 O_PRE_STONE_4, FALSE,
7231 EL_BD_ROCK, ACTION_GROWING, -1
7234 O_PRE_STEEL_1, TRUE,
7235 EL_BD_STEELWALL_GROWING_1, -1, -1
7238 O_PRE_STEEL_1, FALSE,
7239 EL_BD_STEELWALL, ACTION_GROWING, -1
7242 O_PRE_STEEL_2, TRUE,
7243 EL_BD_STEELWALL_GROWING_2, -1, -1
7246 O_PRE_STEEL_2, FALSE,
7247 EL_BD_STEELWALL, ACTION_GROWING, -1
7250 O_PRE_STEEL_3, TRUE,
7251 EL_BD_STEELWALL_GROWING_3, -1, -1
7254 O_PRE_STEEL_3, FALSE,
7255 EL_BD_STEELWALL, ACTION_GROWING, -1
7258 O_PRE_STEEL_4, TRUE,
7259 EL_BD_STEELWALL_GROWING_4, -1, -1
7262 O_PRE_STEEL_4, FALSE,
7263 EL_BD_STEELWALL, ACTION_GROWING, -1
7266 O_GHOST_EXPL_1, TRUE,
7267 EL_BD_GHOST_EXPLODING_1, -1, -1
7270 O_GHOST_EXPL_1, FALSE,
7271 EL_BD_GHOST, ACTION_EXPLODING, -1
7274 O_GHOST_EXPL_2, TRUE,
7275 EL_BD_GHOST_EXPLODING_2, -1, -1
7278 O_GHOST_EXPL_2, FALSE,
7279 EL_BD_GHOST, ACTION_EXPLODING, -1
7282 O_GHOST_EXPL_3, TRUE,
7283 EL_BD_GHOST_EXPLODING_3, -1, -1
7286 O_GHOST_EXPL_3, FALSE,
7287 EL_BD_GHOST, ACTION_EXPLODING, -1
7290 O_GHOST_EXPL_4, TRUE,
7291 EL_BD_GHOST_EXPLODING_4, -1, -1
7294 O_GHOST_EXPL_4, FALSE,
7295 EL_BD_GHOST, ACTION_EXPLODING, -1
7298 O_BOMB_EXPL_1, TRUE,
7299 EL_BD_BOMB_EXPLODING_1, -1, -1
7302 O_BOMB_EXPL_1, FALSE,
7303 EL_BD_BOMB, ACTION_EXPLODING, -1
7306 O_BOMB_EXPL_2, TRUE,
7307 EL_BD_BOMB_EXPLODING_2, -1, -1
7310 O_BOMB_EXPL_2, FALSE,
7311 EL_BD_BOMB, ACTION_EXPLODING, -1
7314 O_BOMB_EXPL_3, TRUE,
7315 EL_BD_BOMB_EXPLODING_3, -1, -1
7318 O_BOMB_EXPL_3, FALSE,
7319 EL_BD_BOMB, ACTION_EXPLODING, -1
7322 O_BOMB_EXPL_4, TRUE,
7323 EL_BD_BOMB_EXPLODING_4, -1, -1
7326 O_BOMB_EXPL_4, FALSE,
7327 EL_BD_BOMB, ACTION_EXPLODING, -1
7330 O_NITRO_EXPL_1, TRUE,
7331 EL_BD_NITRO_PACK_EXPLODING_1, -1, -1
7334 O_NITRO_EXPL_1, FALSE,
7335 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
7338 O_NITRO_EXPL_2, TRUE,
7339 EL_BD_NITRO_PACK_EXPLODING_2, -1, -1
7342 O_NITRO_EXPL_2, FALSE,
7343 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
7346 O_NITRO_EXPL_3, TRUE,
7347 EL_BD_NITRO_PACK_EXPLODING_3, -1, -1
7350 O_NITRO_EXPL_3, FALSE,
7351 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
7354 O_NITRO_EXPL_4, TRUE,
7355 EL_BD_NITRO_PACK_EXPLODING_4, -1, -1
7358 O_NITRO_EXPL_4, FALSE,
7359 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
7362 O_NITRO_PACK_EXPLODE, TRUE,
7363 EL_BD_NITRO_PACK_EXPLODING, -1, -1
7366 O_NITRO_PACK_EXPLODE, FALSE,
7367 EL_BD_NITRO_PACK, ACTION_EXPLODING, -1
7370 O_AMOEBA_2_EXPL_1, TRUE,
7371 EL_BD_AMOEBA_2_EXPLODING_1, -1, -1
7374 O_AMOEBA_2_EXPL_1, FALSE,
7375 EL_BD_AMOEBA_2, ACTION_EXPLODING, -1
7378 O_AMOEBA_2_EXPL_2, TRUE,
7379 EL_BD_AMOEBA_2_EXPLODING_2, -1, -1
7382 O_AMOEBA_2_EXPL_2, FALSE,
7383 EL_BD_AMOEBA_2, ACTION_EXPLODING, -1
7386 O_AMOEBA_2_EXPL_3, TRUE,
7387 EL_BD_AMOEBA_2_EXPLODING_3, -1, -1
7390 O_AMOEBA_2_EXPL_3, FALSE,
7391 EL_BD_AMOEBA_2, ACTION_EXPLODING, -1
7394 O_AMOEBA_2_EXPL_4, TRUE,
7395 EL_BD_AMOEBA_2_EXPLODING_4, -1, -1
7398 O_AMOEBA_2_EXPL_4, FALSE,
7399 EL_BD_AMOEBA_2, ACTION_EXPLODING, -1
7403 EL_BD_NUT_BREAKING_1, -1, -1
7406 O_NUT_EXPL_1, FALSE,
7407 EL_BD_NUT, ACTION_BREAKING, -1
7411 EL_BD_NUT_BREAKING_2, -1, -1
7414 O_NUT_EXPL_2, FALSE,
7415 EL_BD_NUT, ACTION_BREAKING, -1
7419 EL_BD_NUT_BREAKING_3, -1, -1
7422 O_NUT_EXPL_3, FALSE,
7423 EL_BD_NUT, ACTION_BREAKING, -1
7427 EL_BD_NUT_BREAKING_4, -1, -1
7430 O_NUT_EXPL_4, FALSE,
7431 EL_BD_NUT, ACTION_BREAKING, -1
7434 O_PLAYER_PNEUMATIC_LEFT, FALSE,
7435 EL_BD_PLAYER, ACTION_HITTING, MV_BIT_LEFT
7438 O_PLAYER_PNEUMATIC_RIGHT, FALSE,
7439 EL_BD_PLAYER, ACTION_HITTING, MV_BIT_RIGHT
7442 O_PNEUMATIC_ACTIVE_LEFT, FALSE,
7443 EL_BD_PNEUMATIC_HAMMER, ACTION_HITTING, MV_BIT_LEFT
7446 O_PNEUMATIC_ACTIVE_RIGHT, FALSE,
7447 EL_BD_PNEUMATIC_HAMMER, ACTION_HITTING, MV_BIT_RIGHT
7450 // helper (runtime) elements
7453 O_FAKE_BONUS, FALSE,
7454 EL_BD_FAKE_BONUS, -1, -1
7457 O_INBOX_CLOSED, FALSE,
7461 O_INBOX_OPEN, FALSE,
7462 EL_BD_INBOX, ACTION_OPENING, -1
7465 O_OUTBOX_CLOSED, FALSE,
7466 EL_BD_EXIT_CLOSED, -1, -1
7469 O_OUTBOX_OPEN, FALSE,
7470 EL_BD_EXIT_OPEN, -1, -1
7474 EL_BD_COVERED, -1, -1
7477 O_PLAYER_LEFT, FALSE,
7478 EL_BD_PLAYER, ACTION_MOVING, MV_BIT_LEFT
7481 O_PLAYER_RIGHT, FALSE,
7482 EL_BD_PLAYER, ACTION_MOVING, MV_BIT_RIGHT
7486 EL_BD_PLAYER, ACTION_MOVING, MV_BIT_UP
7489 O_PLAYER_DOWN, FALSE,
7490 EL_BD_PLAYER, ACTION_MOVING, MV_BIT_DOWN
7493 O_PLAYER_BLINK, FALSE,
7494 EL_BD_PLAYER, ACTION_BORING_1, -1
7497 O_PLAYER_TAP, FALSE,
7498 EL_BD_PLAYER, ACTION_BORING_2, -1
7501 O_PLAYER_TAP_BLINK, FALSE,
7502 EL_BD_PLAYER, ACTION_BORING_3, -1
7505 O_PLAYER_PUSH_LEFT, FALSE,
7506 EL_BD_PLAYER, ACTION_PUSHING, MV_BIT_LEFT
7509 O_PLAYER_PUSH_RIGHT, FALSE,
7510 EL_BD_PLAYER, ACTION_PUSHING, MV_BIT_RIGHT
7513 O_CREATURE_SWITCH_ON, FALSE,
7514 EL_BD_CREATURE_SWITCH_ACTIVE, -1, -1
7517 O_EXPANDING_WALL_SWITCH_HORIZ, FALSE,
7518 EL_BD_EXPANDABLE_WALL_SWITCH, -1, -1
7521 O_EXPANDING_WALL_SWITCH_VERT, FALSE,
7522 EL_BD_EXPANDABLE_WALL_SWITCH_ACTIVE, -1, -1
7525 O_GRAVITY_SWITCH_ACTIVE, FALSE,
7526 EL_BD_GRAVITY_SWITCH_ACTIVE, -1, -1
7529 O_REPLICATOR_SWITCH_OFF, FALSE,
7530 EL_BD_REPLICATOR_SWITCH, -1, -1
7533 O_REPLICATOR_SWITCH_ON, FALSE,
7534 EL_BD_REPLICATOR_SWITCH_ACTIVE, -1, -1
7537 O_CONVEYOR_DIR_NORMAL, FALSE,
7538 EL_BD_CONVEYOR_DIR_SWITCH, -1, -1
7541 O_CONVEYOR_DIR_CHANGED, FALSE,
7542 EL_BD_CONVEYOR_DIR_SWITCH_ACTIVE, -1, -1
7545 O_CONVEYOR_SWITCH_OFF, FALSE,
7546 EL_BD_CONVEYOR_SWITCH, -1, -1
7549 O_CONVEYOR_SWITCH_ON, FALSE,
7550 EL_BD_CONVEYOR_SWITCH_ACTIVE, -1, -1
7553 O_MAGIC_WALL_ACTIVE, FALSE,
7554 EL_BD_MAGIC_WALL_ACTIVE, -1, -1
7557 O_REPLICATOR_ACTIVE, FALSE,
7558 EL_BD_REPLICATOR_ACTIVE, -1, -1
7561 O_CONVEYOR_LEFT_ACTIVE, FALSE,
7562 EL_BD_CONVEYOR_LEFT_ACTIVE, -1, -1
7565 O_CONVEYOR_RIGHT_ACTIVE, FALSE,
7566 EL_BD_CONVEYOR_RIGHT_ACTIVE, -1, -1
7569 O_BITER_SWITCH_1, FALSE,
7570 EL_BD_BITER_SWITCH_1, -1, -1
7573 O_BITER_SWITCH_2, FALSE,
7574 EL_BD_BITER_SWITCH_2, -1, -1
7577 O_BITER_SWITCH_3, FALSE,
7578 EL_BD_BITER_SWITCH_3, -1, -1
7581 O_BITER_SWITCH_4, FALSE,
7582 EL_BD_BITER_SWITCH_4, -1, -1
7591 int map_element_RND_to_BD_cave(int element_rnd)
7593 static unsigned short mapping_RND_to_BD[NUM_FILE_ELEMENTS];
7594 static boolean mapping_initialized = FALSE;
7596 if (!mapping_initialized)
7600 // return "O_UNKNOWN" for all undefined elements in mapping array
7601 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7602 mapping_RND_to_BD[i] = O_UNKNOWN;
7604 for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
7605 if (bd_object_mapping_list[i].is_rnd_to_bd_mapping)
7606 mapping_RND_to_BD[bd_object_mapping_list[i].element_rnd] =
7607 bd_object_mapping_list[i].element_bd;
7609 mapping_initialized = TRUE;
7612 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7614 Warn("invalid RND element %d", element_rnd);
7619 return mapping_RND_to_BD[element_rnd];
7622 int map_element_RND_to_BD_effect(int element_rnd, int action)
7624 static unsigned short mapping_RND_to_BD[NUM_FILE_ELEMENTS][NUM_ACTIONS];
7625 static boolean mapping_initialized = FALSE;
7627 if (!mapping_initialized)
7631 // return "O_UNKNOWN" for all undefined elements in mapping array
7632 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7633 for (j = 0; j < NUM_ACTIONS; j++)
7634 mapping_RND_to_BD[i][j] = O_UNKNOWN;
7636 for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
7638 int element_rnd = bd_object_mapping_list[i].element_rnd;
7639 int element_bd = bd_object_mapping_list[i].element_bd;
7640 int action = bd_object_mapping_list[i].action;
7643 mapping_RND_to_BD[element_rnd][action] = element_bd;
7646 mapping_initialized = TRUE;
7649 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7651 Warn("invalid RND element %d", element_rnd);
7656 if (action < 0 || action >= NUM_ACTIONS)
7658 Warn("invalid action %d", action);
7663 return mapping_RND_to_BD[element_rnd][action];
7666 int map_element_BD_to_RND_cave(int element_bd)
7668 static unsigned short mapping_BD_to_RND[O_MAX_ALL];
7669 static boolean mapping_initialized = FALSE;
7671 if (!mapping_initialized)
7675 // return "EL_UNKNOWN" for all undefined elements in mapping array
7676 for (i = 0; i < O_MAX_ALL; i++)
7677 mapping_BD_to_RND[i] = EL_UNKNOWN;
7679 for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
7680 if (bd_object_mapping_list[i].is_rnd_to_bd_mapping)
7681 mapping_BD_to_RND[bd_object_mapping_list[i].element_bd] =
7682 bd_object_mapping_list[i].element_rnd;
7684 mapping_initialized = TRUE;
7687 if (element_bd < 0 || element_bd >= O_MAX_ALL)
7689 Warn("invalid BD element %d", element_bd);
7694 return mapping_BD_to_RND[element_bd];
7697 int map_element_BD_to_RND_game(int element_bd)
7699 static unsigned short mapping_BD_to_RND[O_MAX_ALL];
7700 static boolean mapping_initialized = FALSE;
7702 if (!mapping_initialized)
7706 // return "EL_UNKNOWN" for all undefined elements in mapping array
7707 for (i = 0; i < O_MAX_ALL; i++)
7708 mapping_BD_to_RND[i] = EL_UNKNOWN;
7710 for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
7711 mapping_BD_to_RND[bd_object_mapping_list[i].element_bd] =
7712 bd_object_mapping_list[i].element_rnd;
7714 mapping_initialized = TRUE;
7717 if (element_bd < 0 || element_bd >= O_MAX_ALL)
7719 Warn("invalid BD element %d", element_bd);
7724 return mapping_BD_to_RND[element_bd];
7727 static struct Mapping_EM_to_RND_object
7730 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
7731 boolean is_backside; // backside of moving element
7737 em_object_mapping_list[GAME_TILE_MAX + 1] =
7740 Zborder, FALSE, FALSE,
7744 Zplayer, FALSE, FALSE,
7753 Ztank, FALSE, FALSE,
7757 Zeater, FALSE, FALSE,
7761 Zdynamite, FALSE, FALSE,
7765 Zboom, FALSE, FALSE,
7770 Xchain, FALSE, FALSE,
7771 EL_DEFAULT, ACTION_EXPLODING, -1
7774 Xboom_bug, FALSE, FALSE,
7775 EL_BUG, ACTION_EXPLODING, -1
7778 Xboom_tank, FALSE, FALSE,
7779 EL_SPACESHIP, ACTION_EXPLODING, -1
7782 Xboom_android, FALSE, FALSE,
7783 EL_EMC_ANDROID, ACTION_OTHER, -1
7786 Xboom_1, FALSE, FALSE,
7787 EL_DEFAULT, ACTION_EXPLODING, -1
7790 Xboom_2, FALSE, FALSE,
7791 EL_DEFAULT, ACTION_EXPLODING, -1
7795 Xblank, TRUE, FALSE,
7800 Xsplash_e, FALSE, FALSE,
7801 EL_ACID_SPLASH_RIGHT, -1, -1
7804 Xsplash_w, FALSE, FALSE,
7805 EL_ACID_SPLASH_LEFT, -1, -1
7809 Xplant, TRUE, FALSE,
7810 EL_EMC_PLANT, -1, -1
7813 Yplant, FALSE, FALSE,
7814 EL_EMC_PLANT, -1, -1
7818 Xacid_1, TRUE, FALSE,
7822 Xacid_2, FALSE, FALSE,
7826 Xacid_3, FALSE, FALSE,
7830 Xacid_4, FALSE, FALSE,
7834 Xacid_5, FALSE, FALSE,
7838 Xacid_6, FALSE, FALSE,
7842 Xacid_7, FALSE, FALSE,
7846 Xacid_8, FALSE, FALSE,
7851 Xfake_acid_1, TRUE, FALSE,
7852 EL_EMC_FAKE_ACID, -1, -1
7855 Xfake_acid_2, FALSE, FALSE,
7856 EL_EMC_FAKE_ACID, -1, -1
7859 Xfake_acid_3, FALSE, FALSE,
7860 EL_EMC_FAKE_ACID, -1, -1
7863 Xfake_acid_4, FALSE, FALSE,
7864 EL_EMC_FAKE_ACID, -1, -1
7867 Xfake_acid_5, FALSE, FALSE,
7868 EL_EMC_FAKE_ACID, -1, -1
7871 Xfake_acid_6, FALSE, FALSE,
7872 EL_EMC_FAKE_ACID, -1, -1
7875 Xfake_acid_7, FALSE, FALSE,
7876 EL_EMC_FAKE_ACID, -1, -1
7879 Xfake_acid_8, FALSE, FALSE,
7880 EL_EMC_FAKE_ACID, -1, -1
7884 Xfake_acid_1_player, FALSE, FALSE,
7885 EL_EMC_FAKE_ACID, -1, -1
7888 Xfake_acid_2_player, FALSE, FALSE,
7889 EL_EMC_FAKE_ACID, -1, -1
7892 Xfake_acid_3_player, FALSE, FALSE,
7893 EL_EMC_FAKE_ACID, -1, -1
7896 Xfake_acid_4_player, FALSE, FALSE,
7897 EL_EMC_FAKE_ACID, -1, -1
7900 Xfake_acid_5_player, FALSE, FALSE,
7901 EL_EMC_FAKE_ACID, -1, -1
7904 Xfake_acid_6_player, FALSE, FALSE,
7905 EL_EMC_FAKE_ACID, -1, -1
7908 Xfake_acid_7_player, FALSE, FALSE,
7909 EL_EMC_FAKE_ACID, -1, -1
7912 Xfake_acid_8_player, FALSE, FALSE,
7913 EL_EMC_FAKE_ACID, -1, -1
7917 Xgrass, TRUE, FALSE,
7918 EL_EMC_GRASS, -1, -1
7921 Ygrass_nB, FALSE, FALSE,
7922 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
7925 Ygrass_eB, FALSE, FALSE,
7926 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
7929 Ygrass_sB, FALSE, FALSE,
7930 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
7933 Ygrass_wB, FALSE, FALSE,
7934 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
7942 Ydirt_nB, FALSE, FALSE,
7943 EL_SAND, ACTION_DIGGING, MV_BIT_UP
7946 Ydirt_eB, FALSE, FALSE,
7947 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
7950 Ydirt_sB, FALSE, FALSE,
7951 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
7954 Ydirt_wB, FALSE, FALSE,
7955 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
7959 Xandroid, TRUE, FALSE,
7960 EL_EMC_ANDROID, ACTION_ACTIVE, -1
7963 Xandroid_1_n, FALSE, FALSE,
7964 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
7967 Xandroid_2_n, FALSE, FALSE,
7968 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
7971 Xandroid_1_e, FALSE, FALSE,
7972 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
7975 Xandroid_2_e, FALSE, FALSE,
7976 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
7979 Xandroid_1_w, FALSE, FALSE,
7980 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
7983 Xandroid_2_w, FALSE, FALSE,
7984 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
7987 Xandroid_1_s, FALSE, FALSE,
7988 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
7991 Xandroid_2_s, FALSE, FALSE,
7992 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
7995 Yandroid_n, FALSE, FALSE,
7996 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
7999 Yandroid_nB, FALSE, TRUE,
8000 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
8003 Yandroid_ne, FALSE, FALSE,
8004 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
8007 Yandroid_neB, FALSE, TRUE,
8008 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
8011 Yandroid_e, FALSE, FALSE,
8012 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
8015 Yandroid_eB, FALSE, TRUE,
8016 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
8019 Yandroid_se, FALSE, FALSE,
8020 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
8023 Yandroid_seB, FALSE, TRUE,
8024 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
8027 Yandroid_s, FALSE, FALSE,
8028 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
8031 Yandroid_sB, FALSE, TRUE,
8032 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
8035 Yandroid_sw, FALSE, FALSE,
8036 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
8039 Yandroid_swB, FALSE, TRUE,
8040 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
8043 Yandroid_w, FALSE, FALSE,
8044 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
8047 Yandroid_wB, FALSE, TRUE,
8048 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
8051 Yandroid_nw, FALSE, FALSE,
8052 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
8055 Yandroid_nwB, FALSE, TRUE,
8056 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
8060 Xeater_n, TRUE, FALSE,
8061 EL_YAMYAM_UP, -1, -1
8064 Xeater_e, TRUE, FALSE,
8065 EL_YAMYAM_RIGHT, -1, -1
8068 Xeater_w, TRUE, FALSE,
8069 EL_YAMYAM_LEFT, -1, -1
8072 Xeater_s, TRUE, FALSE,
8073 EL_YAMYAM_DOWN, -1, -1
8076 Yeater_n, FALSE, FALSE,
8077 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
8080 Yeater_nB, FALSE, TRUE,
8081 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
8084 Yeater_e, FALSE, FALSE,
8085 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
8088 Yeater_eB, FALSE, TRUE,
8089 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
8092 Yeater_s, FALSE, FALSE,
8093 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
8096 Yeater_sB, FALSE, TRUE,
8097 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
8100 Yeater_w, FALSE, FALSE,
8101 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
8104 Yeater_wB, FALSE, TRUE,
8105 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
8108 Yeater_stone, FALSE, FALSE,
8109 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
8112 Yeater_spring, FALSE, FALSE,
8113 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
8117 Xalien, TRUE, FALSE,
8121 Xalien_pause, FALSE, FALSE,
8125 Yalien_n, FALSE, FALSE,
8126 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
8129 Yalien_nB, FALSE, TRUE,
8130 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
8133 Yalien_e, FALSE, FALSE,
8134 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
8137 Yalien_eB, FALSE, TRUE,
8138 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
8141 Yalien_s, FALSE, FALSE,
8142 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
8145 Yalien_sB, FALSE, TRUE,
8146 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
8149 Yalien_w, FALSE, FALSE,
8150 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
8153 Yalien_wB, FALSE, TRUE,
8154 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
8157 Yalien_stone, FALSE, FALSE,
8158 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
8161 Yalien_spring, FALSE, FALSE,
8162 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
8166 Xbug_1_n, TRUE, FALSE,
8170 Xbug_1_e, TRUE, FALSE,
8171 EL_BUG_RIGHT, -1, -1
8174 Xbug_1_s, TRUE, FALSE,
8178 Xbug_1_w, TRUE, FALSE,
8182 Xbug_2_n, FALSE, FALSE,
8186 Xbug_2_e, FALSE, FALSE,
8187 EL_BUG_RIGHT, -1, -1
8190 Xbug_2_s, FALSE, FALSE,
8194 Xbug_2_w, FALSE, FALSE,
8198 Ybug_n, FALSE, FALSE,
8199 EL_BUG, ACTION_MOVING, MV_BIT_UP
8202 Ybug_nB, FALSE, TRUE,
8203 EL_BUG, ACTION_MOVING, MV_BIT_UP
8206 Ybug_e, FALSE, FALSE,
8207 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
8210 Ybug_eB, FALSE, TRUE,
8211 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
8214 Ybug_s, FALSE, FALSE,
8215 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
8218 Ybug_sB, FALSE, TRUE,
8219 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
8222 Ybug_w, FALSE, FALSE,
8223 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
8226 Ybug_wB, FALSE, TRUE,
8227 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
8230 Ybug_w_n, FALSE, FALSE,
8231 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
8234 Ybug_n_e, FALSE, FALSE,
8235 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
8238 Ybug_e_s, FALSE, FALSE,
8239 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
8242 Ybug_s_w, FALSE, FALSE,
8243 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
8246 Ybug_e_n, FALSE, FALSE,
8247 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
8250 Ybug_s_e, FALSE, FALSE,
8251 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
8254 Ybug_w_s, FALSE, FALSE,
8255 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
8258 Ybug_n_w, FALSE, FALSE,
8259 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
8262 Ybug_stone, FALSE, FALSE,
8263 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
8266 Ybug_spring, FALSE, FALSE,
8267 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
8271 Xtank_1_n, TRUE, FALSE,
8272 EL_SPACESHIP_UP, -1, -1
8275 Xtank_1_e, TRUE, FALSE,
8276 EL_SPACESHIP_RIGHT, -1, -1
8279 Xtank_1_s, TRUE, FALSE,
8280 EL_SPACESHIP_DOWN, -1, -1
8283 Xtank_1_w, TRUE, FALSE,
8284 EL_SPACESHIP_LEFT, -1, -1
8287 Xtank_2_n, FALSE, FALSE,
8288 EL_SPACESHIP_UP, -1, -1
8291 Xtank_2_e, FALSE, FALSE,
8292 EL_SPACESHIP_RIGHT, -1, -1
8295 Xtank_2_s, FALSE, FALSE,
8296 EL_SPACESHIP_DOWN, -1, -1
8299 Xtank_2_w, FALSE, FALSE,
8300 EL_SPACESHIP_LEFT, -1, -1
8303 Ytank_n, FALSE, FALSE,
8304 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
8307 Ytank_nB, FALSE, TRUE,
8308 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
8311 Ytank_e, FALSE, FALSE,
8312 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
8315 Ytank_eB, FALSE, TRUE,
8316 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
8319 Ytank_s, FALSE, FALSE,
8320 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
8323 Ytank_sB, FALSE, TRUE,
8324 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
8327 Ytank_w, FALSE, FALSE,
8328 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
8331 Ytank_wB, FALSE, TRUE,
8332 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
8335 Ytank_w_n, FALSE, FALSE,
8336 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
8339 Ytank_n_e, FALSE, FALSE,
8340 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
8343 Ytank_e_s, FALSE, FALSE,
8344 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
8347 Ytank_s_w, FALSE, FALSE,
8348 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
8351 Ytank_e_n, FALSE, FALSE,
8352 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
8355 Ytank_s_e, FALSE, FALSE,
8356 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
8359 Ytank_w_s, FALSE, FALSE,
8360 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
8363 Ytank_n_w, FALSE, FALSE,
8364 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
8367 Ytank_stone, FALSE, FALSE,
8368 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
8371 Ytank_spring, FALSE, FALSE,
8372 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
8376 Xemerald, TRUE, FALSE,
8380 Xemerald_pause, FALSE, FALSE,
8384 Xemerald_fall, FALSE, FALSE,
8388 Xemerald_shine, FALSE, FALSE,
8389 EL_EMERALD, ACTION_TWINKLING, -1
8392 Yemerald_s, FALSE, FALSE,
8393 EL_EMERALD, ACTION_FALLING, -1
8396 Yemerald_sB, FALSE, TRUE,
8397 EL_EMERALD, ACTION_FALLING, -1
8400 Yemerald_e, FALSE, FALSE,
8401 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
8404 Yemerald_eB, FALSE, TRUE,
8405 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
8408 Yemerald_w, FALSE, FALSE,
8409 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
8412 Yemerald_wB, FALSE, TRUE,
8413 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
8416 Yemerald_blank, FALSE, FALSE,
8417 EL_EMERALD, ACTION_COLLECTING, -1
8421 Xdiamond, TRUE, FALSE,
8425 Xdiamond_pause, FALSE, FALSE,
8429 Xdiamond_fall, FALSE, FALSE,
8433 Xdiamond_shine, FALSE, FALSE,
8434 EL_DIAMOND, ACTION_TWINKLING, -1
8437 Ydiamond_s, FALSE, FALSE,
8438 EL_DIAMOND, ACTION_FALLING, -1
8441 Ydiamond_sB, FALSE, TRUE,
8442 EL_DIAMOND, ACTION_FALLING, -1
8445 Ydiamond_e, FALSE, FALSE,
8446 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
8449 Ydiamond_eB, FALSE, TRUE,
8450 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
8453 Ydiamond_w, FALSE, FALSE,
8454 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
8457 Ydiamond_wB, FALSE, TRUE,
8458 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
8461 Ydiamond_blank, FALSE, FALSE,
8462 EL_DIAMOND, ACTION_COLLECTING, -1
8465 Ydiamond_stone, FALSE, FALSE,
8466 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
8470 Xstone, TRUE, FALSE,
8474 Xstone_pause, FALSE, FALSE,
8478 Xstone_fall, FALSE, FALSE,
8482 Ystone_s, FALSE, FALSE,
8483 EL_ROCK, ACTION_FALLING, -1
8486 Ystone_sB, FALSE, TRUE,
8487 EL_ROCK, ACTION_FALLING, -1
8490 Ystone_e, FALSE, FALSE,
8491 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
8494 Ystone_eB, FALSE, TRUE,
8495 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
8498 Ystone_w, FALSE, FALSE,
8499 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
8502 Ystone_wB, FALSE, TRUE,
8503 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
8511 Xbomb_pause, FALSE, FALSE,
8515 Xbomb_fall, FALSE, FALSE,
8519 Ybomb_s, FALSE, FALSE,
8520 EL_BOMB, ACTION_FALLING, -1
8523 Ybomb_sB, FALSE, TRUE,
8524 EL_BOMB, ACTION_FALLING, -1
8527 Ybomb_e, FALSE, FALSE,
8528 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
8531 Ybomb_eB, FALSE, TRUE,
8532 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
8535 Ybomb_w, FALSE, FALSE,
8536 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
8539 Ybomb_wB, FALSE, TRUE,
8540 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
8543 Ybomb_blank, FALSE, FALSE,
8544 EL_BOMB, ACTION_ACTIVATING, -1
8552 Xnut_pause, FALSE, FALSE,
8556 Xnut_fall, FALSE, FALSE,
8560 Ynut_s, FALSE, FALSE,
8561 EL_NUT, ACTION_FALLING, -1
8564 Ynut_sB, FALSE, TRUE,
8565 EL_NUT, ACTION_FALLING, -1
8568 Ynut_e, FALSE, FALSE,
8569 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
8572 Ynut_eB, FALSE, TRUE,
8573 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
8576 Ynut_w, FALSE, FALSE,
8577 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
8580 Ynut_wB, FALSE, TRUE,
8581 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
8584 Ynut_stone, FALSE, FALSE,
8585 EL_NUT, ACTION_BREAKING, -1
8589 Xspring, TRUE, FALSE,
8593 Xspring_pause, FALSE, FALSE,
8597 Xspring_e, TRUE, FALSE,
8598 EL_SPRING_RIGHT, -1, -1
8601 Xspring_w, TRUE, FALSE,
8602 EL_SPRING_LEFT, -1, -1
8605 Xspring_fall, FALSE, FALSE,
8609 Yspring_s, FALSE, FALSE,
8610 EL_SPRING, ACTION_FALLING, -1
8613 Yspring_sB, FALSE, TRUE,
8614 EL_SPRING, ACTION_FALLING, -1
8617 Yspring_e, FALSE, FALSE,
8618 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
8621 Yspring_eB, FALSE, TRUE,
8622 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
8625 Yspring_w, FALSE, FALSE,
8626 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
8629 Yspring_wB, FALSE, TRUE,
8630 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
8633 Yspring_alien_e, FALSE, FALSE,
8634 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
8637 Yspring_alien_eB, FALSE, TRUE,
8638 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
8641 Yspring_alien_w, FALSE, FALSE,
8642 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
8645 Yspring_alien_wB, FALSE, TRUE,
8646 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
8650 Xpush_emerald_e, FALSE, FALSE,
8651 EL_EMERALD, -1, MV_BIT_RIGHT
8654 Xpush_emerald_w, FALSE, FALSE,
8655 EL_EMERALD, -1, MV_BIT_LEFT
8658 Xpush_diamond_e, FALSE, FALSE,
8659 EL_DIAMOND, -1, MV_BIT_RIGHT
8662 Xpush_diamond_w, FALSE, FALSE,
8663 EL_DIAMOND, -1, MV_BIT_LEFT
8666 Xpush_stone_e, FALSE, FALSE,
8667 EL_ROCK, -1, MV_BIT_RIGHT
8670 Xpush_stone_w, FALSE, FALSE,
8671 EL_ROCK, -1, MV_BIT_LEFT
8674 Xpush_bomb_e, FALSE, FALSE,
8675 EL_BOMB, -1, MV_BIT_RIGHT
8678 Xpush_bomb_w, FALSE, FALSE,
8679 EL_BOMB, -1, MV_BIT_LEFT
8682 Xpush_nut_e, FALSE, FALSE,
8683 EL_NUT, -1, MV_BIT_RIGHT
8686 Xpush_nut_w, FALSE, FALSE,
8687 EL_NUT, -1, MV_BIT_LEFT
8690 Xpush_spring_e, FALSE, FALSE,
8691 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
8694 Xpush_spring_w, FALSE, FALSE,
8695 EL_SPRING_LEFT, -1, MV_BIT_LEFT
8699 Xdynamite, TRUE, FALSE,
8700 EL_EM_DYNAMITE, -1, -1
8703 Ydynamite_blank, FALSE, FALSE,
8704 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
8707 Xdynamite_1, TRUE, FALSE,
8708 EL_EM_DYNAMITE_ACTIVE, -1, -1
8711 Xdynamite_2, FALSE, FALSE,
8712 EL_EM_DYNAMITE_ACTIVE, -1, -1
8715 Xdynamite_3, FALSE, FALSE,
8716 EL_EM_DYNAMITE_ACTIVE, -1, -1
8719 Xdynamite_4, FALSE, FALSE,
8720 EL_EM_DYNAMITE_ACTIVE, -1, -1
8724 Xkey_1, TRUE, FALSE,
8728 Xkey_2, TRUE, FALSE,
8732 Xkey_3, TRUE, FALSE,
8736 Xkey_4, TRUE, FALSE,
8740 Xkey_5, TRUE, FALSE,
8741 EL_EMC_KEY_5, -1, -1
8744 Xkey_6, TRUE, FALSE,
8745 EL_EMC_KEY_6, -1, -1
8748 Xkey_7, TRUE, FALSE,
8749 EL_EMC_KEY_7, -1, -1
8752 Xkey_8, TRUE, FALSE,
8753 EL_EMC_KEY_8, -1, -1
8757 Xdoor_1, TRUE, FALSE,
8758 EL_EM_GATE_1, -1, -1
8761 Xdoor_2, TRUE, FALSE,
8762 EL_EM_GATE_2, -1, -1
8765 Xdoor_3, TRUE, FALSE,
8766 EL_EM_GATE_3, -1, -1
8769 Xdoor_4, TRUE, FALSE,
8770 EL_EM_GATE_4, -1, -1
8773 Xdoor_5, TRUE, FALSE,
8774 EL_EMC_GATE_5, -1, -1
8777 Xdoor_6, TRUE, FALSE,
8778 EL_EMC_GATE_6, -1, -1
8781 Xdoor_7, TRUE, FALSE,
8782 EL_EMC_GATE_7, -1, -1
8785 Xdoor_8, TRUE, FALSE,
8786 EL_EMC_GATE_8, -1, -1
8790 Xfake_door_1, TRUE, FALSE,
8791 EL_EM_GATE_1_GRAY, -1, -1
8794 Xfake_door_2, TRUE, FALSE,
8795 EL_EM_GATE_2_GRAY, -1, -1
8798 Xfake_door_3, TRUE, FALSE,
8799 EL_EM_GATE_3_GRAY, -1, -1
8802 Xfake_door_4, TRUE, FALSE,
8803 EL_EM_GATE_4_GRAY, -1, -1
8806 Xfake_door_5, TRUE, FALSE,
8807 EL_EMC_GATE_5_GRAY, -1, -1
8810 Xfake_door_6, TRUE, FALSE,
8811 EL_EMC_GATE_6_GRAY, -1, -1
8814 Xfake_door_7, TRUE, FALSE,
8815 EL_EMC_GATE_7_GRAY, -1, -1
8818 Xfake_door_8, TRUE, FALSE,
8819 EL_EMC_GATE_8_GRAY, -1, -1
8823 Xballoon, TRUE, FALSE,
8827 Yballoon_n, FALSE, FALSE,
8828 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
8831 Yballoon_nB, FALSE, TRUE,
8832 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
8835 Yballoon_e, FALSE, FALSE,
8836 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
8839 Yballoon_eB, FALSE, TRUE,
8840 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
8843 Yballoon_s, FALSE, FALSE,
8844 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
8847 Yballoon_sB, FALSE, TRUE,
8848 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
8851 Yballoon_w, FALSE, FALSE,
8852 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
8855 Yballoon_wB, FALSE, TRUE,
8856 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
8860 Xball_1, TRUE, FALSE,
8861 EL_EMC_MAGIC_BALL, -1, -1
8864 Yball_1, FALSE, FALSE,
8865 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
8868 Xball_2, FALSE, FALSE,
8869 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
8872 Yball_2, FALSE, FALSE,
8873 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
8876 Yball_blank, FALSE, FALSE,
8877 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
8881 Xamoeba_1, TRUE, FALSE,
8882 EL_AMOEBA_DRY, ACTION_OTHER, -1
8885 Xamoeba_2, FALSE, FALSE,
8886 EL_AMOEBA_DRY, ACTION_OTHER, -1
8889 Xamoeba_3, FALSE, FALSE,
8890 EL_AMOEBA_DRY, ACTION_OTHER, -1
8893 Xamoeba_4, FALSE, FALSE,
8894 EL_AMOEBA_DRY, ACTION_OTHER, -1
8897 Xamoeba_5, TRUE, FALSE,
8898 EL_AMOEBA_WET, ACTION_OTHER, -1
8901 Xamoeba_6, FALSE, FALSE,
8902 EL_AMOEBA_WET, ACTION_OTHER, -1
8905 Xamoeba_7, FALSE, FALSE,
8906 EL_AMOEBA_WET, ACTION_OTHER, -1
8909 Xamoeba_8, FALSE, FALSE,
8910 EL_AMOEBA_WET, ACTION_OTHER, -1
8915 EL_AMOEBA_DROP, ACTION_GROWING, -1
8918 Xdrip_fall, FALSE, FALSE,
8919 EL_AMOEBA_DROP, -1, -1
8922 Xdrip_stretch, FALSE, FALSE,
8923 EL_AMOEBA_DROP, ACTION_FALLING, -1
8926 Xdrip_stretchB, FALSE, TRUE,
8927 EL_AMOEBA_DROP, ACTION_FALLING, -1
8930 Ydrip_1_s, FALSE, FALSE,
8931 EL_AMOEBA_DROP, ACTION_FALLING, -1
8934 Ydrip_1_sB, FALSE, TRUE,
8935 EL_AMOEBA_DROP, ACTION_FALLING, -1
8938 Ydrip_2_s, FALSE, FALSE,
8939 EL_AMOEBA_DROP, ACTION_FALLING, -1
8942 Ydrip_2_sB, FALSE, TRUE,
8943 EL_AMOEBA_DROP, ACTION_FALLING, -1
8947 Xwonderwall, TRUE, FALSE,
8948 EL_MAGIC_WALL, -1, -1
8951 Ywonderwall, FALSE, FALSE,
8952 EL_MAGIC_WALL, ACTION_ACTIVE, -1
8956 Xwheel, TRUE, FALSE,
8957 EL_ROBOT_WHEEL, -1, -1
8960 Ywheel, FALSE, FALSE,
8961 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
8965 Xswitch, TRUE, FALSE,
8966 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
8969 Yswitch, FALSE, FALSE,
8970 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
8974 Xbumper, TRUE, FALSE,
8975 EL_EMC_SPRING_BUMPER, -1, -1
8978 Ybumper, FALSE, FALSE,
8979 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
8983 Xacid_nw, TRUE, FALSE,
8984 EL_ACID_POOL_TOPLEFT, -1, -1
8987 Xacid_ne, TRUE, FALSE,
8988 EL_ACID_POOL_TOPRIGHT, -1, -1
8991 Xacid_sw, TRUE, FALSE,
8992 EL_ACID_POOL_BOTTOMLEFT, -1, -1
8995 Xacid_s, TRUE, FALSE,
8996 EL_ACID_POOL_BOTTOM, -1, -1
8999 Xacid_se, TRUE, FALSE,
9000 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
9004 Xfake_blank, TRUE, FALSE,
9005 EL_INVISIBLE_WALL, -1, -1
9008 Yfake_blank, FALSE, FALSE,
9009 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
9013 Xfake_grass, TRUE, FALSE,
9014 EL_EMC_FAKE_GRASS, -1, -1
9017 Yfake_grass, FALSE, FALSE,
9018 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
9022 Xfake_amoeba, TRUE, FALSE,
9023 EL_EMC_DRIPPER, -1, -1
9026 Yfake_amoeba, FALSE, FALSE,
9027 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
9031 Xlenses, TRUE, FALSE,
9032 EL_EMC_LENSES, -1, -1
9036 Xmagnify, TRUE, FALSE,
9037 EL_EMC_MAGNIFIER, -1, -1
9042 EL_QUICKSAND_EMPTY, -1, -1
9045 Xsand_stone, TRUE, FALSE,
9046 EL_QUICKSAND_FULL, -1, -1
9049 Xsand_stonein_1, FALSE, TRUE,
9050 EL_ROCK, ACTION_FILLING, -1
9053 Xsand_stonein_2, FALSE, TRUE,
9054 EL_ROCK, ACTION_FILLING, -1
9057 Xsand_stonein_3, FALSE, TRUE,
9058 EL_ROCK, ACTION_FILLING, -1
9061 Xsand_stonein_4, FALSE, TRUE,
9062 EL_ROCK, ACTION_FILLING, -1
9065 Xsand_sandstone_1, FALSE, FALSE,
9066 EL_QUICKSAND_FILLING, -1, -1
9069 Xsand_sandstone_2, FALSE, FALSE,
9070 EL_QUICKSAND_FILLING, -1, -1
9073 Xsand_sandstone_3, FALSE, FALSE,
9074 EL_QUICKSAND_FILLING, -1, -1
9077 Xsand_sandstone_4, FALSE, FALSE,
9078 EL_QUICKSAND_FILLING, -1, -1
9081 Xsand_stonesand_1, FALSE, FALSE,
9082 EL_QUICKSAND_EMPTYING, -1, -1
9085 Xsand_stonesand_2, FALSE, FALSE,
9086 EL_QUICKSAND_EMPTYING, -1, -1
9089 Xsand_stonesand_3, FALSE, FALSE,
9090 EL_QUICKSAND_EMPTYING, -1, -1
9093 Xsand_stonesand_4, FALSE, FALSE,
9094 EL_QUICKSAND_EMPTYING, -1, -1
9097 Xsand_stoneout_1, FALSE, FALSE,
9098 EL_ROCK, ACTION_EMPTYING, -1
9101 Xsand_stoneout_2, FALSE, FALSE,
9102 EL_ROCK, ACTION_EMPTYING, -1
9105 Xsand_stonesand_quickout_1, FALSE, FALSE,
9106 EL_QUICKSAND_EMPTYING, -1, -1
9109 Xsand_stonesand_quickout_2, FALSE, FALSE,
9110 EL_QUICKSAND_EMPTYING, -1, -1
9114 Xslide_ns, TRUE, FALSE,
9115 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
9118 Yslide_ns_blank, FALSE, FALSE,
9119 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
9122 Xslide_ew, TRUE, FALSE,
9123 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
9126 Yslide_ew_blank, FALSE, FALSE,
9127 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
9131 Xwind_n, TRUE, FALSE,
9132 EL_BALLOON_SWITCH_UP, -1, -1
9135 Xwind_e, TRUE, FALSE,
9136 EL_BALLOON_SWITCH_RIGHT, -1, -1
9139 Xwind_s, TRUE, FALSE,
9140 EL_BALLOON_SWITCH_DOWN, -1, -1
9143 Xwind_w, TRUE, FALSE,
9144 EL_BALLOON_SWITCH_LEFT, -1, -1
9147 Xwind_any, TRUE, FALSE,
9148 EL_BALLOON_SWITCH_ANY, -1, -1
9151 Xwind_stop, TRUE, FALSE,
9152 EL_BALLOON_SWITCH_NONE, -1, -1
9157 EL_EM_EXIT_CLOSED, -1, -1
9160 Xexit_1, TRUE, FALSE,
9161 EL_EM_EXIT_OPEN, -1, -1
9164 Xexit_2, FALSE, FALSE,
9165 EL_EM_EXIT_OPEN, -1, -1
9168 Xexit_3, FALSE, FALSE,
9169 EL_EM_EXIT_OPEN, -1, -1
9173 Xpause, FALSE, FALSE,
9178 Xwall_1, TRUE, FALSE,
9182 Xwall_2, TRUE, FALSE,
9183 EL_EMC_WALL_14, -1, -1
9186 Xwall_3, TRUE, FALSE,
9187 EL_EMC_WALL_15, -1, -1
9190 Xwall_4, TRUE, FALSE,
9191 EL_EMC_WALL_16, -1, -1
9195 Xroundwall_1, TRUE, FALSE,
9196 EL_WALL_SLIPPERY, -1, -1
9199 Xroundwall_2, TRUE, FALSE,
9200 EL_EMC_WALL_SLIPPERY_2, -1, -1
9203 Xroundwall_3, TRUE, FALSE,
9204 EL_EMC_WALL_SLIPPERY_3, -1, -1
9207 Xroundwall_4, TRUE, FALSE,
9208 EL_EMC_WALL_SLIPPERY_4, -1, -1
9212 Xsteel_1, TRUE, FALSE,
9213 EL_STEELWALL, -1, -1
9216 Xsteel_2, TRUE, FALSE,
9217 EL_EMC_STEELWALL_2, -1, -1
9220 Xsteel_3, TRUE, FALSE,
9221 EL_EMC_STEELWALL_3, -1, -1
9224 Xsteel_4, TRUE, FALSE,
9225 EL_EMC_STEELWALL_4, -1, -1
9229 Xdecor_1, TRUE, FALSE,
9230 EL_EMC_WALL_8, -1, -1
9233 Xdecor_2, TRUE, FALSE,
9234 EL_EMC_WALL_6, -1, -1
9237 Xdecor_3, TRUE, FALSE,
9238 EL_EMC_WALL_4, -1, -1
9241 Xdecor_4, TRUE, FALSE,
9242 EL_EMC_WALL_7, -1, -1
9245 Xdecor_5, TRUE, FALSE,
9246 EL_EMC_WALL_5, -1, -1
9249 Xdecor_6, TRUE, FALSE,
9250 EL_EMC_WALL_9, -1, -1
9253 Xdecor_7, TRUE, FALSE,
9254 EL_EMC_WALL_10, -1, -1
9257 Xdecor_8, TRUE, FALSE,
9258 EL_EMC_WALL_1, -1, -1
9261 Xdecor_9, TRUE, FALSE,
9262 EL_EMC_WALL_2, -1, -1
9265 Xdecor_10, TRUE, FALSE,
9266 EL_EMC_WALL_3, -1, -1
9269 Xdecor_11, TRUE, FALSE,
9270 EL_EMC_WALL_11, -1, -1
9273 Xdecor_12, TRUE, FALSE,
9274 EL_EMC_WALL_12, -1, -1
9278 Xalpha_0, TRUE, FALSE,
9279 EL_CHAR('0'), -1, -1
9282 Xalpha_1, TRUE, FALSE,
9283 EL_CHAR('1'), -1, -1
9286 Xalpha_2, TRUE, FALSE,
9287 EL_CHAR('2'), -1, -1
9290 Xalpha_3, TRUE, FALSE,
9291 EL_CHAR('3'), -1, -1
9294 Xalpha_4, TRUE, FALSE,
9295 EL_CHAR('4'), -1, -1
9298 Xalpha_5, TRUE, FALSE,
9299 EL_CHAR('5'), -1, -1
9302 Xalpha_6, TRUE, FALSE,
9303 EL_CHAR('6'), -1, -1
9306 Xalpha_7, TRUE, FALSE,
9307 EL_CHAR('7'), -1, -1
9310 Xalpha_8, TRUE, FALSE,
9311 EL_CHAR('8'), -1, -1
9314 Xalpha_9, TRUE, FALSE,
9315 EL_CHAR('9'), -1, -1
9318 Xalpha_excla, TRUE, FALSE,
9319 EL_CHAR('!'), -1, -1
9322 Xalpha_apost, TRUE, FALSE,
9323 EL_CHAR('\''), -1, -1
9326 Xalpha_comma, TRUE, FALSE,
9327 EL_CHAR(','), -1, -1
9330 Xalpha_minus, TRUE, FALSE,
9331 EL_CHAR('-'), -1, -1
9334 Xalpha_perio, TRUE, FALSE,
9335 EL_CHAR('.'), -1, -1
9338 Xalpha_colon, TRUE, FALSE,
9339 EL_CHAR(':'), -1, -1
9342 Xalpha_quest, TRUE, FALSE,
9343 EL_CHAR('?'), -1, -1
9346 Xalpha_a, TRUE, FALSE,
9347 EL_CHAR('A'), -1, -1
9350 Xalpha_b, TRUE, FALSE,
9351 EL_CHAR('B'), -1, -1
9354 Xalpha_c, TRUE, FALSE,
9355 EL_CHAR('C'), -1, -1
9358 Xalpha_d, TRUE, FALSE,
9359 EL_CHAR('D'), -1, -1
9362 Xalpha_e, TRUE, FALSE,
9363 EL_CHAR('E'), -1, -1
9366 Xalpha_f, TRUE, FALSE,
9367 EL_CHAR('F'), -1, -1
9370 Xalpha_g, TRUE, FALSE,
9371 EL_CHAR('G'), -1, -1
9374 Xalpha_h, TRUE, FALSE,
9375 EL_CHAR('H'), -1, -1
9378 Xalpha_i, TRUE, FALSE,
9379 EL_CHAR('I'), -1, -1
9382 Xalpha_j, TRUE, FALSE,
9383 EL_CHAR('J'), -1, -1
9386 Xalpha_k, TRUE, FALSE,
9387 EL_CHAR('K'), -1, -1
9390 Xalpha_l, TRUE, FALSE,
9391 EL_CHAR('L'), -1, -1
9394 Xalpha_m, TRUE, FALSE,
9395 EL_CHAR('M'), -1, -1
9398 Xalpha_n, TRUE, FALSE,
9399 EL_CHAR('N'), -1, -1
9402 Xalpha_o, TRUE, FALSE,
9403 EL_CHAR('O'), -1, -1
9406 Xalpha_p, TRUE, FALSE,
9407 EL_CHAR('P'), -1, -1
9410 Xalpha_q, TRUE, FALSE,
9411 EL_CHAR('Q'), -1, -1
9414 Xalpha_r, TRUE, FALSE,
9415 EL_CHAR('R'), -1, -1
9418 Xalpha_s, TRUE, FALSE,
9419 EL_CHAR('S'), -1, -1
9422 Xalpha_t, TRUE, FALSE,
9423 EL_CHAR('T'), -1, -1
9426 Xalpha_u, TRUE, FALSE,
9427 EL_CHAR('U'), -1, -1
9430 Xalpha_v, TRUE, FALSE,
9431 EL_CHAR('V'), -1, -1
9434 Xalpha_w, TRUE, FALSE,
9435 EL_CHAR('W'), -1, -1
9438 Xalpha_x, TRUE, FALSE,
9439 EL_CHAR('X'), -1, -1
9442 Xalpha_y, TRUE, FALSE,
9443 EL_CHAR('Y'), -1, -1
9446 Xalpha_z, TRUE, FALSE,
9447 EL_CHAR('Z'), -1, -1
9450 Xalpha_arrow_e, TRUE, FALSE,
9451 EL_CHAR('>'), -1, -1
9454 Xalpha_arrow_w, TRUE, FALSE,
9455 EL_CHAR('<'), -1, -1
9458 Xalpha_copyr, TRUE, FALSE,
9459 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
9463 Ykey_1_blank, FALSE, FALSE,
9464 EL_EM_KEY_1, ACTION_COLLECTING, -1
9467 Ykey_2_blank, FALSE, FALSE,
9468 EL_EM_KEY_2, ACTION_COLLECTING, -1
9471 Ykey_3_blank, FALSE, FALSE,
9472 EL_EM_KEY_3, ACTION_COLLECTING, -1
9475 Ykey_4_blank, FALSE, FALSE,
9476 EL_EM_KEY_4, ACTION_COLLECTING, -1
9479 Ykey_5_blank, FALSE, FALSE,
9480 EL_EMC_KEY_5, ACTION_COLLECTING, -1
9483 Ykey_6_blank, FALSE, FALSE,
9484 EL_EMC_KEY_6, ACTION_COLLECTING, -1
9487 Ykey_7_blank, FALSE, FALSE,
9488 EL_EMC_KEY_7, ACTION_COLLECTING, -1
9491 Ykey_8_blank, FALSE, FALSE,
9492 EL_EMC_KEY_8, ACTION_COLLECTING, -1
9495 Ylenses_blank, FALSE, FALSE,
9496 EL_EMC_LENSES, ACTION_COLLECTING, -1
9499 Ymagnify_blank, FALSE, FALSE,
9500 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
9503 Ygrass_blank, FALSE, FALSE,
9504 EL_EMC_GRASS, ACTION_SNAPPING, -1
9507 Ydirt_blank, FALSE, FALSE,
9508 EL_SAND, ACTION_SNAPPING, -1
9517 static struct Mapping_EM_to_RND_player
9526 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
9530 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
9534 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
9538 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
9542 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
9546 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
9550 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
9554 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
9558 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
9562 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
9566 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
9570 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
9574 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
9578 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
9582 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
9586 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
9590 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
9594 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
9598 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
9602 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
9606 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
9610 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
9614 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
9618 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
9622 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
9626 EL_PLAYER_1, ACTION_DEFAULT, -1,
9630 EL_PLAYER_2, ACTION_DEFAULT, -1,
9634 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
9638 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
9642 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
9646 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
9650 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
9654 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
9658 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
9662 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
9666 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
9670 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
9674 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
9678 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
9682 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
9686 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
9690 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
9694 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
9698 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
9702 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
9706 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
9710 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
9714 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
9718 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
9722 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
9726 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
9730 EL_PLAYER_3, ACTION_DEFAULT, -1,
9734 EL_PLAYER_4, ACTION_DEFAULT, -1,
9743 int map_element_RND_to_EM_cave(int element_rnd)
9745 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
9746 static boolean mapping_initialized = FALSE;
9748 if (!mapping_initialized)
9752 // return "Xalpha_quest" for all undefined elements in mapping array
9753 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9754 mapping_RND_to_EM[i] = Xalpha_quest;
9756 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
9757 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
9758 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
9759 em_object_mapping_list[i].element_em;
9761 mapping_initialized = TRUE;
9764 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
9766 Warn("invalid RND level element %d", element_rnd);
9771 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
9774 int map_element_EM_to_RND_cave(int element_em_cave)
9776 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
9777 static boolean mapping_initialized = FALSE;
9779 if (!mapping_initialized)
9783 // return "EL_UNKNOWN" for all undefined elements in mapping array
9784 for (i = 0; i < GAME_TILE_MAX; i++)
9785 mapping_EM_to_RND[i] = EL_UNKNOWN;
9787 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
9788 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
9789 em_object_mapping_list[i].element_rnd;
9791 mapping_initialized = TRUE;
9794 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
9796 Warn("invalid EM cave element %d", element_em_cave);
9801 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
9804 int map_element_EM_to_RND_game(int element_em_game)
9806 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
9807 static boolean mapping_initialized = FALSE;
9809 if (!mapping_initialized)
9813 // return "EL_UNKNOWN" for all undefined elements in mapping array
9814 for (i = 0; i < GAME_TILE_MAX; i++)
9815 mapping_EM_to_RND[i] = EL_UNKNOWN;
9817 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
9818 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
9819 em_object_mapping_list[i].element_rnd;
9821 mapping_initialized = TRUE;
9824 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
9826 Warn("invalid EM game element %d", element_em_game);
9831 return mapping_EM_to_RND[element_em_game];
9834 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
9836 struct LevelInfo_EM *level_em = level->native_em_level;
9837 struct CAVE *cav = level_em->cav;
9840 for (i = 0; i < GAME_TILE_MAX; i++)
9841 cav->android_array[i] = Cblank;
9843 for (i = 0; i < level->num_android_clone_elements; i++)
9845 int element_rnd = level->android_clone_element[i];
9846 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
9848 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
9849 if (em_object_mapping_list[j].element_rnd == element_rnd)
9850 cav->android_array[em_object_mapping_list[j].element_em] =
9855 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
9857 struct LevelInfo_EM *level_em = level->native_em_level;
9858 struct CAVE *cav = level_em->cav;
9861 level->num_android_clone_elements = 0;
9863 for (i = 0; i < GAME_TILE_MAX; i++)
9865 int element_em_cave = cav->android_array[i];
9867 boolean element_found = FALSE;
9869 if (element_em_cave == Cblank)
9872 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
9874 for (j = 0; j < level->num_android_clone_elements; j++)
9875 if (level->android_clone_element[j] == element_rnd)
9876 element_found = TRUE;
9880 level->android_clone_element[level->num_android_clone_elements++] =
9883 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
9888 if (level->num_android_clone_elements == 0)
9890 level->num_android_clone_elements = 1;
9891 level->android_clone_element[0] = EL_EMPTY;
9895 int map_direction_RND_to_EM(int direction)
9897 return (direction == MV_UP ? 0 :
9898 direction == MV_RIGHT ? 1 :
9899 direction == MV_DOWN ? 2 :
9900 direction == MV_LEFT ? 3 :
9904 int map_direction_EM_to_RND(int direction)
9906 return (direction == 0 ? MV_UP :
9907 direction == 1 ? MV_RIGHT :
9908 direction == 2 ? MV_DOWN :
9909 direction == 3 ? MV_LEFT :
9913 int map_element_RND_to_SP(int element_rnd)
9915 int element_sp = 0x20; // map unknown elements to yellow "hardware"
9917 if (element_rnd >= EL_SP_START &&
9918 element_rnd <= EL_SP_END)
9919 element_sp = element_rnd - EL_SP_START;
9920 else if (element_rnd == EL_EMPTY_SPACE)
9922 else if (element_rnd == EL_INVISIBLE_WALL)
9928 int map_element_SP_to_RND(int element_sp)
9930 int element_rnd = EL_UNKNOWN;
9932 if (element_sp >= 0x00 &&
9934 element_rnd = EL_SP_START + element_sp;
9935 else if (element_sp == 0x28)
9936 element_rnd = EL_INVISIBLE_WALL;
9941 int map_action_SP_to_RND(int action_sp)
9945 case actActive: return ACTION_ACTIVE;
9946 case actImpact: return ACTION_IMPACT;
9947 case actExploding: return ACTION_EXPLODING;
9948 case actDigging: return ACTION_DIGGING;
9949 case actSnapping: return ACTION_SNAPPING;
9950 case actCollecting: return ACTION_COLLECTING;
9951 case actPassing: return ACTION_PASSING;
9952 case actPushing: return ACTION_PUSHING;
9953 case actDropping: return ACTION_DROPPING;
9955 default: return ACTION_DEFAULT;
9959 int map_element_RND_to_MM(int element_rnd)
9961 return (element_rnd >= EL_MM_START_1 &&
9962 element_rnd <= EL_MM_END_1 ?
9963 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
9965 element_rnd >= EL_MM_START_2 &&
9966 element_rnd <= EL_MM_END_2 ?
9967 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
9969 element_rnd >= EL_MM_START_3 &&
9970 element_rnd <= EL_MM_END_3 ?
9971 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
9973 element_rnd >= EL_CHAR_START &&
9974 element_rnd <= EL_CHAR_END ?
9975 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
9977 element_rnd >= EL_MM_RUNTIME_START &&
9978 element_rnd <= EL_MM_RUNTIME_END ?
9979 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
9981 EL_MM_EMPTY_NATIVE);
9984 int map_element_MM_to_RND(int element_mm)
9986 return (element_mm == EL_MM_EMPTY_NATIVE ||
9987 element_mm == EL_DF_EMPTY_NATIVE ?
9990 element_mm >= EL_MM_START_1_NATIVE &&
9991 element_mm <= EL_MM_END_1_NATIVE ?
9992 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
9994 element_mm >= EL_MM_START_2_NATIVE &&
9995 element_mm <= EL_MM_END_2_NATIVE ?
9996 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
9998 element_mm >= EL_MM_START_3_NATIVE &&
9999 element_mm <= EL_MM_END_3_NATIVE ?
10000 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
10002 element_mm >= EL_MM_CHAR_START_NATIVE &&
10003 element_mm <= EL_MM_CHAR_END_NATIVE ?
10004 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
10006 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
10007 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
10008 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
10013 int map_action_MM_to_RND(int action_mm)
10015 // all MM actions are defined to exactly match their RND counterparts
10019 int map_sound_MM_to_RND(int sound_mm)
10023 case SND_MM_GAME_LEVELTIME_CHARGING:
10024 return SND_GAME_LEVELTIME_CHARGING;
10026 case SND_MM_GAME_HEALTH_CHARGING:
10027 return SND_GAME_HEALTH_CHARGING;
10030 return SND_UNDEFINED;
10034 int map_mm_wall_element(int element)
10036 return (element >= EL_MM_STEEL_WALL_START &&
10037 element <= EL_MM_STEEL_WALL_END ?
10040 element >= EL_MM_WOODEN_WALL_START &&
10041 element <= EL_MM_WOODEN_WALL_END ?
10042 EL_MM_WOODEN_WALL :
10044 element >= EL_MM_ICE_WALL_START &&
10045 element <= EL_MM_ICE_WALL_END ?
10048 element >= EL_MM_AMOEBA_WALL_START &&
10049 element <= EL_MM_AMOEBA_WALL_END ?
10050 EL_MM_AMOEBA_WALL :
10052 element >= EL_DF_STEEL_WALL_START &&
10053 element <= EL_DF_STEEL_WALL_END ?
10056 element >= EL_DF_WOODEN_WALL_START &&
10057 element <= EL_DF_WOODEN_WALL_END ?
10058 EL_DF_WOODEN_WALL :
10063 int map_mm_wall_element_editor(int element)
10067 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
10068 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
10069 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
10070 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
10071 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
10072 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
10074 default: return element;
10078 int get_next_element(int element)
10082 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
10083 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
10084 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
10085 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
10086 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
10087 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
10088 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
10089 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
10090 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
10091 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
10092 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
10094 default: return element;
10098 int el2img_mm(int element_mm)
10100 return el2img(map_element_MM_to_RND(element_mm));
10103 int el_act2img_mm(int element_mm, int action)
10105 return el_act2img(map_element_MM_to_RND(element_mm), action);
10108 int el_act_dir2img(int element, int action, int direction)
10110 element = GFX_ELEMENT(element);
10111 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
10113 // direction_graphic[][] == graphic[] for undefined direction graphics
10114 return element_info[element].direction_graphic[action][direction];
10117 static int el_act_dir2crm(int element, int action, int direction)
10119 element = GFX_ELEMENT(element);
10120 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
10122 // direction_graphic[][] == graphic[] for undefined direction graphics
10123 return element_info[element].direction_crumbled[action][direction];
10126 int el_act2img(int element, int action)
10128 element = GFX_ELEMENT(element);
10130 return element_info[element].graphic[action];
10133 int el_act2crm(int element, int action)
10135 element = GFX_ELEMENT(element);
10137 return element_info[element].crumbled[action];
10140 int el_dir2img(int element, int direction)
10142 element = GFX_ELEMENT(element);
10144 return el_act_dir2img(element, ACTION_DEFAULT, direction);
10147 int el2baseimg(int element)
10149 return element_info[element].graphic[ACTION_DEFAULT];
10152 int el2img(int element)
10154 element = GFX_ELEMENT(element);
10156 return element_info[element].graphic[ACTION_DEFAULT];
10159 int el2edimg(int element)
10161 element = GFX_ELEMENT(element);
10163 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
10166 int el2preimg(int element)
10168 element = GFX_ELEMENT(element);
10170 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
10173 int el2panelimg(int element)
10175 element = GFX_ELEMENT(element);
10177 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
10180 int font2baseimg(int font_nr)
10182 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
10185 int getBeltNrFromBeltElement(int element)
10187 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
10188 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
10189 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
10192 int getBeltNrFromBeltActiveElement(int element)
10194 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
10195 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
10196 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
10199 int getBeltNrFromBeltSwitchElement(int element)
10201 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
10202 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
10203 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
10206 int getBeltDirNrFromBeltElement(int element)
10208 static int belt_base_element[4] =
10210 EL_CONVEYOR_BELT_1_LEFT,
10211 EL_CONVEYOR_BELT_2_LEFT,
10212 EL_CONVEYOR_BELT_3_LEFT,
10213 EL_CONVEYOR_BELT_4_LEFT
10216 int belt_nr = getBeltNrFromBeltElement(element);
10217 int belt_dir_nr = element - belt_base_element[belt_nr];
10219 return (belt_dir_nr % 3);
10222 int getBeltDirNrFromBeltSwitchElement(int element)
10224 static int belt_base_element[4] =
10226 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
10227 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
10228 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
10229 EL_CONVEYOR_BELT_4_SWITCH_LEFT
10232 int belt_nr = getBeltNrFromBeltSwitchElement(element);
10233 int belt_dir_nr = element - belt_base_element[belt_nr];
10235 return (belt_dir_nr % 3);
10238 int getBeltDirFromBeltElement(int element)
10240 static int belt_move_dir[3] =
10247 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
10249 return belt_move_dir[belt_dir_nr];
10252 int getBeltDirFromBeltSwitchElement(int element)
10254 static int belt_move_dir[3] =
10261 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
10263 return belt_move_dir[belt_dir_nr];
10266 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
10268 static int belt_base_element[4] =
10270 EL_CONVEYOR_BELT_1_LEFT,
10271 EL_CONVEYOR_BELT_2_LEFT,
10272 EL_CONVEYOR_BELT_3_LEFT,
10273 EL_CONVEYOR_BELT_4_LEFT
10276 return belt_base_element[belt_nr] + belt_dir_nr;
10279 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
10281 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
10283 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
10286 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
10288 static int belt_base_element[4] =
10290 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
10291 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
10292 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
10293 EL_CONVEYOR_BELT_4_SWITCH_LEFT
10296 return belt_base_element[belt_nr] + belt_dir_nr;
10299 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
10301 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
10303 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
10306 boolean swapTiles_EM(boolean is_pre_emc_cave)
10308 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
10311 boolean getTeamMode_EM(void)
10313 return game.team_mode || network_playing;
10316 boolean isActivePlayer_EM(int player_nr)
10318 return stored_player[player_nr].active;
10321 unsigned int InitRND(int seed)
10323 if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
10324 return InitEngineRandom_BD(seed);
10325 else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10326 return InitEngineRandom_EM(seed);
10327 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10328 return InitEngineRandom_SP(seed);
10329 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
10330 return InitEngineRandom_MM(seed);
10332 return InitEngineRandom_RND(seed);
10335 static struct Mapping_BD_to_RND_object bd_object_mapping[O_MAX_ALL];
10336 static struct Mapping_EM_to_RND_object em_object_mapping[GAME_TILE_MAX];
10337 static struct Mapping_EM_to_RND_player em_player_mapping[MAX_PLAYERS][PLY_MAX];
10339 static int get_effective_element_EM(int tile, int frame_em)
10341 int element = em_object_mapping[tile].element_rnd;
10342 int action = em_object_mapping[tile].action;
10343 boolean is_backside = em_object_mapping[tile].is_backside;
10344 boolean action_removing = (action == ACTION_DIGGING ||
10345 action == ACTION_SNAPPING ||
10346 action == ACTION_COLLECTING);
10354 return (frame_em > 5 ? EL_EMPTY : element);
10360 else // frame_em == 7
10371 case Ydiamond_stone:
10374 case Xdrip_stretch:
10375 case Xdrip_stretchB:
10390 case Ylenses_blank:
10391 case Ymagnify_blank:
10394 case Xsand_stonein_1:
10395 case Xsand_stonein_2:
10396 case Xsand_stonein_3:
10397 case Xsand_stonein_4:
10401 return (is_backside || action_removing ? EL_EMPTY : element);
10406 static boolean check_linear_animation_EM(int tile)
10410 case Xsand_stonesand_1:
10411 case Xsand_stonesand_quickout_1:
10412 case Xsand_sandstone_1:
10413 case Xsand_stonein_1:
10414 case Xsand_stoneout_1:
10442 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
10443 boolean has_crumbled_graphics,
10444 int crumbled, int sync_frame)
10446 // if element can be crumbled, but certain action graphics are just empty
10447 // space (like instantly snapping sand to empty space in 1 frame), do not
10448 // treat these empty space graphics as crumbled graphics in EMC engine
10449 if (crumbled == IMG_EMPTY_SPACE)
10450 has_crumbled_graphics = FALSE;
10452 if (has_crumbled_graphics)
10454 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
10455 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
10456 g_crumbled->anim_delay,
10457 g_crumbled->anim_mode,
10458 g_crumbled->anim_start_frame,
10461 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
10462 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
10464 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
10465 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
10467 g_em->has_crumbled_graphics = TRUE;
10471 g_em->crumbled_bitmap = NULL;
10472 g_em->crumbled_src_x = 0;
10473 g_em->crumbled_src_y = 0;
10474 g_em->crumbled_border_size = 0;
10475 g_em->crumbled_tile_size = 0;
10477 g_em->has_crumbled_graphics = FALSE;
10482 void ResetGfxAnimation_EM(int x, int y, int tile)
10484 GfxFrame[x][y] = 0;
10488 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
10489 int tile, int frame_em, int x, int y)
10491 int action = em_object_mapping[tile].action;
10492 int direction = em_object_mapping[tile].direction;
10493 int effective_element = get_effective_element_EM(tile, frame_em);
10494 int graphic = (direction == MV_NONE ?
10495 el_act2img(effective_element, action) :
10496 el_act_dir2img(effective_element, action, direction));
10497 struct GraphicInfo *g = &graphic_info[graphic];
10499 boolean action_removing = (action == ACTION_DIGGING ||
10500 action == ACTION_SNAPPING ||
10501 action == ACTION_COLLECTING);
10502 boolean action_moving = (action == ACTION_FALLING ||
10503 action == ACTION_MOVING ||
10504 action == ACTION_PUSHING ||
10505 action == ACTION_EATING ||
10506 action == ACTION_FILLING ||
10507 action == ACTION_EMPTYING);
10508 boolean action_falling = (action == ACTION_FALLING ||
10509 action == ACTION_FILLING ||
10510 action == ACTION_EMPTYING);
10512 // special case: graphic uses "2nd movement tile" and has defined
10513 // 7 frames for movement animation (or less) => use default graphic
10514 // for last (8th) frame which ends the movement animation
10515 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
10517 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
10518 graphic = (direction == MV_NONE ?
10519 el_act2img(effective_element, action) :
10520 el_act_dir2img(effective_element, action, direction));
10522 g = &graphic_info[graphic];
10525 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
10527 GfxFrame[x][y] = 0;
10529 else if (action_moving)
10531 boolean is_backside = em_object_mapping[tile].is_backside;
10535 int direction = em_object_mapping[tile].direction;
10536 int move_dir = (action_falling ? MV_DOWN : direction);
10541 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
10542 if (g->double_movement && frame_em == 0)
10543 GfxFrame[x][y] = 0;
10546 if (move_dir == MV_LEFT)
10547 GfxFrame[x - 1][y] = GfxFrame[x][y];
10548 else if (move_dir == MV_RIGHT)
10549 GfxFrame[x + 1][y] = GfxFrame[x][y];
10550 else if (move_dir == MV_UP)
10551 GfxFrame[x][y - 1] = GfxFrame[x][y];
10552 else if (move_dir == MV_DOWN)
10553 GfxFrame[x][y + 1] = GfxFrame[x][y];
10560 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
10561 if (tile == Xsand_stonesand_quickout_1 ||
10562 tile == Xsand_stonesand_quickout_2)
10566 if (graphic_info[graphic].anim_global_sync)
10567 sync_frame = FrameCounter;
10568 else if (graphic_info[graphic].anim_global_anim_sync)
10569 sync_frame = getGlobalAnimSyncFrame();
10570 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
10571 sync_frame = GfxFrame[x][y];
10573 sync_frame = 0; // playfield border (pseudo steel)
10575 SetRandomAnimationValue(x, y);
10577 int frame = getAnimationFrame(g->anim_frames,
10580 g->anim_start_frame,
10583 g_em->unique_identifier =
10584 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
10587 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
10588 int tile, int frame_em, int x, int y)
10590 int action = em_object_mapping[tile].action;
10591 int direction = em_object_mapping[tile].direction;
10592 boolean is_backside = em_object_mapping[tile].is_backside;
10593 int effective_element = get_effective_element_EM(tile, frame_em);
10594 int effective_action = action;
10595 int graphic = (direction == MV_NONE ?
10596 el_act2img(effective_element, effective_action) :
10597 el_act_dir2img(effective_element, effective_action,
10599 int crumbled = (direction == MV_NONE ?
10600 el_act2crm(effective_element, effective_action) :
10601 el_act_dir2crm(effective_element, effective_action,
10603 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
10604 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
10605 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
10606 struct GraphicInfo *g = &graphic_info[graphic];
10609 // special case: graphic uses "2nd movement tile" and has defined
10610 // 7 frames for movement animation (or less) => use default graphic
10611 // for last (8th) frame which ends the movement animation
10612 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
10614 effective_action = ACTION_DEFAULT;
10615 graphic = (direction == MV_NONE ?
10616 el_act2img(effective_element, effective_action) :
10617 el_act_dir2img(effective_element, effective_action,
10619 crumbled = (direction == MV_NONE ?
10620 el_act2crm(effective_element, effective_action) :
10621 el_act_dir2crm(effective_element, effective_action,
10624 g = &graphic_info[graphic];
10627 if (graphic_info[graphic].anim_global_sync)
10628 sync_frame = FrameCounter;
10629 else if (graphic_info[graphic].anim_global_anim_sync)
10630 sync_frame = getGlobalAnimSyncFrame();
10631 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
10632 sync_frame = GfxFrame[x][y];
10634 sync_frame = 0; // playfield border (pseudo steel)
10636 SetRandomAnimationValue(x, y);
10638 int frame = getAnimationFrame(g->anim_frames,
10641 g->anim_start_frame,
10644 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
10645 g->double_movement && is_backside);
10647 // (updating the "crumbled" graphic definitions is probably not really needed,
10648 // as animations for crumbled graphics can't be longer than one EMC cycle)
10649 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
10653 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
10654 int player_nr, int anim, int frame_em)
10656 int element = em_player_mapping[player_nr][anim].element_rnd;
10657 int action = em_player_mapping[player_nr][anim].action;
10658 int direction = em_player_mapping[player_nr][anim].direction;
10659 int graphic = (direction == MV_NONE ?
10660 el_act2img(element, action) :
10661 el_act_dir2img(element, action, direction));
10662 struct GraphicInfo *g = &graphic_info[graphic];
10665 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
10667 stored_player[player_nr].StepFrame = frame_em;
10669 sync_frame = stored_player[player_nr].Frame;
10671 int frame = getAnimationFrame(g->anim_frames,
10674 g->anim_start_frame,
10677 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
10678 &g_em->src_x, &g_em->src_y, FALSE);
10681 #define BD_GFX_RANGE(a, n, i) ((i) >= (a) && (i) < (a) + (n))
10682 #define BD_GFX_FRAME(b, i) (((i) - (b)) * 8)
10684 void InitGraphicInfo_BD(void)
10688 // always start with reliable default values
10689 for (i = 0; i < O_MAX_ALL; i++)
10691 bd_object_mapping[i].element_rnd = EL_UNKNOWN;
10692 bd_object_mapping[i].action = ACTION_DEFAULT;
10693 bd_object_mapping[i].direction = MV_NONE;
10696 for (i = 0; bd_object_mapping_list[i].element_bd != -1; i++)
10698 int e = bd_object_mapping_list[i].element_bd;
10700 bd_object_mapping[e].element_rnd = bd_object_mapping_list[i].element_rnd;
10702 if (bd_object_mapping_list[i].action != -1)
10703 bd_object_mapping[e].action = bd_object_mapping_list[i].action;
10705 if (bd_object_mapping_list[i].direction != -1)
10706 bd_object_mapping[e].direction =
10707 MV_DIR_FROM_BIT(bd_object_mapping_list[i].direction);
10710 for (i = 0; i < O_MAX_ALL; i++)
10712 int element = bd_object_mapping[i].element_rnd;
10713 int action = bd_object_mapping[i].action;
10714 int direction = bd_object_mapping[i].direction;
10716 for (j = 0; j < 8; j++)
10718 int effective_element = element;
10719 int effective_action = action;
10720 int graphic = (el_act_dir2img(effective_element, effective_action,
10722 struct GraphicInfo *g = &graphic_info[graphic];
10723 struct GraphicInfo_BD *g_bd = &graphic_info_bd_object[i][j];
10724 Bitmap *src_bitmap;
10726 int sync_frame = (BD_GFX_RANGE(O_PRE_PL_1, 3, i) ? BD_GFX_FRAME(O_PRE_PL_1, i) :
10727 BD_GFX_RANGE(O_PRE_DIA_1, 5, i) ? BD_GFX_FRAME(O_PRE_DIA_1, i) :
10728 BD_GFX_RANGE(O_PRE_STONE_1, 4, i) ? BD_GFX_FRAME(O_PRE_STONE_1, i) :
10729 BD_GFX_RANGE(O_PRE_STEEL_1, 4, i) ? BD_GFX_FRAME(O_PRE_STEEL_1, i) :
10730 BD_GFX_RANGE(O_BOMB_TICK_1, 7, i) ? BD_GFX_FRAME(O_BOMB_TICK_1, i) :
10731 BD_GFX_RANGE(O_BOMB_EXPL_1, 4, i) ? BD_GFX_FRAME(O_BOMB_EXPL_1, i) :
10732 BD_GFX_RANGE(O_NUT_EXPL_1, 4, i) ? BD_GFX_FRAME(O_NUT_EXPL_1, i) :
10733 BD_GFX_RANGE(O_GHOST_EXPL_1, 4, i) ? BD_GFX_FRAME(O_GHOST_EXPL_1, i) :
10734 BD_GFX_RANGE(O_EXPLODE_1, 5, i) ? BD_GFX_FRAME(O_EXPLODE_1, i) :
10735 BD_GFX_RANGE(O_PRE_CLOCK_1, 4, i) ? BD_GFX_FRAME(O_PRE_CLOCK_1, i) :
10736 BD_GFX_RANGE(O_NITRO_EXPL_1, 4, i) ? BD_GFX_FRAME(O_NITRO_EXPL_1, i) :
10737 BD_GFX_RANGE(O_AMOEBA_2_EXPL_1, 4, i) ? BD_GFX_FRAME(O_AMOEBA_2_EXPL_1, i):
10738 i == O_INBOX_OPEN || i == O_OUTBOX_OPEN ? j :
10740 int frame = getAnimationFrame(g->anim_frames,
10743 g->anim_start_frame,
10746 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
10748 g_bd->bitmap = src_bitmap;
10749 g_bd->src_x = src_x;
10750 g_bd->src_y = src_y;
10751 g_bd->width = TILEX;
10752 g_bd->height = TILEY;
10754 g_bd->graphic = graphic;
10755 g_bd->frame = frame;
10759 // game graphics template for level-specific colors for native BD levels
10760 int graphic = IMG_BD_GAME_GRAPHICS_COLOR_TEMPLATE;
10761 struct GraphicInfo_BD *g_bd = &graphic_info_bd_color_template;
10762 Bitmap *src_bitmap;
10765 getGraphicSourceExt(graphic, 0, &src_bitmap, &src_x, &src_y, FALSE);
10767 g_bd->bitmap = src_bitmap;
10768 g_bd->src_x = src_x;
10769 g_bd->src_y = src_y;
10770 g_bd->width = TILEX;
10771 g_bd->height = TILEY;
10773 g_bd->graphic = graphic;
10777 void InitGraphicInfo_EM(void)
10781 // always start with reliable default values
10782 for (i = 0; i < GAME_TILE_MAX; i++)
10784 em_object_mapping[i].element_rnd = EL_UNKNOWN;
10785 em_object_mapping[i].is_backside = FALSE;
10786 em_object_mapping[i].action = ACTION_DEFAULT;
10787 em_object_mapping[i].direction = MV_NONE;
10790 // always start with reliable default values
10791 for (p = 0; p < MAX_PLAYERS; p++)
10793 for (i = 0; i < PLY_MAX; i++)
10795 em_player_mapping[p][i].element_rnd = EL_UNKNOWN;
10796 em_player_mapping[p][i].action = ACTION_DEFAULT;
10797 em_player_mapping[p][i].direction = MV_NONE;
10801 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
10803 int e = em_object_mapping_list[i].element_em;
10805 em_object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
10806 em_object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
10808 if (em_object_mapping_list[i].action != -1)
10809 em_object_mapping[e].action = em_object_mapping_list[i].action;
10811 if (em_object_mapping_list[i].direction != -1)
10812 em_object_mapping[e].direction =
10813 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
10816 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
10818 int a = em_player_mapping_list[i].action_em;
10819 int p = em_player_mapping_list[i].player_nr;
10821 em_player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
10823 if (em_player_mapping_list[i].action != -1)
10824 em_player_mapping[p][a].action = em_player_mapping_list[i].action;
10826 if (em_player_mapping_list[i].direction != -1)
10827 em_player_mapping[p][a].direction =
10828 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
10831 for (i = 0; i < GAME_TILE_MAX; i++)
10833 int element = em_object_mapping[i].element_rnd;
10834 int action = em_object_mapping[i].action;
10835 int direction = em_object_mapping[i].direction;
10836 boolean is_backside = em_object_mapping[i].is_backside;
10837 boolean action_exploding = ((action == ACTION_EXPLODING ||
10838 action == ACTION_SMASHED_BY_ROCK ||
10839 action == ACTION_SMASHED_BY_SPRING) &&
10840 element != EL_DIAMOND);
10841 boolean action_active = (action == ACTION_ACTIVE);
10842 boolean action_other = (action == ACTION_OTHER);
10844 for (j = 0; j < 8; j++)
10846 int effective_element = get_effective_element_EM(i, j);
10847 int effective_action = (j < 7 ? action :
10848 i == Xdrip_stretch ? action :
10849 i == Xdrip_stretchB ? action :
10850 i == Ydrip_1_s ? action :
10851 i == Ydrip_1_sB ? action :
10852 i == Yball_1 ? action :
10853 i == Xball_2 ? action :
10854 i == Yball_2 ? action :
10855 i == Yball_blank ? action :
10856 i == Ykey_1_blank ? action :
10857 i == Ykey_2_blank ? action :
10858 i == Ykey_3_blank ? action :
10859 i == Ykey_4_blank ? action :
10860 i == Ykey_5_blank ? action :
10861 i == Ykey_6_blank ? action :
10862 i == Ykey_7_blank ? action :
10863 i == Ykey_8_blank ? action :
10864 i == Ylenses_blank ? action :
10865 i == Ymagnify_blank ? action :
10866 i == Ygrass_blank ? action :
10867 i == Ydirt_blank ? action :
10868 i == Xsand_stonein_1 ? action :
10869 i == Xsand_stonein_2 ? action :
10870 i == Xsand_stonein_3 ? action :
10871 i == Xsand_stonein_4 ? action :
10872 i == Xsand_stoneout_1 ? action :
10873 i == Xsand_stoneout_2 ? action :
10874 i == Xboom_android ? ACTION_EXPLODING :
10875 action_exploding ? ACTION_EXPLODING :
10876 action_active ? action :
10877 action_other ? action :
10879 int graphic = (el_act_dir2img(effective_element, effective_action,
10881 int crumbled = (el_act_dir2crm(effective_element, effective_action,
10883 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
10884 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
10885 boolean has_action_graphics = (graphic != base_graphic);
10886 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
10887 struct GraphicInfo *g = &graphic_info[graphic];
10888 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
10889 Bitmap *src_bitmap;
10891 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
10892 boolean special_animation = (action != ACTION_DEFAULT &&
10893 g->anim_frames == 3 &&
10894 g->anim_delay == 2 &&
10895 g->anim_mode & ANIM_LINEAR);
10896 int sync_frame = (i == Xdrip_stretch ? 7 :
10897 i == Xdrip_stretchB ? 7 :
10898 i == Ydrip_2_s ? j + 8 :
10899 i == Ydrip_2_sB ? j + 8 :
10901 i == Xacid_2 ? 10 :
10902 i == Xacid_3 ? 20 :
10903 i == Xacid_4 ? 30 :
10904 i == Xacid_5 ? 40 :
10905 i == Xacid_6 ? 50 :
10906 i == Xacid_7 ? 60 :
10907 i == Xacid_8 ? 70 :
10908 i == Xfake_acid_1 ? 0 :
10909 i == Xfake_acid_2 ? 10 :
10910 i == Xfake_acid_3 ? 20 :
10911 i == Xfake_acid_4 ? 30 :
10912 i == Xfake_acid_5 ? 40 :
10913 i == Xfake_acid_6 ? 50 :
10914 i == Xfake_acid_7 ? 60 :
10915 i == Xfake_acid_8 ? 70 :
10916 i == Xfake_acid_1_player ? 0 :
10917 i == Xfake_acid_2_player ? 10 :
10918 i == Xfake_acid_3_player ? 20 :
10919 i == Xfake_acid_4_player ? 30 :
10920 i == Xfake_acid_5_player ? 40 :
10921 i == Xfake_acid_6_player ? 50 :
10922 i == Xfake_acid_7_player ? 60 :
10923 i == Xfake_acid_8_player ? 70 :
10925 i == Yball_2 ? j + 8 :
10926 i == Yball_blank ? j + 1 :
10927 i == Ykey_1_blank ? j + 1 :
10928 i == Ykey_2_blank ? j + 1 :
10929 i == Ykey_3_blank ? j + 1 :
10930 i == Ykey_4_blank ? j + 1 :
10931 i == Ykey_5_blank ? j + 1 :
10932 i == Ykey_6_blank ? j + 1 :
10933 i == Ykey_7_blank ? j + 1 :
10934 i == Ykey_8_blank ? j + 1 :
10935 i == Ylenses_blank ? j + 1 :
10936 i == Ymagnify_blank ? j + 1 :
10937 i == Ygrass_blank ? j + 1 :
10938 i == Ydirt_blank ? j + 1 :
10939 i == Xamoeba_1 ? 0 :
10940 i == Xamoeba_2 ? 1 :
10941 i == Xamoeba_3 ? 2 :
10942 i == Xamoeba_4 ? 3 :
10943 i == Xamoeba_5 ? 0 :
10944 i == Xamoeba_6 ? 1 :
10945 i == Xamoeba_7 ? 2 :
10946 i == Xamoeba_8 ? 3 :
10947 i == Xexit_2 ? j + 8 :
10948 i == Xexit_3 ? j + 16 :
10949 i == Xdynamite_1 ? 0 :
10950 i == Xdynamite_2 ? 8 :
10951 i == Xdynamite_3 ? 16 :
10952 i == Xdynamite_4 ? 24 :
10953 i == Xsand_stonein_1 ? j + 1 :
10954 i == Xsand_stonein_2 ? j + 9 :
10955 i == Xsand_stonein_3 ? j + 17 :
10956 i == Xsand_stonein_4 ? j + 25 :
10957 i == Xsand_stoneout_1 && j == 0 ? 0 :
10958 i == Xsand_stoneout_1 && j == 1 ? 0 :
10959 i == Xsand_stoneout_1 && j == 2 ? 1 :
10960 i == Xsand_stoneout_1 && j == 3 ? 2 :
10961 i == Xsand_stoneout_1 && j == 4 ? 2 :
10962 i == Xsand_stoneout_1 && j == 5 ? 3 :
10963 i == Xsand_stoneout_1 && j == 6 ? 4 :
10964 i == Xsand_stoneout_1 && j == 7 ? 4 :
10965 i == Xsand_stoneout_2 && j == 0 ? 5 :
10966 i == Xsand_stoneout_2 && j == 1 ? 6 :
10967 i == Xsand_stoneout_2 && j == 2 ? 7 :
10968 i == Xsand_stoneout_2 && j == 3 ? 8 :
10969 i == Xsand_stoneout_2 && j == 4 ? 9 :
10970 i == Xsand_stoneout_2 && j == 5 ? 11 :
10971 i == Xsand_stoneout_2 && j == 6 ? 13 :
10972 i == Xsand_stoneout_2 && j == 7 ? 15 :
10973 i == Xboom_bug && j == 1 ? 2 :
10974 i == Xboom_bug && j == 2 ? 2 :
10975 i == Xboom_bug && j == 3 ? 4 :
10976 i == Xboom_bug && j == 4 ? 4 :
10977 i == Xboom_bug && j == 5 ? 2 :
10978 i == Xboom_bug && j == 6 ? 2 :
10979 i == Xboom_bug && j == 7 ? 0 :
10980 i == Xboom_tank && j == 1 ? 2 :
10981 i == Xboom_tank && j == 2 ? 2 :
10982 i == Xboom_tank && j == 3 ? 4 :
10983 i == Xboom_tank && j == 4 ? 4 :
10984 i == Xboom_tank && j == 5 ? 2 :
10985 i == Xboom_tank && j == 6 ? 2 :
10986 i == Xboom_tank && j == 7 ? 0 :
10987 i == Xboom_android && j == 7 ? 6 :
10988 i == Xboom_1 && j == 1 ? 2 :
10989 i == Xboom_1 && j == 2 ? 2 :
10990 i == Xboom_1 && j == 3 ? 4 :
10991 i == Xboom_1 && j == 4 ? 4 :
10992 i == Xboom_1 && j == 5 ? 6 :
10993 i == Xboom_1 && j == 6 ? 6 :
10994 i == Xboom_1 && j == 7 ? 8 :
10995 i == Xboom_2 && j == 0 ? 8 :
10996 i == Xboom_2 && j == 1 ? 8 :
10997 i == Xboom_2 && j == 2 ? 10 :
10998 i == Xboom_2 && j == 3 ? 10 :
10999 i == Xboom_2 && j == 4 ? 10 :
11000 i == Xboom_2 && j == 5 ? 12 :
11001 i == Xboom_2 && j == 6 ? 12 :
11002 i == Xboom_2 && j == 7 ? 12 :
11003 special_animation && j == 4 ? 3 :
11004 effective_action != action ? 0 :
11006 int frame = getAnimationFrame(g->anim_frames,
11009 g->anim_start_frame,
11012 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
11013 g->double_movement && is_backside);
11015 g_em->bitmap = src_bitmap;
11016 g_em->src_x = src_x;
11017 g_em->src_y = src_y;
11018 g_em->src_offset_x = 0;
11019 g_em->src_offset_y = 0;
11020 g_em->dst_offset_x = 0;
11021 g_em->dst_offset_y = 0;
11022 g_em->width = TILEX;
11023 g_em->height = TILEY;
11025 g_em->preserve_background = FALSE;
11027 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
11030 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
11031 effective_action == ACTION_MOVING ||
11032 effective_action == ACTION_PUSHING ||
11033 effective_action == ACTION_EATING)) ||
11034 (!has_action_graphics && (effective_action == ACTION_FILLING ||
11035 effective_action == ACTION_EMPTYING)))
11038 (effective_action == ACTION_FALLING ||
11039 effective_action == ACTION_FILLING ||
11040 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
11041 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
11042 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
11043 int num_steps = (i == Ydrip_1_s ? 16 :
11044 i == Ydrip_1_sB ? 16 :
11045 i == Ydrip_2_s ? 16 :
11046 i == Ydrip_2_sB ? 16 :
11047 i == Xsand_stonein_1 ? 32 :
11048 i == Xsand_stonein_2 ? 32 :
11049 i == Xsand_stonein_3 ? 32 :
11050 i == Xsand_stonein_4 ? 32 :
11051 i == Xsand_stoneout_1 ? 16 :
11052 i == Xsand_stoneout_2 ? 16 : 8);
11053 int cx = ABS(dx) * (TILEX / num_steps);
11054 int cy = ABS(dy) * (TILEY / num_steps);
11055 int step_frame = (i == Ydrip_2_s ? j + 8 :
11056 i == Ydrip_2_sB ? j + 8 :
11057 i == Xsand_stonein_2 ? j + 8 :
11058 i == Xsand_stonein_3 ? j + 16 :
11059 i == Xsand_stonein_4 ? j + 24 :
11060 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
11061 int step = (is_backside ? step_frame : num_steps - step_frame);
11063 if (is_backside) // tile where movement starts
11065 if (dx < 0 || dy < 0)
11067 g_em->src_offset_x = cx * step;
11068 g_em->src_offset_y = cy * step;
11072 g_em->dst_offset_x = cx * step;
11073 g_em->dst_offset_y = cy * step;
11076 else // tile where movement ends
11078 if (dx < 0 || dy < 0)
11080 g_em->dst_offset_x = cx * step;
11081 g_em->dst_offset_y = cy * step;
11085 g_em->src_offset_x = cx * step;
11086 g_em->src_offset_y = cy * step;
11090 g_em->width = TILEX - cx * step;
11091 g_em->height = TILEY - cy * step;
11094 // create unique graphic identifier to decide if tile must be redrawn
11095 /* bit 31 - 16 (16 bit): EM style graphic
11096 bit 15 - 12 ( 4 bit): EM style frame
11097 bit 11 - 6 ( 6 bit): graphic width
11098 bit 5 - 0 ( 6 bit): graphic height */
11099 g_em->unique_identifier =
11100 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
11104 for (i = 0; i < GAME_TILE_MAX; i++)
11106 for (j = 0; j < 8; j++)
11108 int element = em_object_mapping[i].element_rnd;
11109 int action = em_object_mapping[i].action;
11110 int direction = em_object_mapping[i].direction;
11111 boolean is_backside = em_object_mapping[i].is_backside;
11112 int graphic_action = el_act_dir2img(element, action, direction);
11113 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
11115 if ((action == ACTION_SMASHED_BY_ROCK ||
11116 action == ACTION_SMASHED_BY_SPRING ||
11117 action == ACTION_EATING) &&
11118 graphic_action == graphic_default)
11120 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
11121 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
11122 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
11123 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
11126 // no separate animation for "smashed by rock" -- use rock instead
11127 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
11128 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
11130 g_em->bitmap = g_xx->bitmap;
11131 g_em->src_x = g_xx->src_x;
11132 g_em->src_y = g_xx->src_y;
11133 g_em->src_offset_x = g_xx->src_offset_x;
11134 g_em->src_offset_y = g_xx->src_offset_y;
11135 g_em->dst_offset_x = g_xx->dst_offset_x;
11136 g_em->dst_offset_y = g_xx->dst_offset_y;
11137 g_em->width = g_xx->width;
11138 g_em->height = g_xx->height;
11139 g_em->unique_identifier = g_xx->unique_identifier;
11142 g_em->preserve_background = TRUE;
11147 for (p = 0; p < MAX_PLAYERS; p++)
11149 for (i = 0; i < PLY_MAX; i++)
11151 int element = em_player_mapping[p][i].element_rnd;
11152 int action = em_player_mapping[p][i].action;
11153 int direction = em_player_mapping[p][i].direction;
11155 for (j = 0; j < 8; j++)
11157 int effective_element = element;
11158 int effective_action = action;
11159 int graphic = (direction == MV_NONE ?
11160 el_act2img(effective_element, effective_action) :
11161 el_act_dir2img(effective_element, effective_action,
11163 struct GraphicInfo *g = &graphic_info[graphic];
11164 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
11165 Bitmap *src_bitmap;
11167 int sync_frame = j;
11168 int frame = getAnimationFrame(g->anim_frames,
11171 g->anim_start_frame,
11174 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
11176 g_em->bitmap = src_bitmap;
11177 g_em->src_x = src_x;
11178 g_em->src_y = src_y;
11179 g_em->src_offset_x = 0;
11180 g_em->src_offset_y = 0;
11181 g_em->dst_offset_x = 0;
11182 g_em->dst_offset_y = 0;
11183 g_em->width = TILEX;
11184 g_em->height = TILEY;
11190 static void CheckSaveEngineSnapshot_EM(int frame,
11191 boolean any_player_moving,
11192 boolean any_player_snapping,
11193 boolean any_player_dropping)
11195 if (frame == 7 && !any_player_dropping)
11197 if (!local_player->was_waiting)
11199 if (!CheckSaveEngineSnapshotToList())
11202 local_player->was_waiting = TRUE;
11205 else if (any_player_moving || any_player_snapping || any_player_dropping)
11207 local_player->was_waiting = FALSE;
11211 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
11212 boolean murphy_is_dropping)
11214 if (murphy_is_waiting)
11216 if (!local_player->was_waiting)
11218 if (!CheckSaveEngineSnapshotToList())
11221 local_player->was_waiting = TRUE;
11226 local_player->was_waiting = FALSE;
11230 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
11231 boolean button_released)
11233 if (button_released)
11235 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
11236 CheckSaveEngineSnapshotToList();
11238 else if (element_clicked)
11240 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
11241 CheckSaveEngineSnapshotToList();
11243 game.snapshot.changed_action = TRUE;
11247 boolean CheckSingleStepMode_EM(int frame,
11248 boolean any_player_moving,
11249 boolean any_player_snapping,
11250 boolean any_player_dropping)
11252 if (tape.single_step && tape.recording && !tape.pausing)
11253 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
11254 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11256 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
11257 any_player_snapping, any_player_dropping);
11259 return tape.pausing;
11262 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
11263 boolean murphy_is_dropping)
11265 boolean murphy_starts_dropping = FALSE;
11268 for (i = 0; i < MAX_PLAYERS; i++)
11269 if (stored_player[i].force_dropping)
11270 murphy_starts_dropping = TRUE;
11272 if (tape.single_step && tape.recording && !tape.pausing)
11273 if (murphy_is_waiting && !murphy_starts_dropping)
11274 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11276 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
11279 void CheckSingleStepMode_MM(boolean element_clicked,
11280 boolean button_released)
11282 if (tape.single_step && tape.recording && !tape.pausing)
11283 if (button_released)
11284 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11286 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
11289 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
11290 int graphic, int sync_frame)
11292 int frame = getGraphicAnimationFrame(graphic, sync_frame);
11294 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
11297 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
11299 return (IS_NEXT_FRAME(sync_frame, graphic));
11302 int getGraphicInfo_Delay(int graphic)
11304 return graphic_info[graphic].anim_delay;
11307 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
11309 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
11312 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
11318 void PlayMenuSoundExt(int sound)
11320 if (sound == SND_UNDEFINED)
11323 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
11324 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
11327 if (IS_LOOP_SOUND(sound))
11328 PlaySoundLoop(sound);
11333 void PlayMenuSound(void)
11335 PlayMenuSoundExt(menu.sound[game_status]);
11338 void PlayMenuSoundStereo(int sound, int stereo_position)
11340 if (sound == SND_UNDEFINED)
11343 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
11344 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
11347 if (IS_LOOP_SOUND(sound))
11348 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
11350 PlaySoundStereo(sound, stereo_position);
11353 void PlayMenuSoundIfLoopExt(int sound)
11355 if (sound == SND_UNDEFINED)
11358 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
11359 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
11362 if (IS_LOOP_SOUND(sound))
11363 PlaySoundLoop(sound);
11366 void PlayMenuSoundIfLoop(void)
11368 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
11371 void PlayMenuMusicExt(int music)
11373 if (music == MUS_UNDEFINED)
11376 if (!setup.sound_music)
11379 if (IS_LOOP_MUSIC(music))
11380 PlayMusicLoop(music);
11385 void PlayMenuMusic(void)
11387 char *curr_music = getCurrentlyPlayingMusicFilename();
11388 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
11390 if (!strEqual(curr_music, next_music))
11391 PlayMenuMusicExt(menu.music[game_status]);
11394 void PlayMenuSoundsAndMusic(void)
11400 static void FadeMenuSounds(void)
11405 static void FadeMenuMusic(void)
11407 char *curr_music = getCurrentlyPlayingMusicFilename();
11408 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
11410 if (!strEqual(curr_music, next_music))
11414 void FadeMenuSoundsAndMusic(void)
11420 void PlaySoundActivating(void)
11423 PlaySound(SND_MENU_ITEM_ACTIVATING);
11427 void PlaySoundSelecting(void)
11430 PlaySound(SND_MENU_ITEM_SELECTING);
11434 void ToggleFullscreenIfNeeded(void)
11436 // if setup and video fullscreen state are already matching, nothing do do
11437 if (setup.fullscreen == video.fullscreen_enabled ||
11438 !video.fullscreen_available)
11441 SDLSetWindowFullscreen(setup.fullscreen);
11443 // set setup value according to successfully changed fullscreen mode
11444 setup.fullscreen = video.fullscreen_enabled;
11447 void ChangeWindowScalingIfNeeded(void)
11449 // if setup and video window scaling are already matching, nothing do do
11450 if (setup.window_scaling_percent == video.window_scaling_percent ||
11451 video.fullscreen_enabled)
11454 SDLSetWindowScaling(setup.window_scaling_percent);
11456 // set setup value according to successfully changed window scaling
11457 setup.window_scaling_percent = video.window_scaling_percent;
11460 void ChangeVsyncModeIfNeeded(void)
11462 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
11463 int video_vsync_mode = video.vsync_mode;
11465 // if setup and video vsync mode are already matching, nothing do do
11466 if (setup_vsync_mode == video_vsync_mode)
11469 // if renderer is using OpenGL, vsync mode can directly be changed
11470 SDLSetScreenVsyncMode(setup.vsync_mode);
11472 // if vsync mode unchanged, try re-creating renderer to set vsync mode
11473 if (video.vsync_mode == video_vsync_mode)
11475 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
11477 // save backbuffer content which gets lost when re-creating screen
11478 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
11480 // force re-creating screen and renderer to set new vsync mode
11481 video.fullscreen_enabled = !setup.fullscreen;
11483 // when creating new renderer, destroy textures linked to old renderer
11484 FreeAllImageTextures(); // needs old renderer to free the textures
11486 // re-create screen and renderer (including change of vsync mode)
11487 ChangeVideoModeIfNeeded(setup.fullscreen);
11489 // set setup value according to successfully changed fullscreen mode
11490 setup.fullscreen = video.fullscreen_enabled;
11492 // restore backbuffer content from temporary backbuffer backup bitmap
11493 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
11494 FreeBitmap(tmp_backbuffer);
11496 // update visible window/screen
11497 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
11499 // when changing vsync mode, re-create textures for new renderer
11500 InitImageTextures();
11503 // set setup value according to successfully changed vsync mode
11504 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
11507 static void JoinRectangles(int *x, int *y, int *width, int *height,
11508 int x2, int y2, int width2, int height2)
11510 // do not join with "off-screen" rectangle
11511 if (x2 == -1 || y2 == -1)
11516 *width = MAX(*width, width2);
11517 *height = MAX(*height, height2);
11520 void SetAnimStatus(int anim_status_new)
11522 if (anim_status_new == GAME_MODE_MAIN)
11523 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
11524 else if (anim_status_new == GAME_MODE_NAMES)
11525 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
11526 else if (anim_status_new == GAME_MODE_SCORES)
11527 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
11529 global.anim_status_next = anim_status_new;
11531 // directly set screen modes that are entered without fading
11532 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
11533 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
11534 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
11535 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
11536 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
11537 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
11538 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
11539 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
11540 global.anim_status = global.anim_status_next;
11543 void SetGameStatus(int game_status_new)
11545 if (game_status_new != game_status)
11546 game_status_last_screen = game_status;
11548 game_status = game_status_new;
11550 SetAnimStatus(game_status_new);
11553 void SetFontStatus(int game_status_new)
11555 static int last_game_status = -1;
11557 if (game_status_new != -1)
11559 // set game status for font use after storing last game status
11560 last_game_status = game_status;
11561 game_status = game_status_new;
11565 // reset game status after font use from last stored game status
11566 game_status = last_game_status;
11570 void ResetFontStatus(void)
11575 void SetLevelSetInfo(char *identifier, int level_nr)
11577 setString(&levelset.identifier, identifier);
11579 levelset.level_nr = level_nr;
11582 boolean CheckIfAllViewportsHaveChanged(void)
11584 // if game status has not changed, viewports have not changed either
11585 if (game_status == game_status_last)
11588 // check if all viewports have changed with current game status
11590 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
11591 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
11592 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
11593 int new_real_sx = vp_playfield->x;
11594 int new_real_sy = vp_playfield->y;
11595 int new_full_sxsize = vp_playfield->width;
11596 int new_full_sysize = vp_playfield->height;
11597 int new_dx = vp_door_1->x;
11598 int new_dy = vp_door_1->y;
11599 int new_dxsize = vp_door_1->width;
11600 int new_dysize = vp_door_1->height;
11601 int new_vx = vp_door_2->x;
11602 int new_vy = vp_door_2->y;
11603 int new_vxsize = vp_door_2->width;
11604 int new_vysize = vp_door_2->height;
11606 boolean playfield_viewport_has_changed =
11607 (new_real_sx != REAL_SX ||
11608 new_real_sy != REAL_SY ||
11609 new_full_sxsize != FULL_SXSIZE ||
11610 new_full_sysize != FULL_SYSIZE);
11612 boolean door_1_viewport_has_changed =
11615 new_dxsize != DXSIZE ||
11616 new_dysize != DYSIZE);
11618 boolean door_2_viewport_has_changed =
11621 new_vxsize != VXSIZE ||
11622 new_vysize != VYSIZE ||
11623 game_status_last == GAME_MODE_EDITOR);
11625 return (playfield_viewport_has_changed &&
11626 door_1_viewport_has_changed &&
11627 door_2_viewport_has_changed);
11630 boolean CheckFadeAll(void)
11632 return (CheckIfGlobalBorderHasChanged() ||
11633 CheckIfAllViewportsHaveChanged());
11636 void ChangeViewportPropertiesIfNeeded(void)
11638 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
11639 FALSE : setup.small_game_graphics);
11640 int gfx_game_mode = getGlobalGameStatus(game_status);
11641 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
11643 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
11644 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
11645 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
11646 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
11647 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
11648 int new_win_xsize = vp_window->width;
11649 int new_win_ysize = vp_window->height;
11650 int border_left = vp_playfield->border_left;
11651 int border_right = vp_playfield->border_right;
11652 int border_top = vp_playfield->border_top;
11653 int border_bottom = vp_playfield->border_bottom;
11654 int new_sx = vp_playfield->x + border_left;
11655 int new_sy = vp_playfield->y + border_top;
11656 int new_sxsize = vp_playfield->width - border_left - border_right;
11657 int new_sysize = vp_playfield->height - border_top - border_bottom;
11658 int new_real_sx = vp_playfield->x;
11659 int new_real_sy = vp_playfield->y;
11660 int new_full_sxsize = vp_playfield->width;
11661 int new_full_sysize = vp_playfield->height;
11662 int new_dx = vp_door_1->x;
11663 int new_dy = vp_door_1->y;
11664 int new_dxsize = vp_door_1->width;
11665 int new_dysize = vp_door_1->height;
11666 int new_vx = vp_door_2->x;
11667 int new_vy = vp_door_2->y;
11668 int new_vxsize = vp_door_2->width;
11669 int new_vysize = vp_door_2->height;
11670 int new_ex = vp_door_3->x;
11671 int new_ey = vp_door_3->y;
11672 int new_exsize = vp_door_3->width;
11673 int new_eysize = vp_door_3->height;
11674 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
11675 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
11676 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
11677 int new_scr_fieldx = new_sxsize / tilesize;
11678 int new_scr_fieldy = new_sysize / tilesize;
11679 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
11680 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
11681 boolean init_gfx_buffers = FALSE;
11682 boolean init_video_buffer = FALSE;
11683 boolean init_gadgets_and_anims = FALSE;
11684 boolean init_bd_graphics = FALSE;
11685 boolean init_em_graphics = FALSE;
11687 if (new_win_xsize != WIN_XSIZE ||
11688 new_win_ysize != WIN_YSIZE)
11690 WIN_XSIZE = new_win_xsize;
11691 WIN_YSIZE = new_win_ysize;
11693 init_video_buffer = TRUE;
11694 init_gfx_buffers = TRUE;
11695 init_gadgets_and_anims = TRUE;
11697 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
11700 if (new_scr_fieldx != SCR_FIELDX ||
11701 new_scr_fieldy != SCR_FIELDY)
11703 // this always toggles between MAIN and GAME when using small tile size
11705 SCR_FIELDX = new_scr_fieldx;
11706 SCR_FIELDY = new_scr_fieldy;
11708 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
11711 if (new_sx != SX ||
11719 new_sxsize != SXSIZE ||
11720 new_sysize != SYSIZE ||
11721 new_dxsize != DXSIZE ||
11722 new_dysize != DYSIZE ||
11723 new_vxsize != VXSIZE ||
11724 new_vysize != VYSIZE ||
11725 new_exsize != EXSIZE ||
11726 new_eysize != EYSIZE ||
11727 new_real_sx != REAL_SX ||
11728 new_real_sy != REAL_SY ||
11729 new_full_sxsize != FULL_SXSIZE ||
11730 new_full_sysize != FULL_SYSIZE ||
11731 new_tilesize_var != TILESIZE_VAR
11734 // ------------------------------------------------------------------------
11735 // determine next fading area for changed viewport definitions
11736 // ------------------------------------------------------------------------
11738 // start with current playfield area (default fading area)
11741 FADE_SXSIZE = FULL_SXSIZE;
11742 FADE_SYSIZE = FULL_SYSIZE;
11744 // add new playfield area if position or size has changed
11745 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
11746 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
11748 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11749 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
11752 // add current and new door 1 area if position or size has changed
11753 if (new_dx != DX || new_dy != DY ||
11754 new_dxsize != DXSIZE || new_dysize != DYSIZE)
11756 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11757 DX, DY, DXSIZE, DYSIZE);
11758 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11759 new_dx, new_dy, new_dxsize, new_dysize);
11762 // add current and new door 2 area if position or size has changed
11763 if (new_vx != VX || new_vy != VY ||
11764 new_vxsize != VXSIZE || new_vysize != VYSIZE)
11766 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11767 VX, VY, VXSIZE, VYSIZE);
11768 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
11769 new_vx, new_vy, new_vxsize, new_vysize);
11772 // ------------------------------------------------------------------------
11773 // handle changed tile size
11774 // ------------------------------------------------------------------------
11776 if (new_tilesize_var != TILESIZE_VAR)
11778 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
11780 // changing tile size invalidates scroll values of engine snapshots
11781 FreeEngineSnapshotSingle();
11783 // changing tile size requires update of graphic mapping for BD/EM engine
11784 init_bd_graphics = TRUE;
11785 init_em_graphics = TRUE;
11796 SXSIZE = new_sxsize;
11797 SYSIZE = new_sysize;
11798 DXSIZE = new_dxsize;
11799 DYSIZE = new_dysize;
11800 VXSIZE = new_vxsize;
11801 VYSIZE = new_vysize;
11802 EXSIZE = new_exsize;
11803 EYSIZE = new_eysize;
11804 REAL_SX = new_real_sx;
11805 REAL_SY = new_real_sy;
11806 FULL_SXSIZE = new_full_sxsize;
11807 FULL_SYSIZE = new_full_sysize;
11808 TILESIZE_VAR = new_tilesize_var;
11810 init_gfx_buffers = TRUE;
11811 init_gadgets_and_anims = TRUE;
11813 // Debug("tools:viewport", "viewports: init_gfx_buffers");
11814 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
11817 if (init_gfx_buffers)
11819 // Debug("tools:viewport", "init_gfx_buffers");
11821 SCR_FIELDX = new_scr_fieldx_buffers;
11822 SCR_FIELDY = new_scr_fieldy_buffers;
11826 SCR_FIELDX = new_scr_fieldx;
11827 SCR_FIELDY = new_scr_fieldy;
11829 SetDrawDeactivationMask(REDRAW_NONE);
11830 SetDrawBackgroundMask(REDRAW_FIELD);
11833 if (init_video_buffer)
11835 // Debug("tools:viewport", "init_video_buffer");
11837 FreeAllImageTextures(); // needs old renderer to free the textures
11839 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
11840 InitImageTextures();
11843 if (init_gadgets_and_anims)
11845 // Debug("tools:viewport", "init_gadgets_and_anims");
11848 InitGlobalAnimations();
11851 if (init_bd_graphics)
11853 InitGraphicInfo_BD();
11856 if (init_em_graphics)
11858 InitGraphicInfo_EM();
11862 void OpenURL(char *url)
11864 #if SDL_VERSION_ATLEAST(2,0,14)
11867 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
11868 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
11869 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
11873 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
11875 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
11878 char *getCurrentLevelsetName(void)
11880 return leveldir_current->name;
11884 // ============================================================================
11886 // ============================================================================
11888 #if defined(PLATFORM_WINDOWS)
11889 /* FILETIME of Jan 1 1970 00:00:00. */
11890 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
11893 * timezone information is stored outside the kernel so tzp isn't used anymore.
11895 * Note: this function is not for Win32 high precision timing purpose. See
11898 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
11900 FILETIME file_time;
11901 SYSTEMTIME system_time;
11902 ULARGE_INTEGER ularge;
11904 GetSystemTime(&system_time);
11905 SystemTimeToFileTime(&system_time, &file_time);
11906 ularge.LowPart = file_time.dwLowDateTime;
11907 ularge.HighPart = file_time.dwHighDateTime;
11909 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
11910 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
11916 static char *test_init_uuid_random_function_simple(void)
11918 static char seed_text[100];
11919 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
11921 sprintf(seed_text, "%d", seed);
11926 static char *test_init_uuid_random_function_better(void)
11928 static char seed_text[100];
11929 struct timeval current_time;
11931 gettimeofday(¤t_time, NULL);
11933 prng_seed_bytes(¤t_time, sizeof(current_time));
11935 sprintf(seed_text, "%ld.%ld",
11936 (long)current_time.tv_sec,
11937 (long)current_time.tv_usec);
11942 #if defined(PLATFORM_WINDOWS)
11943 static char *test_init_uuid_random_function_better_windows(void)
11945 static char seed_text[100];
11946 struct timeval current_time;
11948 gettimeofday_windows(¤t_time, NULL);
11950 prng_seed_bytes(¤t_time, sizeof(current_time));
11952 sprintf(seed_text, "%ld.%ld",
11953 (long)current_time.tv_sec,
11954 (long)current_time.tv_usec);
11960 static unsigned int test_uuid_random_function_simple(int max)
11962 return GetSimpleRandom(max);
11965 static unsigned int test_uuid_random_function_better(int max)
11967 return (max > 0 ? prng_get_uint() % max : 0);
11970 #if defined(PLATFORM_WINDOWS)
11971 #define NUM_UUID_TESTS 3
11973 #define NUM_UUID_TESTS 2
11976 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
11978 HashTable *hash_seeds =
11979 create_hashtable(get_hash_from_string, hash_key_strings_are_equal, free, NULL);
11980 HashTable *hash_uuids =
11981 create_hashtable(get_hash_from_string, hash_key_strings_are_equal, free, NULL);
11982 static char message[100];
11985 char *random_name = (nr == 0 ? "simple" : "better");
11986 char *random_type = (always_seed ? "always" : "only once");
11987 char *(*init_random_function)(void) =
11989 test_init_uuid_random_function_simple :
11990 test_init_uuid_random_function_better);
11991 unsigned int (*random_function)(int) =
11993 test_uuid_random_function_simple :
11994 test_uuid_random_function_better);
11997 #if defined(PLATFORM_WINDOWS)
12000 random_name = "windows";
12001 init_random_function = test_init_uuid_random_function_better_windows;
12007 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
12008 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
12010 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
12011 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
12012 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
12014 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
12018 // always initialize random number generator at least once
12019 init_random_function();
12021 unsigned int time_start = SDL_GetTicks();
12023 for (i = 0; i < num_uuids; i++)
12027 char *seed = getStringCopy(init_random_function());
12029 hashtable_remove(hash_seeds, seed);
12030 hashtable_insert(hash_seeds, seed, "1");
12033 char *uuid = getStringCopy(getUUIDExt(random_function));
12035 hashtable_remove(hash_uuids, uuid);
12036 hashtable_insert(hash_uuids, uuid, "1");
12039 int num_unique_seeds = hashtable_count(hash_seeds);
12040 int num_unique_uuids = hashtable_count(hash_uuids);
12042 unsigned int time_needed = SDL_GetTicks() - time_start;
12044 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
12046 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
12049 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
12051 if (nr == NUM_UUID_TESTS - 1 && always_seed)
12052 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
12054 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
12056 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
12058 Request(message, REQ_CONFIRM);
12060 hashtable_destroy(hash_seeds);
12061 hashtable_destroy(hash_uuids);
12064 void TestGeneratingUUIDs(void)
12066 int num_uuids = 1000000;
12069 for (i = 0; i < NUM_UUID_TESTS; i++)
12070 for (j = 0; j < 2; j++)
12071 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
12073 CloseAllAndExit(0);