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 UnmapToolButtons(void);
179 static void HandleToolButtons(struct GadgetInfo *);
180 static int el_act_dir2crm(int, int, int);
181 static int el_act2crm(int, int);
183 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
184 static int request_gadget_id = -1;
186 static char *print_if_not_empty(int element)
188 static char *s = NULL;
189 char *token_name = element_info[element].token_name;
194 s = checked_malloc(strlen(token_name) + 10 + 1);
196 if (element != EL_EMPTY)
197 sprintf(s, "%d\t['%s']", element, token_name);
199 sprintf(s, "%d", element);
204 int getFieldbufferOffsetX_RND(int dir, int pos)
206 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
207 int dx = (dir & MV_HORIZONTAL ? pos : 0);
208 int dx_var = dx * TILESIZE_VAR / TILESIZE;
211 if (EVEN(SCR_FIELDX))
213 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
214 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
216 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
217 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
219 fx += (dx_var > 0 ? TILEX_VAR : 0);
226 if (full_lev_fieldx <= SCR_FIELDX)
228 if (EVEN(SCR_FIELDX))
229 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
231 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
237 int getFieldbufferOffsetY_RND(int dir, int pos)
239 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
240 int dy = (dir & MV_VERTICAL ? pos : 0);
241 int dy_var = dy * TILESIZE_VAR / TILESIZE;
244 if (EVEN(SCR_FIELDY))
246 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
247 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
249 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
250 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
252 fy += (dy_var > 0 ? TILEY_VAR : 0);
259 if (full_lev_fieldy <= SCR_FIELDY)
261 if (EVEN(SCR_FIELDY))
262 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
264 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
270 static int getLevelFromScreenX_RND(int sx)
272 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
275 int lx = LEVELX((px + dx) / TILESIZE_VAR);
280 static int getLevelFromScreenY_RND(int sy)
282 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
285 int ly = LEVELY((py + dy) / TILESIZE_VAR);
290 static int getLevelFromScreenX_EM(int sx)
292 int level_xsize = level.native_em_level->cav->width;
293 int full_xsize = level_xsize * TILESIZE_VAR;
295 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
297 int fx = getFieldbufferOffsetX_EM();
300 int lx = LEVELX((px + dx) / TILESIZE_VAR);
305 static int getLevelFromScreenY_EM(int sy)
307 int level_ysize = level.native_em_level->cav->height;
308 int full_ysize = level_ysize * TILESIZE_VAR;
310 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
312 int fy = getFieldbufferOffsetY_EM();
315 int ly = LEVELY((py + dy) / TILESIZE_VAR);
320 static int getLevelFromScreenX_SP(int sx)
322 int menBorder = setup.sp_show_border_elements;
323 int level_xsize = level.native_sp_level->width;
324 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
326 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
328 int fx = getFieldbufferOffsetX_SP();
331 int lx = LEVELX((px + dx) / TILESIZE_VAR);
336 static int getLevelFromScreenY_SP(int sy)
338 int menBorder = setup.sp_show_border_elements;
339 int level_ysize = level.native_sp_level->height;
340 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
342 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
344 int fy = getFieldbufferOffsetY_SP();
347 int ly = LEVELY((py + dy) / TILESIZE_VAR);
352 static int getLevelFromScreenX_MM(int sx)
354 int level_xsize = level.native_mm_level->fieldx;
355 int full_xsize = level_xsize * TILESIZE_VAR;
357 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
360 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
365 static int getLevelFromScreenY_MM(int sy)
367 int level_ysize = level.native_mm_level->fieldy;
368 int full_ysize = level_ysize * TILESIZE_VAR;
370 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
373 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
378 int getLevelFromScreenX(int x)
380 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
381 return getLevelFromScreenX_EM(x);
382 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
383 return getLevelFromScreenX_SP(x);
384 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
385 return getLevelFromScreenX_MM(x);
387 return getLevelFromScreenX_RND(x);
390 int getLevelFromScreenY(int y)
392 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
393 return getLevelFromScreenY_EM(y);
394 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
395 return getLevelFromScreenY_SP(y);
396 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
397 return getLevelFromScreenY_MM(y);
399 return getLevelFromScreenY_RND(y);
402 int getScreenFieldSizeX(void)
404 return (tape.playing ? tape.scr_fieldx : SCR_FIELDX);
407 int getScreenFieldSizeY(void)
409 return (tape.playing ? tape.scr_fieldy : SCR_FIELDY);
412 void DumpTile(int x, int y)
419 Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
422 if (!IN_LEV_FIELD(x, y))
424 Info("(not in level field)");
430 token_name = element_info[Tile[x][y]].token_name;
432 Info("Tile: %d\t['%s']", Tile[x][y], token_name);
433 Info("Back: %s", print_if_not_empty(Back[x][y]));
434 Info("Store: %s", print_if_not_empty(Store[x][y]));
435 Info("Store2: %s", print_if_not_empty(Store2[x][y]));
436 Info("StorePlayer: %s", print_if_not_empty(StorePlayer[x][y]));
437 Info("MovPos: %d", MovPos[x][y]);
438 Info("MovDir: %d", MovDir[x][y]);
439 Info("MovDelay: %d", MovDelay[x][y]);
440 Info("ChangeDelay: %d", ChangeDelay[x][y]);
441 Info("CustomValue: %d", CustomValue[x][y]);
442 Info("GfxElement: %d", GfxElement[x][y]);
443 Info("GfxAction: %d", GfxAction[x][y]);
444 Info("GfxFrame: %d [%d]", GfxFrame[x][y], FrameCounter);
445 Info("Player x/y: %d, %d", local_player->jx, local_player->jy);
449 void DumpTileFromScreen(int sx, int sy)
451 int lx = getLevelFromScreenX(sx);
452 int ly = getLevelFromScreenY(sy);
457 void SetDrawtoField(int mode)
459 if (mode == DRAW_TO_FIELDBUFFER)
465 BX2 = SCR_FIELDX + 1;
466 BY2 = SCR_FIELDY + 1;
468 drawto_field = fieldbuffer;
470 else // DRAW_TO_BACKBUFFER
476 BX2 = SCR_FIELDX - 1;
477 BY2 = SCR_FIELDY - 1;
479 drawto_field = backbuffer;
483 int GetDrawtoField(void)
485 return (drawto_field == fieldbuffer ? DRAW_TO_FIELDBUFFER : DRAW_TO_BACKBUFFER);
488 static void RedrawPlayfield_RND(void)
490 if (game.envelope_active)
493 DrawLevel(REDRAW_ALL);
497 void RedrawPlayfield(void)
499 if (game_status != GAME_MODE_PLAYING)
502 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
503 RedrawPlayfield_EM(TRUE);
504 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
505 RedrawPlayfield_SP(TRUE);
506 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
507 RedrawPlayfield_MM();
508 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
509 RedrawPlayfield_RND();
511 BlitScreenToBitmap(backbuffer);
513 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
517 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
520 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
521 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
523 // may happen for "border.draw_masked.*" with undefined "global.border.*"
524 if (src_bitmap == NULL)
527 if (x == -1 && y == -1)
530 if (draw_target == DRAW_TO_SCREEN)
531 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
533 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
536 static void DrawMaskedBorderExt_FIELD(int draw_target)
538 if (global.border_status >= GAME_MODE_MAIN &&
539 global.border_status <= GAME_MODE_PLAYING &&
540 border.draw_masked[global.border_status])
541 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
545 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
547 // when drawing to backbuffer, never draw border over open doors
548 if (draw_target == DRAW_TO_BACKBUFFER &&
549 (GetDoorState() & DOOR_OPEN_1))
552 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
553 (global.border_status != GAME_MODE_EDITOR ||
554 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
555 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
558 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
560 // when drawing to backbuffer, never draw border over open doors
561 if (draw_target == DRAW_TO_BACKBUFFER &&
562 (GetDoorState() & DOOR_OPEN_2))
565 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
566 global.border_status != GAME_MODE_EDITOR)
567 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
570 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
572 // currently not available
575 static void DrawMaskedBorderExt_ALL(int draw_target)
577 DrawMaskedBorderExt_FIELD(draw_target);
578 DrawMaskedBorderExt_DOOR_1(draw_target);
579 DrawMaskedBorderExt_DOOR_2(draw_target);
580 DrawMaskedBorderExt_DOOR_3(draw_target);
583 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
585 // never draw masked screen borders on borderless screens
586 if (global.border_status == GAME_MODE_LOADING ||
587 global.border_status == GAME_MODE_TITLE)
590 if (redraw_mask & REDRAW_ALL)
591 DrawMaskedBorderExt_ALL(draw_target);
594 if (redraw_mask & REDRAW_FIELD)
595 DrawMaskedBorderExt_FIELD(draw_target);
596 if (redraw_mask & REDRAW_DOOR_1)
597 DrawMaskedBorderExt_DOOR_1(draw_target);
598 if (redraw_mask & REDRAW_DOOR_2)
599 DrawMaskedBorderExt_DOOR_2(draw_target);
600 if (redraw_mask & REDRAW_DOOR_3)
601 DrawMaskedBorderExt_DOOR_3(draw_target);
605 void DrawMaskedBorder_FIELD(void)
607 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
610 void DrawMaskedBorder(int redraw_mask)
612 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
615 void DrawMaskedBorderToTarget(int draw_target)
617 if (draw_target == DRAW_TO_BACKBUFFER ||
618 draw_target == DRAW_TO_SCREEN)
620 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
624 int last_border_status = global.border_status;
626 if (draw_target == DRAW_TO_FADE_SOURCE)
628 global.border_status = gfx.fade_border_source_status;
629 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
631 else if (draw_target == DRAW_TO_FADE_TARGET)
633 global.border_status = gfx.fade_border_target_status;
634 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
637 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
639 global.border_status = last_border_status;
640 gfx.masked_border_bitmap_ptr = backbuffer;
644 void DrawTileCursor(int draw_target)
646 DrawTileCursor_MM(draw_target, game_status == GAME_MODE_PLAYING);
649 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
651 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
654 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
656 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
657 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
659 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
662 void BlitScreenToBitmap(Bitmap *target_bitmap)
664 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
665 BlitScreenToBitmap_EM(target_bitmap);
666 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
667 BlitScreenToBitmap_SP(target_bitmap);
668 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
669 BlitScreenToBitmap_MM(target_bitmap);
670 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
671 BlitScreenToBitmap_RND(target_bitmap);
673 redraw_mask |= REDRAW_FIELD;
676 static void DrawFramesPerSecond(void)
679 int font_nr = FONT_TEXT_2;
680 int font_width = getFontWidth(font_nr);
681 int draw_deactivation_mask = GetDrawDeactivationMask();
682 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
684 // draw FPS with leading space (needed if field buffer deactivated)
685 sprintf(text, " %04.1f fps", global.frames_per_second);
687 // override draw deactivation mask (required for invisible warp mode)
688 SetDrawDeactivationMask(REDRAW_NONE);
690 // draw opaque FPS if field buffer deactivated, else draw masked FPS
691 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
692 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
694 // set draw deactivation mask to previous value
695 SetDrawDeactivationMask(draw_deactivation_mask);
697 // force full-screen redraw in this frame
698 redraw_mask = REDRAW_ALL;
702 static void PrintFrameTimeDebugging(void)
704 static unsigned int last_counter = 0;
705 unsigned int counter = Counter();
706 int diff_1 = counter - last_counter;
707 int diff_2 = diff_1 - GAME_FRAME_DELAY;
709 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
710 char diff_bar[2 * diff_2_max + 5];
714 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
716 for (i = 0; i < diff_2_max; i++)
717 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
718 i >= diff_2_max - diff_2_cut ? '-' : ' ');
720 diff_bar[pos++] = '|';
722 for (i = 0; i < diff_2_max; i++)
723 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
725 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
727 diff_bar[pos++] = '\0';
729 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
732 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
735 last_counter = counter;
739 static int unifiedRedrawMask(int mask)
741 if (mask & REDRAW_ALL)
744 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
750 static boolean equalRedrawMasks(int mask_1, int mask_2)
752 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
755 void BackToFront(void)
757 static int last_redraw_mask = REDRAW_NONE;
759 // force screen redraw in every frame to continue drawing global animations
760 // (but always use the last redraw mask to prevent unwanted side effects)
761 if (redraw_mask == REDRAW_NONE)
762 redraw_mask = last_redraw_mask;
764 last_redraw_mask = redraw_mask;
767 // masked border now drawn immediately when blitting backbuffer to window
769 // draw masked border to all viewports, if defined
770 DrawMaskedBorder(redraw_mask);
773 // draw frames per second (only if debug mode is enabled)
774 if (redraw_mask & REDRAW_FPS)
775 DrawFramesPerSecond();
777 // remove playfield redraw before potentially merging with doors redraw
778 if (DrawingDeactivated(REAL_SX, REAL_SY))
779 redraw_mask &= ~REDRAW_FIELD;
781 // redraw complete window if both playfield and (some) doors need redraw
782 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
783 redraw_mask = REDRAW_ALL;
785 /* although redrawing the whole window would be fine for normal gameplay,
786 being able to only redraw the playfield is required for deactivating
787 certain drawing areas (mainly playfield) to work, which is needed for
788 warp-forward to be fast enough (by skipping redraw of most frames) */
790 if (redraw_mask & REDRAW_ALL)
792 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
794 else if (redraw_mask & REDRAW_FIELD)
796 BlitBitmap(backbuffer, window,
797 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
799 else if (redraw_mask & REDRAW_DOORS)
801 // merge door areas to prevent calling screen redraw more than once
807 if (redraw_mask & REDRAW_DOOR_1)
811 x2 = MAX(x2, DX + DXSIZE);
812 y2 = MAX(y2, DY + DYSIZE);
815 if (redraw_mask & REDRAW_DOOR_2)
819 x2 = MAX(x2, VX + VXSIZE);
820 y2 = MAX(y2, VY + VYSIZE);
823 if (redraw_mask & REDRAW_DOOR_3)
827 x2 = MAX(x2, EX + EXSIZE);
828 y2 = MAX(y2, EY + EYSIZE);
831 // make sure that at least one pixel is blitted, and inside the screen
832 // (else nothing is blitted, causing the animations not to be updated)
833 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
834 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
835 x2 = MIN(MAX(1, x2), WIN_XSIZE);
836 y2 = MIN(MAX(1, y2), WIN_YSIZE);
838 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
841 redraw_mask = REDRAW_NONE;
844 PrintFrameTimeDebugging();
848 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
850 unsigned int frame_delay_value_old = GetVideoFrameDelay();
852 SetVideoFrameDelay(frame_delay_value);
856 SetVideoFrameDelay(frame_delay_value_old);
859 static int fade_type_skip = FADE_TYPE_NONE;
861 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
863 void (*draw_border_function)(void) = NULL;
864 int x, y, width, height;
865 int fade_delay, post_delay;
867 if (fade_type == FADE_TYPE_FADE_OUT)
869 if (fade_type_skip != FADE_TYPE_NONE)
871 // skip all fade operations until specified fade operation
872 if (fade_type & fade_type_skip)
873 fade_type_skip = FADE_TYPE_NONE;
878 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
882 redraw_mask |= fade_mask;
884 if (fade_type == FADE_TYPE_SKIP)
886 fade_type_skip = fade_mode;
891 fade_delay = fading.fade_delay;
892 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
894 if (fade_type_skip != FADE_TYPE_NONE)
896 // skip all fade operations until specified fade operation
897 if (fade_type & fade_type_skip)
898 fade_type_skip = FADE_TYPE_NONE;
903 if (global.autoplay_leveldir)
908 if (fade_mask == REDRAW_FIELD)
913 height = FADE_SYSIZE;
915 if (border.draw_masked_when_fading)
916 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
918 DrawMaskedBorder_FIELD(); // draw once
928 // when switching screens without fading, set fade delay to zero
929 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
932 // do not display black frame when fading out without fade delay
933 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
936 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
937 draw_border_function);
939 redraw_mask &= ~fade_mask;
941 ClearAutoRepeatKeyEvents();
944 static void SetScreenStates_BeforeFadingIn(void)
946 // temporarily set screen mode for animations to screen after fading in
947 global.anim_status = global.anim_status_next;
949 // store backbuffer with all animations that will be started after fading in
950 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
952 // set screen mode for animations back to fading
953 global.anim_status = GAME_MODE_PSEUDO_FADING;
956 static void SetScreenStates_AfterFadingIn(void)
958 // store new source screen (to use correct masked border for fading)
959 gfx.fade_border_source_status = global.border_status;
961 global.anim_status = global.anim_status_next;
964 static void SetScreenStates_BeforeFadingOut(void)
966 // store new target screen (to use correct masked border for fading)
967 gfx.fade_border_target_status = game_status;
969 // set screen mode for animations to fading
970 global.anim_status = GAME_MODE_PSEUDO_FADING;
972 // store backbuffer with all animations that will be stopped for fading out
973 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
976 static void SetScreenStates_AfterFadingOut(void)
978 global.border_status = game_status;
981 void FadeIn(int fade_mask)
983 SetScreenStates_BeforeFadingIn();
986 DrawMaskedBorder(REDRAW_ALL);
989 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
990 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
992 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
996 FADE_SXSIZE = FULL_SXSIZE;
997 FADE_SYSIZE = FULL_SYSIZE;
999 // activate virtual buttons depending on upcoming game status
1000 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1001 game_status == GAME_MODE_PLAYING && !tape.playing)
1002 SetOverlayActive(TRUE);
1004 SetScreenStates_AfterFadingIn();
1006 // force update of global animation status in case of rapid screen changes
1007 redraw_mask = REDRAW_ALL;
1011 void FadeOut(int fade_mask)
1013 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1014 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1015 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1018 SetScreenStates_BeforeFadingOut();
1020 SetTileCursorActive(FALSE);
1021 SetOverlayActive(FALSE);
1024 DrawMaskedBorder(REDRAW_ALL);
1027 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1028 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1030 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1032 SetScreenStates_AfterFadingOut();
1035 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1037 static struct TitleFadingInfo fading_leave_stored;
1040 fading_leave_stored = fading_leave;
1042 fading = fading_leave_stored;
1045 void FadeSetEnterMenu(void)
1047 fading = menu.enter_menu;
1049 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1052 void FadeSetLeaveMenu(void)
1054 fading = menu.leave_menu;
1056 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1059 void FadeSetEnterScreen(void)
1061 fading = menu.enter_screen[game_status];
1063 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1066 void FadeSetNextScreen(void)
1068 fading = menu.next_screen[game_status];
1070 // (do not overwrite fade mode set by FadeSetEnterScreen)
1071 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1074 void FadeSetLeaveScreen(void)
1076 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1079 void FadeSetFromType(int type)
1081 if (type & TYPE_ENTER_SCREEN)
1082 FadeSetEnterScreen();
1083 else if (type & TYPE_ENTER)
1085 else if (type & TYPE_LEAVE)
1089 void FadeSetDisabled(void)
1091 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1093 fading = fading_none;
1096 void FadeSkipNextFadeIn(void)
1098 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1101 void FadeSkipNextFadeOut(void)
1103 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1106 static int getGlobalGameStatus(int status)
1108 return (status == GAME_MODE_PSEUDO_TYPENAME ? GAME_MODE_MAIN :
1109 status == GAME_MODE_SCOREINFO ? GAME_MODE_SCORES :
1113 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1115 if (graphic == IMG_UNDEFINED)
1118 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1120 return (graphic_info[graphic].bitmap != NULL || redefined ?
1121 graphic_info[graphic].bitmap :
1122 graphic_info[default_graphic].bitmap);
1125 static Bitmap *getBackgroundBitmap(int graphic)
1127 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1130 static Bitmap *getGlobalBorderBitmap(int graphic)
1132 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1135 Bitmap *getGlobalBorderBitmapFromStatus(int status_raw)
1137 int status = getGlobalGameStatus(status_raw);
1139 (status == GAME_MODE_MAIN ? IMG_GLOBAL_BORDER_MAIN :
1140 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1141 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1142 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1145 return getGlobalBorderBitmap(graphic);
1148 void SetWindowBackgroundImageIfDefined(int graphic)
1150 if (graphic_info[graphic].bitmap)
1151 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1154 void SetMainBackgroundImageIfDefined(int graphic)
1156 if (graphic_info[graphic].bitmap)
1157 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1160 void SetDoorBackgroundImageIfDefined(int graphic)
1162 if (graphic_info[graphic].bitmap)
1163 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1166 void SetWindowBackgroundImage(int graphic)
1168 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1171 void SetMainBackgroundImage(int graphic)
1173 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1176 void SetDoorBackgroundImage(int graphic)
1178 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1181 void SetPanelBackground(void)
1183 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1185 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1186 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1188 SetDoorBackgroundBitmap(bitmap_db_panel);
1191 void DrawBackground(int x, int y, int width, int height)
1193 // "drawto" might still point to playfield buffer here (hall of fame)
1194 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1196 if (IN_GFX_FIELD_FULL(x, y))
1197 redraw_mask |= REDRAW_FIELD;
1198 else if (IN_GFX_DOOR_1(x, y))
1199 redraw_mask |= REDRAW_DOOR_1;
1200 else if (IN_GFX_DOOR_2(x, y))
1201 redraw_mask |= REDRAW_DOOR_2;
1202 else if (IN_GFX_DOOR_3(x, y))
1203 redraw_mask |= REDRAW_DOOR_3;
1206 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1208 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1210 if (font->bitmap == NULL)
1213 DrawBackground(x, y, width, height);
1216 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1218 struct GraphicInfo *g = &graphic_info[graphic];
1220 if (g->bitmap == NULL)
1223 DrawBackground(x, y, width, height);
1226 static int game_status_last = -1;
1227 static Bitmap *global_border_bitmap_last = NULL;
1228 static Bitmap *global_border_bitmap = NULL;
1229 static int real_sx_last = -1, real_sy_last = -1;
1230 static int full_sxsize_last = -1, full_sysize_last = -1;
1231 static int dx_last = -1, dy_last = -1;
1232 static int dxsize_last = -1, dysize_last = -1;
1233 static int vx_last = -1, vy_last = -1;
1234 static int vxsize_last = -1, vysize_last = -1;
1235 static int ex_last = -1, ey_last = -1;
1236 static int exsize_last = -1, eysize_last = -1;
1238 boolean CheckIfGlobalBorderHasChanged(void)
1240 // if game status has not changed, global border has not changed either
1241 if (game_status == game_status_last)
1244 // determine and store new global border bitmap for current game status
1245 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1247 return (global_border_bitmap_last != global_border_bitmap);
1250 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1252 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1253 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1255 // if game status has not changed, nothing has to be redrawn
1256 if (game_status == game_status_last)
1259 // redraw if last screen was title screen
1260 if (game_status_last == GAME_MODE_TITLE)
1263 // redraw if global screen border has changed
1264 if (CheckIfGlobalBorderHasChanged())
1267 // redraw if position or size of playfield area has changed
1268 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1269 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1272 // redraw if position or size of door area has changed
1273 if (dx_last != DX || dy_last != DY ||
1274 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1277 // redraw if position or size of tape area has changed
1278 if (vx_last != VX || vy_last != VY ||
1279 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1282 // redraw if position or size of editor area has changed
1283 if (ex_last != EX || ey_last != EY ||
1284 exsize_last != EXSIZE || eysize_last != EYSIZE)
1291 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1294 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1296 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1299 void RedrawGlobalBorder(void)
1301 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1303 RedrawGlobalBorderFromBitmap(bitmap);
1305 redraw_mask = REDRAW_ALL;
1308 static void RedrawGlobalBorderIfNeeded(void)
1310 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1311 if (game_status == game_status_last)
1315 // copy current draw buffer to later copy back areas that have not changed
1316 if (game_status_last != GAME_MODE_TITLE)
1317 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1319 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1320 if (CheckIfGlobalBorderRedrawIsNeeded())
1322 // determine and store new global border bitmap for current game status
1323 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1326 // redraw global screen border (or clear, if defined to be empty)
1327 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1329 if (game_status == GAME_MODE_EDITOR)
1330 DrawSpecialEditorDoor();
1332 // copy previous playfield and door areas, if they are defined on both
1333 // previous and current screen and if they still have the same size
1335 if (real_sx_last != -1 && real_sy_last != -1 &&
1336 REAL_SX != -1 && REAL_SY != -1 &&
1337 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1338 BlitBitmap(bitmap_db_store_1, backbuffer,
1339 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1342 if (dx_last != -1 && dy_last != -1 &&
1343 DX != -1 && DY != -1 &&
1344 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1345 BlitBitmap(bitmap_db_store_1, backbuffer,
1346 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1348 if (game_status != GAME_MODE_EDITOR)
1350 if (vx_last != -1 && vy_last != -1 &&
1351 VX != -1 && VY != -1 &&
1352 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1353 BlitBitmap(bitmap_db_store_1, backbuffer,
1354 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1358 if (ex_last != -1 && ey_last != -1 &&
1359 EX != -1 && EY != -1 &&
1360 exsize_last == EXSIZE && eysize_last == EYSIZE)
1361 BlitBitmap(bitmap_db_store_1, backbuffer,
1362 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1365 redraw_mask = REDRAW_ALL;
1368 game_status_last = game_status;
1370 global_border_bitmap_last = global_border_bitmap;
1372 real_sx_last = REAL_SX;
1373 real_sy_last = REAL_SY;
1374 full_sxsize_last = FULL_SXSIZE;
1375 full_sysize_last = FULL_SYSIZE;
1378 dxsize_last = DXSIZE;
1379 dysize_last = DYSIZE;
1382 vxsize_last = VXSIZE;
1383 vysize_last = VYSIZE;
1386 exsize_last = EXSIZE;
1387 eysize_last = EYSIZE;
1390 void ClearField(void)
1392 RedrawGlobalBorderIfNeeded();
1394 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1395 // (when entering hall of fame after playing)
1396 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1398 // !!! maybe this should be done before clearing the background !!!
1399 if (game_status == GAME_MODE_PLAYING)
1401 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1402 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1406 SetDrawtoField(DRAW_TO_BACKBUFFER);
1410 void MarkTileDirty(int x, int y)
1412 redraw_mask |= REDRAW_FIELD;
1415 void SetBorderElement(void)
1419 BorderElement = EL_EMPTY;
1421 // only the R'n'D game engine may use an additional steelwall border
1422 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1425 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1427 for (x = 0; x < lev_fieldx; x++)
1429 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1430 BorderElement = EL_STEELWALL;
1432 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1438 void FloodFillLevelExt(int start_x, int start_y, int fill_element,
1439 int max_array_fieldx, int max_array_fieldy,
1440 short field[max_array_fieldx][max_array_fieldy],
1441 int max_fieldx, int max_fieldy)
1443 static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY];
1444 struct XY *check = xy_topdown;
1445 int old_element = field[start_x][start_y];
1448 // do nothing if start field already has the desired content
1449 if (old_element == fill_element)
1452 stack_buffer[stack_pos++] = (struct XY){ start_x, start_y };
1454 while (stack_pos > 0)
1456 struct XY current = stack_buffer[--stack_pos];
1459 field[current.x][current.y] = fill_element;
1461 for (i = 0; i < 4; i++)
1463 int x = current.x + check[i].x;
1464 int y = current.y + check[i].y;
1466 // check for stack buffer overflow (should not happen)
1467 if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY)
1468 Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug.");
1470 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1471 stack_buffer[stack_pos++] = (struct XY){ x, y };
1476 void FloodFillLevel(int from_x, int from_y, int fill_element,
1477 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1478 int max_fieldx, int max_fieldy)
1480 FloodFillLevelExt(from_x, from_y, fill_element,
1481 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1482 max_fieldx, max_fieldy);
1485 void SetRandomAnimationValue(int x, int y)
1487 gfx.anim_random_frame = GfxRandom[x][y];
1490 int getGraphicAnimationFrame(int graphic, int sync_frame)
1492 // animation synchronized with global frame counter, not move position
1493 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1494 sync_frame = FrameCounter;
1495 else if (graphic_info[graphic].anim_global_anim_sync)
1496 sync_frame = getGlobalAnimSyncFrame();
1498 return getAnimationFrame(graphic_info[graphic].anim_frames,
1499 graphic_info[graphic].anim_delay,
1500 graphic_info[graphic].anim_mode,
1501 graphic_info[graphic].anim_start_frame,
1505 int getGraphicAnimationFrameXY(int graphic, int lx, int ly)
1507 if (graphic_info[graphic].anim_mode & ANIM_TILED)
1509 struct GraphicInfo *g = &graphic_info[graphic];
1510 int xsize = MAX(1, g->anim_frames_per_line);
1511 int ysize = MAX(1, g->anim_frames / xsize);
1512 int xoffset = g->anim_start_frame % xsize;
1513 int yoffset = g->anim_start_frame % ysize;
1514 // may be needed if screen field is significantly larger than playfield
1515 int x = (lx + xoffset + SCR_FIELDX * xsize) % xsize;
1516 int y = (ly + yoffset + SCR_FIELDY * ysize) % ysize;
1517 int sync_frame = y * xsize + x;
1519 return sync_frame % g->anim_frames;
1521 else if (graphic_info[graphic].anim_mode & ANIM_RANDOM_STATIC)
1523 struct GraphicInfo *g = &graphic_info[graphic];
1524 // may be needed if screen field is significantly larger than playfield
1525 int x = (lx + SCR_FIELDX * lev_fieldx) % lev_fieldx;
1526 int y = (ly + SCR_FIELDY * lev_fieldy) % lev_fieldy;
1527 int sync_frame = GfxRandomStatic[x][y];
1529 return sync_frame % g->anim_frames;
1533 int sync_frame = (IN_LEV_FIELD(lx, ly) ? GfxFrame[lx][ly] : -1);
1535 return getGraphicAnimationFrame(graphic, sync_frame);
1539 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1541 struct GraphicInfo *g = &graphic_info[graphic];
1542 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1544 if (tilesize == gfx.standard_tile_size)
1545 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1546 else if (tilesize == game.tile_size)
1547 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1549 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1552 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1553 boolean get_backside)
1555 struct GraphicInfo *g = &graphic_info[graphic];
1556 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1557 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1559 if (g->offset_y == 0) // frames are ordered horizontally
1561 int max_width = g->anim_frames_per_line * g->width;
1562 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1564 *x = pos % max_width;
1565 *y = src_y % g->height + pos / max_width * g->height;
1567 else if (g->offset_x == 0) // frames are ordered vertically
1569 int max_height = g->anim_frames_per_line * g->height;
1570 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1572 *x = src_x % g->width + pos / max_height * g->width;
1573 *y = pos % max_height;
1575 else // frames are ordered diagonally
1577 *x = src_x + frame * g->offset_x;
1578 *y = src_y + frame * g->offset_y;
1582 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1583 Bitmap **bitmap, int *x, int *y,
1584 boolean get_backside)
1586 struct GraphicInfo *g = &graphic_info[graphic];
1588 // if no graphics defined at all, use fallback graphics
1589 if (g->bitmaps == NULL)
1590 *g = graphic_info[IMG_CHAR_EXCLAM];
1592 // if no in-game graphics defined, always use standard graphic size
1593 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1594 tilesize = TILESIZE;
1596 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1597 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1599 *x = *x * tilesize / g->tile_size;
1600 *y = *y * tilesize / g->tile_size;
1603 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1604 Bitmap **bitmap, int *x, int *y)
1606 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1609 void getFixedGraphicSource(int graphic, int frame,
1610 Bitmap **bitmap, int *x, int *y)
1612 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1615 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1617 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1620 void getGlobalAnimGraphicSource(int graphic, int frame,
1621 Bitmap **bitmap, int *x, int *y)
1623 struct GraphicInfo *g = &graphic_info[graphic];
1625 // if no graphics defined at all, use fallback graphics
1626 if (g->bitmaps == NULL)
1627 *g = graphic_info[IMG_CHAR_EXCLAM];
1629 // use original size graphics, if existing, else use standard size graphics
1630 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1631 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1633 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1635 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1638 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1639 int *x, int *y, boolean get_backside)
1641 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1645 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1647 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1650 void DrawGraphic(int x, int y, int graphic, int frame)
1653 if (!IN_SCR_FIELD(x, y))
1655 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1656 Debug("draw:DrawGraphic", "This should never happen!");
1662 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1665 MarkTileDirty(x, y);
1668 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1671 if (!IN_SCR_FIELD(x, y))
1673 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1675 Debug("draw:DrawFixedGraphic", "This should never happen!");
1681 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1683 MarkTileDirty(x, y);
1686 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1692 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1694 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1697 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1703 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1704 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1707 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1710 if (!IN_SCR_FIELD(x, y))
1712 Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1714 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1720 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1723 MarkTileDirty(x, y);
1726 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1729 if (!IN_SCR_FIELD(x, y))
1731 Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1733 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1739 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1741 MarkTileDirty(x, y);
1744 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1750 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1752 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1756 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1757 int graphic, int frame)
1762 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1764 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1768 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1770 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1772 MarkTileDirty(x / tilesize, y / tilesize);
1775 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1778 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1779 graphic, frame, tilesize);
1780 MarkTileDirty(x / tilesize, y / tilesize);
1783 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1789 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1790 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1793 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1794 int frame, int tilesize)
1799 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1800 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1803 void DrawMiniGraphic(int x, int y, int graphic)
1805 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1806 MarkTileDirty(x / 2, y / 2);
1809 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1814 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1815 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1818 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1819 int graphic, int frame,
1820 int cut_mode, int mask_mode)
1825 int width = TILEX, height = TILEY;
1828 if (dx || dy) // shifted graphic
1830 if (x < BX1) // object enters playfield from the left
1837 else if (x > BX2) // object enters playfield from the right
1843 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1849 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1851 else if (dx) // general horizontal movement
1852 MarkTileDirty(x + SIGN(dx), y);
1854 if (y < BY1) // object enters playfield from the top
1856 if (cut_mode == CUT_BELOW) // object completely above top border
1864 else if (y > BY2) // object enters playfield from the bottom
1870 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1876 else if (dy > 0 && cut_mode == CUT_ABOVE)
1878 if (y == BY2) // object completely above bottom border
1884 MarkTileDirty(x, y + 1);
1885 } // object leaves playfield to the bottom
1886 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1888 else if (dy) // general vertical movement
1889 MarkTileDirty(x, y + SIGN(dy));
1893 if (!IN_SCR_FIELD(x, y))
1895 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1897 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1903 width = width * TILESIZE_VAR / TILESIZE;
1904 height = height * TILESIZE_VAR / TILESIZE;
1905 cx = cx * TILESIZE_VAR / TILESIZE;
1906 cy = cy * TILESIZE_VAR / TILESIZE;
1907 dx = dx * TILESIZE_VAR / TILESIZE;
1908 dy = dy * TILESIZE_VAR / TILESIZE;
1910 if (width > 0 && height > 0)
1912 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1917 dst_x = FX + x * TILEX_VAR + dx;
1918 dst_y = FY + y * TILEY_VAR + dy;
1920 if (mask_mode == USE_MASKING)
1921 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1924 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1927 MarkTileDirty(x, y);
1931 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1932 int graphic, int frame,
1933 int cut_mode, int mask_mode)
1938 int width = TILEX_VAR, height = TILEY_VAR;
1941 int x2 = x + SIGN(dx);
1942 int y2 = y + SIGN(dy);
1944 // movement with two-tile animations must be sync'ed with movement position,
1945 // not with current GfxFrame (which can be higher when using slow movement)
1946 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1947 int anim_frames = graphic_info[graphic].anim_frames;
1949 // (we also need anim_delay here for movement animations with less frames)
1950 int anim_delay = graphic_info[graphic].anim_delay;
1951 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1953 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1954 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1956 // re-calculate animation frame for two-tile movement animation
1957 frame = getGraphicAnimationFrame(graphic, sync_frame);
1959 // check if movement start graphic inside screen area and should be drawn
1960 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1962 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1964 dst_x = FX + x1 * TILEX_VAR;
1965 dst_y = FY + y1 * TILEY_VAR;
1967 if (mask_mode == USE_MASKING)
1968 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1971 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1974 MarkTileDirty(x1, y1);
1977 // check if movement end graphic inside screen area and should be drawn
1978 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1980 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1982 dst_x = FX + x2 * TILEX_VAR;
1983 dst_y = FY + y2 * TILEY_VAR;
1985 if (mask_mode == USE_MASKING)
1986 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1989 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1992 MarkTileDirty(x2, y2);
1996 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1997 int graphic, int frame,
1998 int cut_mode, int mask_mode)
2002 DrawGraphic(x, y, graphic, frame);
2007 if (graphic_info[graphic].double_movement) // EM style movement images
2008 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
2010 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
2013 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
2014 int graphic, int frame, int cut_mode)
2016 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
2019 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
2020 int cut_mode, int mask_mode)
2022 int lx = LEVELX(x), ly = LEVELY(y);
2026 if (IN_LEV_FIELD(lx, ly))
2028 if (element == EL_EMPTY)
2029 element = GfxElementEmpty[lx][ly];
2031 SetRandomAnimationValue(lx, ly);
2033 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2034 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2036 // do not use double (EM style) movement graphic when not moving
2037 if (graphic_info[graphic].double_movement && !dx && !dy)
2039 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2040 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2043 if (game.use_masked_elements && (dx || dy))
2044 mask_mode = USE_MASKING;
2046 else // border element
2048 graphic = el2img(element);
2049 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2052 if (element == EL_EXPANDABLE_WALL)
2054 boolean left_stopped = FALSE, right_stopped = FALSE;
2056 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2057 left_stopped = TRUE;
2058 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2059 right_stopped = TRUE;
2061 if (left_stopped && right_stopped)
2063 else if (left_stopped)
2065 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2066 frame = graphic_info[graphic].anim_frames - 1;
2068 else if (right_stopped)
2070 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2071 frame = graphic_info[graphic].anim_frames - 1;
2076 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2077 else if (mask_mode == USE_MASKING)
2078 DrawGraphicThruMask(x, y, graphic, frame);
2080 DrawGraphic(x, y, graphic, frame);
2083 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2084 int cut_mode, int mask_mode)
2086 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2087 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2088 cut_mode, mask_mode);
2091 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2094 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2097 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2100 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2103 void DrawLevelElementThruMask(int x, int y, int element)
2105 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2108 void DrawLevelFieldThruMask(int x, int y)
2110 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2113 // !!! implementation of quicksand is totally broken !!!
2114 #define IS_CRUMBLED_TILE(x, y, e) \
2115 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2116 !IS_MOVING(x, y) || \
2117 (e) == EL_QUICKSAND_EMPTYING || \
2118 (e) == EL_QUICKSAND_FAST_EMPTYING))
2120 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2125 int width, height, cx, cy;
2126 int sx = SCREENX(x), sy = SCREENY(y);
2127 int crumbled_border_size = graphic_info[graphic].border_size;
2128 int crumbled_tile_size = graphic_info[graphic].tile_size;
2129 int crumbled_border_size_var =
2130 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2133 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2135 for (i = 1; i < 4; i++)
2137 int dxx = (i & 1 ? dx : 0);
2138 int dyy = (i & 2 ? dy : 0);
2141 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2144 // check if neighbour field is of same crumble type
2145 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2146 graphic_info[graphic].class ==
2147 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2149 // return if check prevents inner corner
2150 if (same == (dxx == dx && dyy == dy))
2154 // if we reach this point, we have an inner corner
2156 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2158 width = crumbled_border_size_var;
2159 height = crumbled_border_size_var;
2160 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2161 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2163 if (game.use_masked_elements)
2165 int graphic0 = el2img(EL_EMPTY);
2166 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2167 Bitmap *src_bitmap0;
2170 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2172 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2174 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2176 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2178 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2181 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2183 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2186 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2191 int width, height, bx, by, cx, cy;
2192 int sx = SCREENX(x), sy = SCREENY(y);
2193 int crumbled_border_size = graphic_info[graphic].border_size;
2194 int crumbled_tile_size = graphic_info[graphic].tile_size;
2195 int crumbled_border_size_var =
2196 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2197 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2200 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2202 // only needed when using masked elements
2203 int graphic0 = el2img(EL_EMPTY);
2204 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2205 Bitmap *src_bitmap0;
2208 if (game.use_masked_elements)
2209 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2211 // draw simple, sloppy, non-corner-accurate crumbled border
2213 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2214 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2215 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2216 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2218 if (game.use_masked_elements)
2220 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2222 FX + sx * TILEX_VAR + cx,
2223 FY + sy * TILEY_VAR + cy);
2225 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2227 FX + sx * TILEX_VAR + cx,
2228 FY + sy * TILEY_VAR + cy);
2231 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2233 FX + sx * TILEX_VAR + cx,
2234 FY + sy * TILEY_VAR + cy);
2236 // (remaining middle border part must be at least as big as corner part)
2237 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2238 crumbled_border_size_var >= TILESIZE_VAR / 3)
2241 // correct corners of crumbled border, if needed
2243 for (i = -1; i <= 1; i += 2)
2245 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2246 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2247 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2250 // check if neighbour field is of same crumble type
2251 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2252 graphic_info[graphic].class ==
2253 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2255 // no crumbled corner, but continued crumbled border
2257 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2258 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2259 int b1 = (i == 1 ? crumbled_border_size_var :
2260 TILESIZE_VAR - 2 * crumbled_border_size_var);
2262 width = crumbled_border_size_var;
2263 height = crumbled_border_size_var;
2265 if (dir == 1 || dir == 2)
2280 if (game.use_masked_elements)
2282 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2284 FX + sx * TILEX_VAR + cx,
2285 FY + sy * TILEY_VAR + cy);
2287 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2289 FX + sx * TILEX_VAR + cx,
2290 FY + sy * TILEY_VAR + cy);
2293 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2295 FX + sx * TILEX_VAR + cx,
2296 FY + sy * TILEY_VAR + cy);
2301 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2303 int sx = SCREENX(x), sy = SCREENY(y);
2306 struct XY *xy = xy_topdown;
2308 if (!IN_LEV_FIELD(x, y))
2311 element = TILE_GFX_ELEMENT(x, y);
2313 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2315 if (!IN_SCR_FIELD(sx, sy))
2318 // crumble field borders towards direct neighbour fields
2319 for (i = 0; i < 4; i++)
2321 int xx = x + xy[i].x;
2322 int yy = y + xy[i].y;
2324 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2327 // check if neighbour field is of same crumble type
2328 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2329 graphic_info[graphic].class ==
2330 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2333 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2336 // crumble inner field corners towards corner neighbour fields
2337 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2338 graphic_info[graphic].anim_frames == 2)
2340 for (i = 0; i < 4; i++)
2342 int dx = (i & 1 ? +1 : -1);
2343 int dy = (i & 2 ? +1 : -1);
2345 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2349 MarkTileDirty(sx, sy);
2351 else // center field is not crumbled -- crumble neighbour fields
2353 // crumble field borders of direct neighbour fields
2354 for (i = 0; i < 4; i++)
2356 int xx = x + xy[i].x;
2357 int yy = y + xy[i].y;
2358 int sxx = sx + xy[i].x;
2359 int syy = sy + xy[i].y;
2361 if (!IN_LEV_FIELD(xx, yy) ||
2362 !IN_SCR_FIELD(sxx, syy))
2365 // do not crumble fields that are being digged or snapped
2366 if (Tile[xx][yy] == EL_EMPTY ||
2367 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2370 element = TILE_GFX_ELEMENT(xx, yy);
2372 if (!IS_CRUMBLED_TILE(xx, yy, element))
2375 graphic = el_act2crm(element, ACTION_DEFAULT);
2377 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2379 MarkTileDirty(sxx, syy);
2382 // crumble inner field corners of corner neighbour fields
2383 for (i = 0; i < 4; i++)
2385 int dx = (i & 1 ? +1 : -1);
2386 int dy = (i & 2 ? +1 : -1);
2392 if (!IN_LEV_FIELD(xx, yy) ||
2393 !IN_SCR_FIELD(sxx, syy))
2396 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2399 element = TILE_GFX_ELEMENT(xx, yy);
2401 if (!IS_CRUMBLED_TILE(xx, yy, element))
2404 graphic = el_act2crm(element, ACTION_DEFAULT);
2406 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2407 graphic_info[graphic].anim_frames == 2)
2408 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2410 MarkTileDirty(sxx, syy);
2415 void DrawLevelFieldCrumbled(int x, int y)
2419 if (!IN_LEV_FIELD(x, y))
2422 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2423 GfxElement[x][y] != EL_UNDEFINED &&
2424 GFX_CRUMBLED(GfxElement[x][y]))
2426 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2431 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2433 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2436 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2439 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2440 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2441 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2442 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2443 int sx = SCREENX(x), sy = SCREENY(y);
2445 DrawScreenGraphic(sx, sy, graphic1, frame1);
2446 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2449 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2451 int sx = SCREENX(x), sy = SCREENY(y);
2452 struct XY *xy = xy_topdown;
2455 // crumble direct neighbour fields (required for field borders)
2456 for (i = 0; i < 4; i++)
2458 int xx = x + xy[i].x;
2459 int yy = y + xy[i].y;
2460 int sxx = sx + xy[i].x;
2461 int syy = sy + xy[i].y;
2463 if (!IN_LEV_FIELD(xx, yy) ||
2464 !IN_SCR_FIELD(sxx, syy) ||
2465 !GFX_CRUMBLED(Tile[xx][yy]) ||
2469 DrawLevelField(xx, yy);
2472 // crumble corner neighbour fields (required for inner field corners)
2473 for (i = 0; i < 4; i++)
2475 int dx = (i & 1 ? +1 : -1);
2476 int dy = (i & 2 ? +1 : -1);
2482 if (!IN_LEV_FIELD(xx, yy) ||
2483 !IN_SCR_FIELD(sxx, syy) ||
2484 !GFX_CRUMBLED(Tile[xx][yy]) ||
2488 int element = TILE_GFX_ELEMENT(xx, yy);
2489 int graphic = el_act2crm(element, ACTION_DEFAULT);
2491 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2492 graphic_info[graphic].anim_frames == 2)
2493 DrawLevelField(xx, yy);
2497 static int getBorderElement(int x, int y)
2501 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2502 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2503 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2504 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2505 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2506 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2507 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2509 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2510 int steel_position = (x == -1 && y == -1 ? 0 :
2511 x == lev_fieldx && y == -1 ? 1 :
2512 x == -1 && y == lev_fieldy ? 2 :
2513 x == lev_fieldx && y == lev_fieldy ? 3 :
2514 x == -1 || x == lev_fieldx ? 4 :
2515 y == -1 || y == lev_fieldy ? 5 : 6);
2517 return border[steel_position][steel_type];
2520 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2522 if (game.use_masked_elements)
2524 if (graphic != el2img(EL_EMPTY))
2525 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2527 DrawGraphicThruMask(x, y, graphic, frame);
2531 DrawGraphic(x, y, graphic, frame);
2535 void DrawLevelGraphic(int x, int y, int graphic, int frame)
2537 DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2540 void DrawScreenElement(int x, int y, int element)
2542 int mask_mode = NO_MASKING;
2544 if (game.use_masked_elements)
2546 int lx = LEVELX(x), ly = LEVELY(y);
2548 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2550 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2552 mask_mode = USE_MASKING;
2556 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2557 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2560 void DrawLevelElement(int x, int y, int element)
2562 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2563 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2566 void DrawScreenField(int x, int y)
2568 int lx = LEVELX(x), ly = LEVELY(y);
2569 int element, content;
2571 if (!IN_LEV_FIELD(lx, ly))
2573 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2576 element = getBorderElement(lx, ly);
2578 DrawScreenElement(x, y, element);
2583 element = Tile[lx][ly];
2584 content = Store[lx][ly];
2586 if (IS_MOVING(lx, ly))
2588 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2589 boolean cut_mode = NO_CUTTING;
2591 if (element == EL_QUICKSAND_EMPTYING ||
2592 element == EL_QUICKSAND_FAST_EMPTYING ||
2593 element == EL_MAGIC_WALL_EMPTYING ||
2594 element == EL_BD_MAGIC_WALL_EMPTYING ||
2595 element == EL_DC_MAGIC_WALL_EMPTYING ||
2596 element == EL_AMOEBA_DROPPING)
2597 cut_mode = CUT_ABOVE;
2598 else if (element == EL_QUICKSAND_FILLING ||
2599 element == EL_QUICKSAND_FAST_FILLING ||
2600 element == EL_MAGIC_WALL_FILLING ||
2601 element == EL_BD_MAGIC_WALL_FILLING ||
2602 element == EL_DC_MAGIC_WALL_FILLING)
2603 cut_mode = CUT_BELOW;
2605 if (cut_mode == CUT_ABOVE)
2606 DrawScreenElement(x, y, element);
2608 DrawScreenElement(x, y, EL_EMPTY);
2610 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2612 int dir = MovDir[lx][ly];
2613 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2614 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2616 if (IN_SCR_FIELD(newx, newy))
2617 DrawScreenElement(newx, newy, EL_EMPTY);
2621 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2622 else if (cut_mode == NO_CUTTING)
2623 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2626 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2628 if (cut_mode == CUT_BELOW &&
2629 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2630 DrawLevelElement(lx, ly + 1, element);
2633 if (content == EL_ACID)
2635 int dir = MovDir[lx][ly];
2636 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2637 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2639 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2641 // prevent target field from being drawn again (but without masking)
2642 // (this would happen if target field is scanned after moving element)
2643 Stop[newlx][newly] = TRUE;
2646 else if (IS_BLOCKED(lx, ly))
2651 boolean cut_mode = NO_CUTTING;
2652 int element_old, content_old;
2654 Blocked2Moving(lx, ly, &oldx, &oldy);
2657 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2658 MovDir[oldx][oldy] == MV_RIGHT);
2660 element_old = Tile[oldx][oldy];
2661 content_old = Store[oldx][oldy];
2663 if (element_old == EL_QUICKSAND_EMPTYING ||
2664 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2665 element_old == EL_MAGIC_WALL_EMPTYING ||
2666 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2667 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2668 element_old == EL_AMOEBA_DROPPING)
2669 cut_mode = CUT_ABOVE;
2671 DrawScreenElement(x, y, EL_EMPTY);
2674 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2676 else if (cut_mode == NO_CUTTING)
2677 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2680 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2683 else if (IS_DRAWABLE(element))
2684 DrawScreenElement(x, y, element);
2686 DrawScreenElement(x, y, EL_EMPTY);
2689 void DrawLevelField(int x, int y)
2691 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2692 DrawScreenField(SCREENX(x), SCREENY(y));
2693 else if (IS_MOVING(x, y))
2697 Moving2Blocked(x, y, &newx, &newy);
2698 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2699 DrawScreenField(SCREENX(newx), SCREENY(newy));
2701 else if (IS_BLOCKED(x, y))
2705 Blocked2Moving(x, y, &oldx, &oldy);
2706 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2707 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2711 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2712 int (*el2img_function)(int), boolean masked,
2713 int element_bits_draw)
2715 int element_base = map_mm_wall_element(element);
2716 int element_bits = (IS_DF_WALL(element) ?
2717 element - EL_DF_WALL_START :
2718 IS_MM_WALL(element) ?
2719 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2720 int graphic = el2img_function(element_base);
2721 int tilesize_draw = tilesize / 2;
2726 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2728 for (i = 0; i < 4; i++)
2730 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2731 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2733 if (!(element_bits_draw & (1 << i)))
2736 if (element_bits & (1 << i))
2739 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2740 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2742 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2743 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2748 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2749 tilesize_draw, tilesize_draw);
2754 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2755 boolean masked, int element_bits_draw)
2757 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2758 element, tilesize, el2edimg, masked, element_bits_draw);
2761 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2762 int (*el2img_function)(int))
2764 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2768 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2771 if (IS_MM_WALL(element))
2773 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2774 element, tilesize, el2edimg, masked, 0x000f);
2778 int graphic = el2edimg(element);
2781 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2783 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2787 void DrawSizedElement(int x, int y, int element, int tilesize)
2789 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2792 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2794 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2797 void DrawMiniElement(int x, int y, int element)
2801 graphic = el2edimg(element);
2802 DrawMiniGraphic(x, y, graphic);
2805 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2808 int x = sx + scroll_x, y = sy + scroll_y;
2810 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2811 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2812 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2813 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2815 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2818 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2820 int x = sx + scroll_x, y = sy + scroll_y;
2822 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2823 DrawMiniElement(sx, sy, EL_EMPTY);
2824 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2825 DrawMiniElement(sx, sy, Tile[x][y]);
2827 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2830 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2831 int x, int y, int xsize, int ysize,
2832 int tile_width, int tile_height)
2836 int dst_x = startx + x * tile_width;
2837 int dst_y = starty + y * tile_height;
2838 int width = graphic_info[graphic].width;
2839 int height = graphic_info[graphic].height;
2840 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2841 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2842 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2843 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2844 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2845 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2846 boolean draw_masked = graphic_info[graphic].draw_masked;
2848 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2850 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2852 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2856 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2857 inner_sx + (x - 1) * tile_width % inner_width);
2858 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2859 inner_sy + (y - 1) * tile_height % inner_height);
2862 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2865 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2869 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2870 int x, int y, int xsize, int ysize,
2873 int font_width = getFontWidth(font_nr);
2874 int font_height = getFontHeight(font_nr);
2876 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2877 font_width, font_height);
2880 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2882 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2883 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2884 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2885 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2886 boolean no_delay = (tape.warp_forward);
2887 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2888 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2889 DelayCounter anim_delay = { anim_delay_value };
2890 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2891 int font_width = getFontWidth(font_nr);
2892 int font_height = getFontHeight(font_nr);
2893 int max_xsize = level.envelope[envelope_nr].xsize;
2894 int max_ysize = level.envelope[envelope_nr].ysize;
2895 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2896 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2897 int xend = max_xsize;
2898 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2899 int xstep = (xstart < xend ? 1 : 0);
2900 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2902 int end = MAX(xend - xstart, yend - ystart);
2905 for (i = start; i <= end; i++)
2907 int last_frame = end; // last frame of this "for" loop
2908 int x = xstart + i * xstep;
2909 int y = ystart + i * ystep;
2910 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2911 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2912 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2913 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2916 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2918 BlitScreenToBitmap(backbuffer);
2920 SetDrawtoField(DRAW_TO_BACKBUFFER);
2922 for (yy = 0; yy < ysize; yy++)
2923 for (xx = 0; xx < xsize; xx++)
2924 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2926 DrawTextArea(sx + font_width, sy + font_height,
2927 level.envelope[envelope_nr].text, font_nr, max_xsize,
2928 xsize - 2, ysize - 2, 0, mask_mode,
2929 level.envelope[envelope_nr].autowrap,
2930 level.envelope[envelope_nr].centered, FALSE);
2932 redraw_mask |= REDRAW_FIELD;
2935 SkipUntilDelayReached(&anim_delay, &i, last_frame);
2938 ClearAutoRepeatKeyEvents();
2941 void ShowEnvelope(int envelope_nr)
2943 int element = EL_ENVELOPE_1 + envelope_nr;
2944 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2945 int sound_opening = element_info[element].sound[ACTION_OPENING];
2946 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2947 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2948 boolean no_delay = (tape.warp_forward);
2949 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2950 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2951 int anim_mode = graphic_info[graphic].anim_mode;
2952 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2953 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2954 boolean overlay_enabled = GetOverlayEnabled();
2956 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2958 SetOverlayEnabled(FALSE);
2961 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2963 if (anim_mode == ANIM_DEFAULT)
2964 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2966 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2969 Delay_WithScreenUpdates(wait_delay_value);
2971 WaitForEventToContinue();
2974 SetOverlayEnabled(overlay_enabled);
2976 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2978 if (anim_mode != ANIM_NONE)
2979 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2981 if (anim_mode == ANIM_DEFAULT)
2982 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2984 game.envelope_active = FALSE;
2986 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2988 redraw_mask |= REDRAW_FIELD;
2992 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
2993 int xsize, int ysize)
2995 if (!global.use_envelope_request ||
2996 request.sort_priority <= 0)
2999 if (request.bitmap == NULL ||
3000 xsize > request.xsize ||
3001 ysize > request.ysize)
3003 if (request.bitmap != NULL)
3004 FreeBitmap(request.bitmap);
3006 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
3008 SDL_Surface *surface = request.bitmap->surface;
3010 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
3011 Fail("SDLGetNativeSurface() failed");
3014 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3016 SDLFreeBitmapTextures(request.bitmap);
3017 SDLCreateBitmapTextures(request.bitmap);
3019 // set envelope request run-time values
3022 request.xsize = xsize;
3023 request.ysize = ysize;
3026 void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage)
3028 if (global.use_envelope_request &&
3029 game.request_active_or_moving &&
3030 request.sort_priority > 0 &&
3031 drawing_target == DRAW_TO_SCREEN &&
3032 drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
3034 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3035 request.sx, request.sy);
3039 static void setRequestBasePosition(int *x, int *y)
3041 int sx_base, sy_base;
3043 if (request.x != -1)
3044 sx_base = request.x;
3045 else if (request.align == ALIGN_LEFT)
3047 else if (request.align == ALIGN_RIGHT)
3048 sx_base = SX + SXSIZE;
3050 sx_base = SX + SXSIZE / 2;
3052 if (request.y != -1)
3053 sy_base = request.y;
3054 else if (request.valign == VALIGN_TOP)
3056 else if (request.valign == VALIGN_BOTTOM)
3057 sy_base = SY + SYSIZE;
3059 sy_base = SY + SYSIZE / 2;
3065 static void setRequestPositionExt(int *x, int *y, int width, int height,
3066 boolean add_border_size)
3068 int border_size = request.border_size;
3069 int sx_base, sy_base;
3072 setRequestBasePosition(&sx_base, &sy_base);
3074 if (request.align == ALIGN_LEFT)
3076 else if (request.align == ALIGN_RIGHT)
3077 sx = sx_base - width;
3079 sx = sx_base - width / 2;
3081 if (request.valign == VALIGN_TOP)
3083 else if (request.valign == VALIGN_BOTTOM)
3084 sy = sy_base - height;
3086 sy = sy_base - height / 2;
3088 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3089 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3091 if (add_border_size)
3101 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3103 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3106 static void DrawEnvelopeRequest(char *text)
3108 char *text_final = text;
3109 char *text_door_style = NULL;
3110 int graphic = IMG_BACKGROUND_REQUEST;
3111 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3112 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3113 int font_nr = FONT_REQUEST;
3114 int font_width = getFontWidth(font_nr);
3115 int font_height = getFontHeight(font_nr);
3116 int border_size = request.border_size;
3117 int line_spacing = request.line_spacing;
3118 int line_height = font_height + line_spacing;
3119 int max_text_width = request.width - 2 * border_size;
3120 int max_text_height = request.height - 2 * border_size;
3121 int line_length = max_text_width / font_width;
3122 int max_lines = max_text_height / line_height;
3123 int text_width = line_length * font_width;
3124 int width = request.width;
3125 int height = request.height;
3126 int tile_size = MAX(request.step_offset, 1);
3127 int x_steps = width / tile_size;
3128 int y_steps = height / tile_size;
3129 int sx_offset = border_size;
3130 int sy_offset = border_size;
3134 if (request.centered)
3135 sx_offset = (request.width - text_width) / 2;
3137 if (request.wrap_single_words && !request.autowrap)
3139 char *src_text_ptr, *dst_text_ptr;
3141 text_door_style = checked_malloc(2 * strlen(text) + 1);
3143 src_text_ptr = text;
3144 dst_text_ptr = text_door_style;
3146 while (*src_text_ptr)
3148 if (*src_text_ptr == ' ' ||
3149 *src_text_ptr == '?' ||
3150 *src_text_ptr == '!')
3151 *dst_text_ptr++ = '\n';
3153 if (*src_text_ptr != ' ')
3154 *dst_text_ptr++ = *src_text_ptr;
3159 *dst_text_ptr = '\0';
3161 text_final = text_door_style;
3164 setRequestPosition(&sx, &sy, FALSE);
3166 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3168 for (y = 0; y < y_steps; y++)
3169 for (x = 0; x < x_steps; x++)
3170 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3171 x, y, x_steps, y_steps,
3172 tile_size, tile_size);
3174 // force DOOR font inside door area
3175 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3177 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3178 line_length, -1, max_lines, line_spacing, mask_mode,
3179 request.autowrap, request.centered, FALSE);
3183 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3184 RedrawGadget(tool_gadget[i]);
3186 // store readily prepared envelope request for later use when animating
3187 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3189 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3191 if (text_door_style)
3192 free(text_door_style);
3195 static void AnimateEnvelopeRequest(int anim_mode, int action)
3197 int graphic = IMG_BACKGROUND_REQUEST;
3198 boolean draw_masked = graphic_info[graphic].draw_masked;
3199 int delay_value_normal = request.step_delay;
3200 int delay_value_fast = delay_value_normal / 2;
3201 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3202 boolean no_delay = (tape.warp_forward);
3203 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3204 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3205 DelayCounter anim_delay = { anim_delay_value };
3207 int tile_size = MAX(request.step_offset, 1);
3208 int max_xsize = request.width / tile_size;
3209 int max_ysize = request.height / tile_size;
3210 int max_xsize_inner = max_xsize - 2;
3211 int max_ysize_inner = max_ysize - 2;
3213 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3214 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3215 int xend = max_xsize_inner;
3216 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3217 int xstep = (xstart < xend ? 1 : 0);
3218 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3220 int end = MAX(xend - xstart, yend - ystart);
3223 if (setup.quick_doors)
3230 for (i = start; i <= end; i++)
3232 int last_frame = end; // last frame of this "for" loop
3233 int x = xstart + i * xstep;
3234 int y = ystart + i * ystep;
3235 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3236 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3237 int xsize_size_left = (xsize - 1) * tile_size;
3238 int ysize_size_top = (ysize - 1) * tile_size;
3239 int max_xsize_pos = (max_xsize - 1) * tile_size;
3240 int max_ysize_pos = (max_ysize - 1) * tile_size;
3241 int width = xsize * tile_size;
3242 int height = ysize * tile_size;
3247 setRequestPosition(&src_x, &src_y, FALSE);
3248 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3250 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3252 for (yy = 0; yy < 2; yy++)
3254 for (xx = 0; xx < 2; xx++)
3256 int src_xx = src_x + xx * max_xsize_pos;
3257 int src_yy = src_y + yy * max_ysize_pos;
3258 int dst_xx = dst_x + xx * xsize_size_left;
3259 int dst_yy = dst_y + yy * ysize_size_top;
3260 int xx_size = (xx ? tile_size : xsize_size_left);
3261 int yy_size = (yy ? tile_size : ysize_size_top);
3264 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3265 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3267 BlitBitmap(bitmap_db_store_2, backbuffer,
3268 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3272 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3274 redraw_mask |= REDRAW_FIELD;
3278 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3281 ClearAutoRepeatKeyEvents();
3284 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3286 int graphic = IMG_BACKGROUND_REQUEST;
3287 int sound_opening = SND_REQUEST_OPENING;
3288 int sound_closing = SND_REQUEST_CLOSING;
3289 int anim_mode_1 = request.anim_mode; // (higher priority)
3290 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3291 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3292 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3293 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3295 if (game_status == GAME_MODE_PLAYING)
3296 BlitScreenToBitmap(backbuffer);
3298 SetDrawtoField(DRAW_TO_BACKBUFFER);
3300 // SetDrawBackgroundMask(REDRAW_NONE);
3302 if (action == ACTION_OPENING)
3304 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3306 if (req_state & REQ_ASK)
3308 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3309 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3310 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3311 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3313 else if (req_state & REQ_CONFIRM)
3315 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3316 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3318 else if (req_state & REQ_PLAYER)
3320 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3321 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3322 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3323 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3326 DrawEnvelopeRequest(text);
3329 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3331 if (action == ACTION_OPENING)
3333 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3335 if (anim_mode == ANIM_DEFAULT)
3336 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3338 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3342 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3344 if (anim_mode != ANIM_NONE)
3345 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3347 if (anim_mode == ANIM_DEFAULT)
3348 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3351 game.envelope_active = FALSE;
3353 if (action == ACTION_CLOSING)
3354 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3356 // SetDrawBackgroundMask(last_draw_background_mask);
3358 redraw_mask |= REDRAW_FIELD;
3362 if (action == ACTION_CLOSING &&
3363 game_status == GAME_MODE_PLAYING &&
3364 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3365 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3368 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3370 if (IS_MM_WALL(element))
3372 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3378 int graphic = el2preimg(element);
3380 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3381 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3386 void DrawLevel(int draw_background_mask)
3390 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3391 SetDrawBackgroundMask(draw_background_mask);
3395 for (x = BX1; x <= BX2; x++)
3396 for (y = BY1; y <= BY2; y++)
3397 DrawScreenField(x, y);
3399 redraw_mask |= REDRAW_FIELD;
3402 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3407 for (x = 0; x < size_x; x++)
3408 for (y = 0; y < size_y; y++)
3409 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3411 redraw_mask |= REDRAW_FIELD;
3414 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3418 for (x = 0; x < size_x; x++)
3419 for (y = 0; y < size_y; y++)
3420 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3422 redraw_mask |= REDRAW_FIELD;
3425 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3427 boolean show_level_border = (BorderElement != EL_EMPTY);
3428 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3429 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3430 int tile_size = preview.tile_size;
3431 int preview_width = preview.xsize * tile_size;
3432 int preview_height = preview.ysize * tile_size;
3433 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3434 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3435 int real_preview_width = real_preview_xsize * tile_size;
3436 int real_preview_height = real_preview_ysize * tile_size;
3437 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3438 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3441 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3444 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3446 dst_x += (preview_width - real_preview_width) / 2;
3447 dst_y += (preview_height - real_preview_height) / 2;
3449 for (x = 0; x < real_preview_xsize; x++)
3451 for (y = 0; y < real_preview_ysize; y++)
3453 int lx = from_x + x + (show_level_border ? -1 : 0);
3454 int ly = from_y + y + (show_level_border ? -1 : 0);
3455 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3456 getBorderElement(lx, ly));
3458 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3459 element, tile_size);
3463 redraw_mask |= REDRAW_FIELD;
3466 #define MICROLABEL_EMPTY 0
3467 #define MICROLABEL_LEVEL_NAME 1
3468 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3469 #define MICROLABEL_LEVEL_AUTHOR 3
3470 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3471 #define MICROLABEL_IMPORTED_FROM 5
3472 #define MICROLABEL_IMPORTED_BY_HEAD 6
3473 #define MICROLABEL_IMPORTED_BY 7
3475 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3477 int max_text_width = SXSIZE;
3478 int font_width = getFontWidth(font_nr);
3480 if (pos->align == ALIGN_CENTER)
3481 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3482 else if (pos->align == ALIGN_RIGHT)
3483 max_text_width = pos->x;
3485 max_text_width = SXSIZE - pos->x;
3487 return max_text_width / font_width;
3490 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3492 char label_text[MAX_OUTPUT_LINESIZE + 1];
3493 int max_len_label_text;
3494 int font_nr = pos->font;
3497 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3500 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3501 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3502 mode == MICROLABEL_IMPORTED_BY_HEAD)
3503 font_nr = pos->font_alt;
3505 max_len_label_text = getMaxTextLength(pos, font_nr);
3507 if (pos->size != -1)
3508 max_len_label_text = pos->size;
3510 for (i = 0; i < max_len_label_text; i++)
3511 label_text[i] = ' ';
3512 label_text[max_len_label_text] = '\0';
3514 if (strlen(label_text) > 0)
3515 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3518 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3519 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3520 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3521 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3522 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3523 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3524 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3525 max_len_label_text);
3526 label_text[max_len_label_text] = '\0';
3528 if (strlen(label_text) > 0)
3529 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3531 redraw_mask |= REDRAW_FIELD;
3534 static void DrawPreviewLevelLabel(int mode)
3536 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3539 static void DrawPreviewLevelInfo(int mode)
3541 if (mode == MICROLABEL_LEVEL_NAME)
3542 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3543 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3544 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3547 static void DrawPreviewLevelExt(boolean restart)
3549 static DelayCounter scroll_delay = { 0 };
3550 static DelayCounter label_delay = { 0 };
3551 static int from_x, from_y, scroll_direction;
3552 static int label_state, label_counter;
3553 boolean show_level_border = (BorderElement != EL_EMPTY);
3554 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3555 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3557 scroll_delay.value = preview.step_delay;
3558 label_delay.value = MICROLEVEL_LABEL_DELAY;
3565 if (preview.anim_mode == ANIM_CENTERED)
3567 if (level_xsize > preview.xsize)
3568 from_x = (level_xsize - preview.xsize) / 2;
3569 if (level_ysize > preview.ysize)
3570 from_y = (level_ysize - preview.ysize) / 2;
3573 from_x += preview.xoffset;
3574 from_y += preview.yoffset;
3576 scroll_direction = MV_RIGHT;
3580 DrawPreviewLevelPlayfield(from_x, from_y);
3581 DrawPreviewLevelLabel(label_state);
3583 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3584 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3586 // initialize delay counters
3587 ResetDelayCounter(&scroll_delay);
3588 ResetDelayCounter(&label_delay);
3590 if (leveldir_current->name)
3592 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3593 char label_text[MAX_OUTPUT_LINESIZE + 1];
3594 int font_nr = pos->font;
3595 int max_len_label_text = getMaxTextLength(pos, font_nr);
3597 if (pos->size != -1)
3598 max_len_label_text = pos->size;
3600 strncpy(label_text, leveldir_current->name, max_len_label_text);
3601 label_text[max_len_label_text] = '\0';
3603 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3604 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3610 // scroll preview level, if needed
3611 if (preview.anim_mode != ANIM_NONE &&
3612 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3613 DelayReached(&scroll_delay))
3615 switch (scroll_direction)
3620 from_x -= preview.step_offset;
3621 from_x = (from_x < 0 ? 0 : from_x);
3624 scroll_direction = MV_UP;
3628 if (from_x < level_xsize - preview.xsize)
3630 from_x += preview.step_offset;
3631 from_x = (from_x > level_xsize - preview.xsize ?
3632 level_xsize - preview.xsize : from_x);
3635 scroll_direction = MV_DOWN;
3641 from_y -= preview.step_offset;
3642 from_y = (from_y < 0 ? 0 : from_y);
3645 scroll_direction = MV_RIGHT;
3649 if (from_y < level_ysize - preview.ysize)
3651 from_y += preview.step_offset;
3652 from_y = (from_y > level_ysize - preview.ysize ?
3653 level_ysize - preview.ysize : from_y);
3656 scroll_direction = MV_LEFT;
3663 DrawPreviewLevelPlayfield(from_x, from_y);
3666 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3667 // redraw micro level label, if needed
3668 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3669 !strEqual(level.author, ANONYMOUS_NAME) &&
3670 !strEqual(level.author, leveldir_current->name) &&
3671 DelayReached(&label_delay))
3673 int max_label_counter = 23;
3675 if (leveldir_current->imported_from != NULL &&
3676 strlen(leveldir_current->imported_from) > 0)
3677 max_label_counter += 14;
3678 if (leveldir_current->imported_by != NULL &&
3679 strlen(leveldir_current->imported_by) > 0)
3680 max_label_counter += 14;
3682 label_counter = (label_counter + 1) % max_label_counter;
3683 label_state = (label_counter >= 0 && label_counter <= 7 ?
3684 MICROLABEL_LEVEL_NAME :
3685 label_counter >= 9 && label_counter <= 12 ?
3686 MICROLABEL_LEVEL_AUTHOR_HEAD :
3687 label_counter >= 14 && label_counter <= 21 ?
3688 MICROLABEL_LEVEL_AUTHOR :
3689 label_counter >= 23 && label_counter <= 26 ?
3690 MICROLABEL_IMPORTED_FROM_HEAD :
3691 label_counter >= 28 && label_counter <= 35 ?
3692 MICROLABEL_IMPORTED_FROM :
3693 label_counter >= 37 && label_counter <= 40 ?
3694 MICROLABEL_IMPORTED_BY_HEAD :
3695 label_counter >= 42 && label_counter <= 49 ?
3696 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3698 if (leveldir_current->imported_from == NULL &&
3699 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3700 label_state == MICROLABEL_IMPORTED_FROM))
3701 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3702 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3704 DrawPreviewLevelLabel(label_state);
3708 void DrawPreviewPlayers(void)
3710 if (game_status != GAME_MODE_MAIN)
3713 // do not draw preview players if level preview redefined, but players aren't
3714 if (preview.redefined && !menu.main.preview_players.redefined)
3717 boolean player_found[MAX_PLAYERS];
3718 int num_players = 0;
3721 for (i = 0; i < MAX_PLAYERS; i++)
3722 player_found[i] = FALSE;
3724 // check which players can be found in the level (simple approach)
3725 for (x = 0; x < lev_fieldx; x++)
3727 for (y = 0; y < lev_fieldy; y++)
3729 int element = level.field[x][y];
3731 if (IS_PLAYER_ELEMENT(element))
3733 int player_nr = GET_PLAYER_NR(element);
3735 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3737 if (!player_found[player_nr])
3740 player_found[player_nr] = TRUE;
3745 struct TextPosInfo *pos = &menu.main.preview_players;
3746 int tile_size = pos->tile_size;
3747 int border_size = pos->border_size;
3748 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3749 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3750 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3751 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3752 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3753 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3754 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3755 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3756 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3757 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3758 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3759 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3761 // clear area in which the players will be drawn
3762 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3763 max_players_width, max_players_height);
3765 if (!network.enabled && !setup.team_mode)
3768 // only draw players if level is suited for team mode
3769 if (num_players < 2)
3772 // draw all players that were found in the level
3773 for (i = 0; i < MAX_PLAYERS; i++)
3775 if (player_found[i])
3777 int graphic = el2img(EL_PLAYER_1 + i);
3779 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3781 xpos += player_xoffset;
3782 ypos += player_yoffset;
3787 void DrawPreviewLevelInitial(void)
3789 DrawPreviewLevelExt(TRUE);
3790 DrawPreviewPlayers();
3793 void DrawPreviewLevelAnimation(void)
3795 DrawPreviewLevelExt(FALSE);
3798 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3799 int border_size, int font_nr)
3801 int graphic = el2img(EL_PLAYER_1 + player_nr);
3802 int font_height = getFontHeight(font_nr);
3803 int player_height = MAX(tile_size, font_height);
3804 int xoffset_text = tile_size + border_size;
3805 int yoffset_text = (player_height - font_height) / 2;
3806 int yoffset_graphic = (player_height - tile_size) / 2;
3807 char *player_name = getNetworkPlayerName(player_nr + 1);
3809 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3811 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3814 static void DrawNetworkPlayersExt(boolean force)
3816 if (game_status != GAME_MODE_MAIN)
3819 if (!network.connected && !force)
3822 // do not draw network players if level preview redefined, but players aren't
3823 if (preview.redefined && !menu.main.network_players.redefined)
3826 int num_players = 0;
3829 for (i = 0; i < MAX_PLAYERS; i++)
3830 if (stored_player[i].connected_network)
3833 struct TextPosInfo *pos = &menu.main.network_players;
3834 int tile_size = pos->tile_size;
3835 int border_size = pos->border_size;
3836 int xoffset_text = tile_size + border_size;
3837 int font_nr = pos->font;
3838 int font_width = getFontWidth(font_nr);
3839 int font_height = getFontHeight(font_nr);
3840 int player_height = MAX(tile_size, font_height);
3841 int player_yoffset = player_height + border_size;
3842 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3843 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3844 int all_players_height = num_players * player_yoffset - border_size;
3845 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3846 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3847 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3849 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3850 max_players_width, max_players_height);
3852 // first draw local network player ...
3853 for (i = 0; i < MAX_PLAYERS; i++)
3855 if (stored_player[i].connected_network &&
3856 stored_player[i].connected_locally)
3858 char *player_name = getNetworkPlayerName(i + 1);
3859 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3860 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3862 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3864 ypos += player_yoffset;
3868 // ... then draw all other network players
3869 for (i = 0; i < MAX_PLAYERS; i++)
3871 if (stored_player[i].connected_network &&
3872 !stored_player[i].connected_locally)
3874 char *player_name = getNetworkPlayerName(i + 1);
3875 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3876 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3878 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3880 ypos += player_yoffset;
3885 void DrawNetworkPlayers(void)
3887 DrawNetworkPlayersExt(FALSE);
3890 void ClearNetworkPlayers(void)
3892 DrawNetworkPlayersExt(TRUE);
3895 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3896 int graphic, int lx, int ly,
3899 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3901 if (mask_mode == USE_MASKING)
3902 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3904 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3907 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3908 int graphic, int sync_frame, int mask_mode)
3910 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3912 if (mask_mode == USE_MASKING)
3913 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3915 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3918 static void DrawGraphicAnimation(int x, int y, int graphic)
3920 int lx = LEVELX(x), ly = LEVELY(y);
3921 int mask_mode = NO_MASKING;
3923 if (!IN_SCR_FIELD(x, y))
3926 if (game.use_masked_elements)
3928 if (Tile[lx][ly] != EL_EMPTY)
3930 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3932 mask_mode = USE_MASKING;
3936 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3937 graphic, lx, ly, mask_mode);
3939 MarkTileDirty(x, y);
3942 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3944 int lx = LEVELX(x), ly = LEVELY(y);
3945 int mask_mode = NO_MASKING;
3947 if (!IN_SCR_FIELD(x, y))
3950 if (game.use_masked_elements)
3952 if (Tile[lx][ly] != EL_EMPTY)
3954 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3956 mask_mode = USE_MASKING;
3960 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3961 graphic, lx, ly, mask_mode);
3963 MarkTileDirty(x, y);
3966 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3968 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3971 void DrawLevelElementAnimation(int x, int y, int element)
3973 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3975 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3978 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3980 int sx = SCREENX(x), sy = SCREENY(y);
3982 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3985 if (Tile[x][y] == EL_EMPTY)
3986 graphic = el2img(GfxElementEmpty[x][y]);
3988 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3991 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
3994 DrawGraphicAnimation(sx, sy, graphic);
3997 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3998 DrawLevelFieldCrumbled(x, y);
4000 if (GFX_CRUMBLED(Tile[x][y]))
4001 DrawLevelFieldCrumbled(x, y);
4005 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4007 int sx = SCREENX(x), sy = SCREENY(y);
4010 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4013 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4015 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4018 DrawGraphicAnimation(sx, sy, graphic);
4020 if (GFX_CRUMBLED(element))
4021 DrawLevelFieldCrumbled(x, y);
4024 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4026 if (player->use_murphy)
4028 // this works only because currently only one player can be "murphy" ...
4029 static int last_horizontal_dir = MV_LEFT;
4030 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4032 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4033 last_horizontal_dir = move_dir;
4035 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4037 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4039 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4045 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
4048 static boolean equalGraphics(int graphic1, int graphic2)
4050 struct GraphicInfo *g1 = &graphic_info[graphic1];
4051 struct GraphicInfo *g2 = &graphic_info[graphic2];
4053 return (g1->bitmap == g2->bitmap &&
4054 g1->src_x == g2->src_x &&
4055 g1->src_y == g2->src_y &&
4056 g1->anim_frames == g2->anim_frames &&
4057 g1->anim_delay == g2->anim_delay &&
4058 g1->anim_mode == g2->anim_mode);
4061 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4065 DRAW_PLAYER_STAGE_INIT = 0,
4066 DRAW_PLAYER_STAGE_LAST_FIELD,
4067 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4068 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4069 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4070 DRAW_PLAYER_STAGE_PLAYER,
4072 DRAW_PLAYER_STAGE_PLAYER,
4073 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4075 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4076 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4078 NUM_DRAW_PLAYER_STAGES
4081 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4083 static int static_last_player_graphic[MAX_PLAYERS];
4084 static int static_last_player_frame[MAX_PLAYERS];
4085 static boolean static_player_is_opaque[MAX_PLAYERS];
4086 static boolean draw_player[MAX_PLAYERS];
4087 int pnr = player->index_nr;
4089 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4091 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4092 static_last_player_frame[pnr] = player->Frame;
4093 static_player_is_opaque[pnr] = FALSE;
4095 draw_player[pnr] = TRUE;
4098 if (!draw_player[pnr])
4102 if (!IN_LEV_FIELD(player->jx, player->jy))
4104 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4105 Debug("draw:DrawPlayerExt", "This should never happen!");
4107 draw_player[pnr] = FALSE;
4113 int last_player_graphic = static_last_player_graphic[pnr];
4114 int last_player_frame = static_last_player_frame[pnr];
4115 boolean player_is_opaque = static_player_is_opaque[pnr];
4117 int jx = player->jx;
4118 int jy = player->jy;
4119 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4120 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4121 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4122 int last_jx = (player->is_moving ? jx - dx : jx);
4123 int last_jy = (player->is_moving ? jy - dy : jy);
4124 int next_jx = jx + dx;
4125 int next_jy = jy + dy;
4126 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4127 int sx = SCREENX(jx);
4128 int sy = SCREENY(jy);
4129 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4130 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4131 int element = Tile[jx][jy];
4132 int last_element = Tile[last_jx][last_jy];
4133 int action = (player->is_pushing ? ACTION_PUSHING :
4134 player->is_digging ? ACTION_DIGGING :
4135 player->is_collecting ? ACTION_COLLECTING :
4136 player->is_moving ? ACTION_MOVING :
4137 player->is_snapping ? ACTION_SNAPPING :
4138 player->is_dropping ? ACTION_DROPPING :
4139 player->is_waiting ? player->action_waiting :
4142 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4144 // ------------------------------------------------------------------------
4145 // initialize drawing the player
4146 // ------------------------------------------------------------------------
4148 draw_player[pnr] = FALSE;
4150 // GfxElement[][] is set to the element the player is digging or collecting;
4151 // remove also for off-screen player if the player is not moving anymore
4152 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4153 GfxElement[jx][jy] = EL_UNDEFINED;
4155 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4158 if (element == EL_EXPLOSION)
4161 InitPlayerGfxAnimation(player, action, move_dir);
4163 draw_player[pnr] = TRUE;
4165 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4167 // ------------------------------------------------------------------------
4168 // draw things in the field the player is leaving, if needed
4169 // ------------------------------------------------------------------------
4171 if (!IN_SCR_FIELD(sx, sy))
4172 draw_player[pnr] = FALSE;
4174 if (!player->is_moving)
4177 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4179 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4181 if (last_element == EL_DYNAMITE_ACTIVE ||
4182 last_element == EL_EM_DYNAMITE_ACTIVE ||
4183 last_element == EL_SP_DISK_RED_ACTIVE)
4184 DrawDynamite(last_jx, last_jy);
4186 DrawLevelFieldThruMask(last_jx, last_jy);
4188 else if (last_element == EL_DYNAMITE_ACTIVE ||
4189 last_element == EL_EM_DYNAMITE_ACTIVE ||
4190 last_element == EL_SP_DISK_RED_ACTIVE)
4191 DrawDynamite(last_jx, last_jy);
4193 DrawLevelField(last_jx, last_jy);
4195 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
4196 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4198 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4200 // ------------------------------------------------------------------------
4201 // draw things behind the player, if needed
4202 // ------------------------------------------------------------------------
4206 DrawLevelElement(jx, jy, Back[jx][jy]);
4211 if (IS_ACTIVE_BOMB(element))
4213 DrawLevelElement(jx, jy, EL_EMPTY);
4218 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4220 int old_element = GfxElement[jx][jy];
4221 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4222 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4224 if (GFX_CRUMBLED(old_element))
4225 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4227 DrawScreenGraphic(sx, sy, old_graphic, frame);
4229 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4230 static_player_is_opaque[pnr] = TRUE;
4234 GfxElement[jx][jy] = EL_UNDEFINED;
4236 // make sure that pushed elements are drawn with correct frame rate
4237 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4239 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4240 GfxFrame[jx][jy] = player->StepFrame;
4242 DrawLevelField(jx, jy);
4245 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4247 // ------------------------------------------------------------------------
4248 // draw things the player is pushing, if needed
4249 // ------------------------------------------------------------------------
4251 if (!player->is_pushing || !player->is_moving)
4254 int gfx_frame = GfxFrame[jx][jy];
4256 if (!IS_MOVING(jx, jy)) // push movement already finished
4258 element = Tile[next_jx][next_jy];
4259 gfx_frame = GfxFrame[next_jx][next_jy];
4262 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4263 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4264 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4266 // draw background element under pushed element (like the Sokoban field)
4267 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4269 // this allows transparent pushing animation over non-black background
4272 DrawLevelElement(jx, jy, Back[jx][jy]);
4274 DrawLevelElement(jx, jy, EL_EMPTY);
4276 if (Back[next_jx][next_jy])
4277 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4279 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4281 else if (Back[next_jx][next_jy])
4282 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4284 int px = SCREENX(jx), py = SCREENY(jy);
4285 int pxx = (TILEX - ABS(sxx)) * dx;
4286 int pyy = (TILEY - ABS(syy)) * dy;
4289 // do not draw (EM style) pushing animation when pushing is finished
4290 // (two-tile animations usually do not contain start and end frame)
4291 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4292 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4294 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4296 // masked drawing is needed for EMC style (double) movement graphics
4297 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4298 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4301 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4303 // ------------------------------------------------------------------------
4304 // draw player himself
4305 // ------------------------------------------------------------------------
4307 int graphic = getPlayerGraphic(player, move_dir);
4309 // in the case of changed player action or direction, prevent the current
4310 // animation frame from being restarted for identical animations
4311 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4312 player->Frame = last_player_frame;
4314 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4316 if (player_is_opaque)
4317 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4319 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4321 if (SHIELD_ON(player))
4323 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4324 IMG_SHIELD_NORMAL_ACTIVE);
4325 frame = getGraphicAnimationFrame(graphic, -1);
4327 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4330 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4332 // ------------------------------------------------------------------------
4333 // draw things in front of player (active dynamite or dynabombs)
4334 // ------------------------------------------------------------------------
4336 if (IS_ACTIVE_BOMB(element))
4338 int graphic = el2img(element);
4339 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4341 if (game.emulation == EMU_SUPAPLEX)
4342 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4344 DrawGraphicThruMask(sx, sy, graphic, frame);
4347 if (player_is_moving && last_element == EL_EXPLOSION)
4349 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4350 GfxElement[last_jx][last_jy] : EL_EMPTY);
4351 int graphic = el_act2img(element, ACTION_EXPLODING);
4352 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4353 int phase = ExplodePhase[last_jx][last_jy] - 1;
4354 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4357 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4360 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4362 // ------------------------------------------------------------------------
4363 // draw elements the player is just walking/passing through/under
4364 // ------------------------------------------------------------------------
4366 if (player_is_moving)
4368 // handle the field the player is leaving ...
4369 if (IS_ACCESSIBLE_INSIDE(last_element))
4370 DrawLevelField(last_jx, last_jy);
4371 else if (IS_ACCESSIBLE_UNDER(last_element))
4372 DrawLevelFieldThruMask(last_jx, last_jy);
4375 // do not redraw accessible elements if the player is just pushing them
4376 if (!player_is_moving || !player->is_pushing)
4378 // ... and the field the player is entering
4379 if (IS_ACCESSIBLE_INSIDE(element))
4380 DrawLevelField(jx, jy);
4381 else if (IS_ACCESSIBLE_UNDER(element))
4382 DrawLevelFieldThruMask(jx, jy);
4385 MarkTileDirty(sx, sy);
4389 void DrawPlayer(struct PlayerInfo *player)
4393 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4394 DrawPlayerExt(player, i);
4397 void DrawAllPlayers(void)
4401 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4402 for (j = 0; j < MAX_PLAYERS; j++)
4403 if (stored_player[j].active)
4404 DrawPlayerExt(&stored_player[j], i);
4407 void DrawPlayerField(int x, int y)
4409 if (!IS_PLAYER(x, y))
4412 DrawPlayer(PLAYERINFO(x, y));
4415 // ----------------------------------------------------------------------------
4417 void WaitForEventToContinue(void)
4419 boolean first_wait = TRUE;
4420 boolean still_wait = TRUE;
4422 if (program.headless)
4425 // simulate releasing mouse button over last gadget, if still pressed
4427 HandleGadgets(-1, -1, 0);
4429 button_status = MB_RELEASED;
4432 ClearPlayerAction();
4438 if (NextValidEvent(&event))
4442 case EVENT_BUTTONPRESS:
4443 case EVENT_FINGERPRESS:
4447 case EVENT_BUTTONRELEASE:
4448 case EVENT_FINGERRELEASE:
4449 still_wait = first_wait;
4452 case EVENT_KEYPRESS:
4453 case SDL_CONTROLLERBUTTONDOWN:
4454 case SDL_JOYBUTTONDOWN:
4459 HandleOtherEvents(&event);
4463 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4468 if (!PendingEvent())
4473 #define MAX_REQUEST_LINES 13
4474 #define MAX_REQUEST_LINE_FONT1_LEN 7
4475 #define MAX_REQUEST_LINE_FONT2_LEN 10
4477 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4479 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4481 int draw_buffer_last = GetDrawtoField();
4482 int width = request.width;
4483 int height = request.height;
4487 // when showing request dialog after game ended, deactivate game panel
4488 if (game_just_ended)
4489 game.panel.active = FALSE;
4491 game.request_active = TRUE;
4493 setRequestPosition(&sx, &sy, FALSE);
4495 button_status = MB_RELEASED;
4497 request_gadget_id = -1;
4502 boolean event_handled = FALSE;
4504 if (game_just_ended)
4506 SetDrawtoField(draw_buffer_game);
4508 HandleGameActions();
4510 SetDrawtoField(DRAW_TO_BACKBUFFER);
4512 if (global.use_envelope_request)
4514 // copy current state of request area to middle of playfield area
4515 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4523 while (NextValidEvent(&event))
4525 event_handled = TRUE;
4529 case EVENT_BUTTONPRESS:
4530 case EVENT_BUTTONRELEASE:
4531 case EVENT_MOTIONNOTIFY:
4535 if (event.type == EVENT_MOTIONNOTIFY)
4540 motion_status = TRUE;
4541 mx = ((MotionEvent *) &event)->x;
4542 my = ((MotionEvent *) &event)->y;
4546 motion_status = FALSE;
4547 mx = ((ButtonEvent *) &event)->x;
4548 my = ((ButtonEvent *) &event)->y;
4549 if (event.type == EVENT_BUTTONPRESS)
4550 button_status = ((ButtonEvent *) &event)->button;
4552 button_status = MB_RELEASED;
4555 // this sets 'request_gadget_id'
4556 HandleGadgets(mx, my, button_status);
4558 switch (request_gadget_id)
4560 case TOOL_CTRL_ID_YES:
4561 case TOOL_CTRL_ID_TOUCH_YES:
4564 case TOOL_CTRL_ID_NO:
4565 case TOOL_CTRL_ID_TOUCH_NO:
4568 case TOOL_CTRL_ID_CONFIRM:
4569 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4570 result = TRUE | FALSE;
4573 case TOOL_CTRL_ID_PLAYER_1:
4576 case TOOL_CTRL_ID_PLAYER_2:
4579 case TOOL_CTRL_ID_PLAYER_3:
4582 case TOOL_CTRL_ID_PLAYER_4:
4587 // only check clickable animations if no request gadget clicked
4588 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4595 case SDL_WINDOWEVENT:
4596 HandleWindowEvent((WindowEvent *) &event);
4599 case SDL_APP_WILLENTERBACKGROUND:
4600 case SDL_APP_DIDENTERBACKGROUND:
4601 case SDL_APP_WILLENTERFOREGROUND:
4602 case SDL_APP_DIDENTERFOREGROUND:
4603 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4606 case EVENT_KEYPRESS:
4608 Key key = GetEventKey((KeyEvent *)&event);
4613 if (req_state & REQ_CONFIRM)
4622 #if defined(KSYM_Rewind)
4623 case KSYM_Rewind: // for Amazon Fire TV remote
4632 #if defined(KSYM_FastForward)
4633 case KSYM_FastForward: // for Amazon Fire TV remote
4639 HandleKeysDebug(key, KEY_PRESSED);
4643 if (req_state & REQ_PLAYER)
4645 int old_player_nr = setup.network_player_nr;
4648 result = old_player_nr + 1;
4653 result = old_player_nr + 1;
4684 case EVENT_FINGERRELEASE:
4685 case EVENT_KEYRELEASE:
4686 ClearPlayerAction();
4689 case SDL_CONTROLLERBUTTONDOWN:
4690 switch (event.cbutton.button)
4692 case SDL_CONTROLLER_BUTTON_A:
4693 case SDL_CONTROLLER_BUTTON_X:
4694 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4695 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4699 case SDL_CONTROLLER_BUTTON_B:
4700 case SDL_CONTROLLER_BUTTON_Y:
4701 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4702 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4703 case SDL_CONTROLLER_BUTTON_BACK:
4708 if (req_state & REQ_PLAYER)
4710 int old_player_nr = setup.network_player_nr;
4713 result = old_player_nr + 1;
4715 switch (event.cbutton.button)
4717 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4718 case SDL_CONTROLLER_BUTTON_Y:
4722 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4723 case SDL_CONTROLLER_BUTTON_B:
4727 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4728 case SDL_CONTROLLER_BUTTON_A:
4732 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4733 case SDL_CONTROLLER_BUTTON_X:
4744 case SDL_CONTROLLERBUTTONUP:
4745 HandleJoystickEvent(&event);
4746 ClearPlayerAction();
4750 HandleOtherEvents(&event);
4755 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4757 int joy = AnyJoystick();
4759 if (joy & JOY_BUTTON_1)
4761 else if (joy & JOY_BUTTON_2)
4764 else if (AnyJoystick())
4766 int joy = AnyJoystick();
4768 if (req_state & REQ_PLAYER)
4772 else if (joy & JOY_RIGHT)
4774 else if (joy & JOY_DOWN)
4776 else if (joy & JOY_LEFT)
4783 if (game_just_ended)
4785 if (global.use_envelope_request)
4787 // copy back current state of pressed buttons inside request area
4788 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4792 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4798 SetDrawtoField(draw_buffer_last);
4800 game.request_active = FALSE;
4805 static boolean RequestDoor(char *text, unsigned int req_state)
4807 int draw_buffer_last = GetDrawtoField();
4808 unsigned int old_door_state;
4809 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4810 int font_nr = FONT_TEXT_2;
4815 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4817 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4818 font_nr = FONT_TEXT_1;
4821 if (game_status == GAME_MODE_PLAYING)
4822 BlitScreenToBitmap(backbuffer);
4824 // disable deactivated drawing when quick-loading level tape recording
4825 if (tape.playing && tape.deactivate_display)
4826 TapeDeactivateDisplayOff(TRUE);
4828 SetMouseCursor(CURSOR_DEFAULT);
4830 // pause network game while waiting for request to answer
4831 if (network.enabled &&
4832 game_status == GAME_MODE_PLAYING &&
4833 !game.all_players_gone &&
4834 req_state & REQUEST_WAIT_FOR_INPUT)
4835 SendToServer_PausePlaying();
4837 old_door_state = GetDoorState();
4839 // simulate releasing mouse button over last gadget, if still pressed
4841 HandleGadgets(-1, -1, 0);
4845 // draw released gadget before proceeding
4848 if (old_door_state & DOOR_OPEN_1)
4850 CloseDoor(DOOR_CLOSE_1);
4852 // save old door content
4853 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4854 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4857 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4858 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4860 // clear door drawing field
4861 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4863 // force DOOR font inside door area
4864 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4866 // write text for request
4867 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4869 char text_line[max_request_line_len + 1];
4875 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4877 tc = *(text_ptr + tx);
4878 // if (!tc || tc == ' ')
4879 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4883 if ((tc == '?' || tc == '!') && tl == 0)
4893 strncpy(text_line, text_ptr, tl);
4896 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4897 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4898 text_line, font_nr);
4900 text_ptr += tl + (tc == ' ' ? 1 : 0);
4901 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4906 if (req_state & REQ_ASK)
4908 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4909 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4910 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4911 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4913 else if (req_state & REQ_CONFIRM)
4915 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4916 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4918 else if (req_state & REQ_PLAYER)
4920 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4921 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4922 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4923 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4926 // copy request gadgets to door backbuffer
4927 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4929 OpenDoor(DOOR_OPEN_1);
4931 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4933 if (game_status == GAME_MODE_PLAYING)
4935 SetPanelBackground();
4936 SetDrawBackgroundMask(REDRAW_DOOR_1);
4940 SetDrawBackgroundMask(REDRAW_FIELD);
4946 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4948 // ---------- handle request buttons ----------
4949 result = RequestHandleEvents(req_state, draw_buffer_last);
4953 if (!(req_state & REQ_STAY_OPEN))
4955 CloseDoor(DOOR_CLOSE_1);
4957 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4958 (req_state & REQ_REOPEN))
4959 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4964 if (game_status == GAME_MODE_PLAYING)
4966 SetPanelBackground();
4967 SetDrawBackgroundMask(REDRAW_DOOR_1);
4971 SetDrawBackgroundMask(REDRAW_FIELD);
4974 // continue network game after request
4975 if (network.enabled &&
4976 game_status == GAME_MODE_PLAYING &&
4977 !game.all_players_gone &&
4978 req_state & REQUEST_WAIT_FOR_INPUT)
4979 SendToServer_ContinuePlaying();
4981 // restore deactivated drawing when quick-loading level tape recording
4982 if (tape.playing && tape.deactivate_display)
4983 TapeDeactivateDisplayOn();
4988 static boolean RequestEnvelope(char *text, unsigned int req_state)
4990 int draw_buffer_last = GetDrawtoField();
4993 if (game_status == GAME_MODE_PLAYING)
4994 BlitScreenToBitmap(backbuffer);
4996 // disable deactivated drawing when quick-loading level tape recording
4997 if (tape.playing && tape.deactivate_display)
4998 TapeDeactivateDisplayOff(TRUE);
5000 SetMouseCursor(CURSOR_DEFAULT);
5002 // pause network game while waiting for request to answer
5003 if (network.enabled &&
5004 game_status == GAME_MODE_PLAYING &&
5005 !game.all_players_gone &&
5006 req_state & REQUEST_WAIT_FOR_INPUT)
5007 SendToServer_PausePlaying();
5009 // simulate releasing mouse button over last gadget, if still pressed
5011 HandleGadgets(-1, -1, 0);
5015 // (replace with setting corresponding request background)
5016 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5017 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5019 // clear door drawing field
5020 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5022 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5024 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5026 if (game_status == GAME_MODE_PLAYING)
5028 SetPanelBackground();
5029 SetDrawBackgroundMask(REDRAW_DOOR_1);
5033 SetDrawBackgroundMask(REDRAW_FIELD);
5039 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5041 // ---------- handle request buttons ----------
5042 result = RequestHandleEvents(req_state, draw_buffer_last);
5046 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5050 if (game_status == GAME_MODE_PLAYING)
5052 SetPanelBackground();
5053 SetDrawBackgroundMask(REDRAW_DOOR_1);
5057 SetDrawBackgroundMask(REDRAW_FIELD);
5060 // continue network game after request
5061 if (network.enabled &&
5062 game_status == GAME_MODE_PLAYING &&
5063 !game.all_players_gone &&
5064 req_state & REQUEST_WAIT_FOR_INPUT)
5065 SendToServer_ContinuePlaying();
5067 // restore deactivated drawing when quick-loading level tape recording
5068 if (tape.playing && tape.deactivate_display)
5069 TapeDeactivateDisplayOn();
5074 boolean Request(char *text, unsigned int req_state)
5076 boolean overlay_enabled = GetOverlayEnabled();
5079 game.request_active_or_moving = TRUE;
5081 SetOverlayEnabled(FALSE);
5083 if (global.use_envelope_request)
5084 result = RequestEnvelope(text, req_state);
5086 result = RequestDoor(text, req_state);
5088 SetOverlayEnabled(overlay_enabled);
5090 game.request_active_or_moving = FALSE;
5095 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5097 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5098 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5101 if (dpo1->sort_priority != dpo2->sort_priority)
5102 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5104 compare_result = dpo1->nr - dpo2->nr;
5106 return compare_result;
5109 void InitGraphicCompatibilityInfo_Doors(void)
5115 struct DoorInfo *door;
5119 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5120 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5122 { -1, -1, -1, NULL }
5124 struct Rect door_rect_list[] =
5126 { DX, DY, DXSIZE, DYSIZE },
5127 { VX, VY, VXSIZE, VYSIZE }
5131 for (i = 0; doors[i].door_token != -1; i++)
5133 int door_token = doors[i].door_token;
5134 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5135 int part_1 = doors[i].part_1;
5136 int part_8 = doors[i].part_8;
5137 int part_2 = part_1 + 1;
5138 int part_3 = part_1 + 2;
5139 struct DoorInfo *door = doors[i].door;
5140 struct Rect *door_rect = &door_rect_list[door_index];
5141 boolean door_gfx_redefined = FALSE;
5143 // check if any door part graphic definitions have been redefined
5145 for (j = 0; door_part_controls[j].door_token != -1; j++)
5147 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5148 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5150 if (dpc->door_token == door_token && fi->redefined)
5151 door_gfx_redefined = TRUE;
5154 // check for old-style door graphic/animation modifications
5156 if (!door_gfx_redefined)
5158 if (door->anim_mode & ANIM_STATIC_PANEL)
5160 door->panel.step_xoffset = 0;
5161 door->panel.step_yoffset = 0;
5164 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5166 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5167 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5168 int num_door_steps, num_panel_steps;
5170 // remove door part graphics other than the two default wings
5172 for (j = 0; door_part_controls[j].door_token != -1; j++)
5174 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5175 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5177 if (dpc->graphic >= part_3 &&
5178 dpc->graphic <= part_8)
5182 // set graphics and screen positions of the default wings
5184 g_part_1->width = door_rect->width;
5185 g_part_1->height = door_rect->height;
5186 g_part_2->width = door_rect->width;
5187 g_part_2->height = door_rect->height;
5188 g_part_2->src_x = door_rect->width;
5189 g_part_2->src_y = g_part_1->src_y;
5191 door->part_2.x = door->part_1.x;
5192 door->part_2.y = door->part_1.y;
5194 if (door->width != -1)
5196 g_part_1->width = door->width;
5197 g_part_2->width = door->width;
5199 // special treatment for graphics and screen position of right wing
5200 g_part_2->src_x += door_rect->width - door->width;
5201 door->part_2.x += door_rect->width - door->width;
5204 if (door->height != -1)
5206 g_part_1->height = door->height;
5207 g_part_2->height = door->height;
5209 // special treatment for graphics and screen position of bottom wing
5210 g_part_2->src_y += door_rect->height - door->height;
5211 door->part_2.y += door_rect->height - door->height;
5214 // set animation delays for the default wings and panels
5216 door->part_1.step_delay = door->step_delay;
5217 door->part_2.step_delay = door->step_delay;
5218 door->panel.step_delay = door->step_delay;
5220 // set animation draw order for the default wings
5222 door->part_1.sort_priority = 2; // draw left wing over ...
5223 door->part_2.sort_priority = 1; // ... right wing
5225 // set animation draw offset for the default wings
5227 if (door->anim_mode & ANIM_HORIZONTAL)
5229 door->part_1.step_xoffset = door->step_offset;
5230 door->part_1.step_yoffset = 0;
5231 door->part_2.step_xoffset = door->step_offset * -1;
5232 door->part_2.step_yoffset = 0;
5234 num_door_steps = g_part_1->width / door->step_offset;
5236 else // ANIM_VERTICAL
5238 door->part_1.step_xoffset = 0;
5239 door->part_1.step_yoffset = door->step_offset;
5240 door->part_2.step_xoffset = 0;
5241 door->part_2.step_yoffset = door->step_offset * -1;
5243 num_door_steps = g_part_1->height / door->step_offset;
5246 // set animation draw offset for the default panels
5248 if (door->step_offset > 1)
5250 num_panel_steps = 2 * door_rect->height / door->step_offset;
5251 door->panel.start_step = num_panel_steps - num_door_steps;
5252 door->panel.start_step_closing = door->panel.start_step;
5256 num_panel_steps = door_rect->height / door->step_offset;
5257 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5258 door->panel.start_step_closing = door->panel.start_step;
5259 door->panel.step_delay *= 2;
5266 void InitDoors(void)
5270 for (i = 0; door_part_controls[i].door_token != -1; i++)
5272 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5273 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5275 // initialize "start_step_opening" and "start_step_closing", if needed
5276 if (dpc->pos->start_step_opening == 0 &&
5277 dpc->pos->start_step_closing == 0)
5279 // dpc->pos->start_step_opening = dpc->pos->start_step;
5280 dpc->pos->start_step_closing = dpc->pos->start_step;
5283 // fill structure for door part draw order (sorted below)
5285 dpo->sort_priority = dpc->pos->sort_priority;
5288 // sort door part controls according to sort_priority and graphic number
5289 qsort(door_part_order, MAX_DOOR_PARTS,
5290 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5293 unsigned int OpenDoor(unsigned int door_state)
5295 if (door_state & DOOR_COPY_BACK)
5297 if (door_state & DOOR_OPEN_1)
5298 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5299 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5301 if (door_state & DOOR_OPEN_2)
5302 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5303 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5305 door_state &= ~DOOR_COPY_BACK;
5308 return MoveDoor(door_state);
5311 unsigned int CloseDoor(unsigned int door_state)
5313 unsigned int old_door_state = GetDoorState();
5315 if (!(door_state & DOOR_NO_COPY_BACK))
5317 if (old_door_state & DOOR_OPEN_1)
5318 BlitBitmap(backbuffer, bitmap_db_door_1,
5319 DX, DY, DXSIZE, DYSIZE, 0, 0);
5321 if (old_door_state & DOOR_OPEN_2)
5322 BlitBitmap(backbuffer, bitmap_db_door_2,
5323 VX, VY, VXSIZE, VYSIZE, 0, 0);
5325 door_state &= ~DOOR_NO_COPY_BACK;
5328 return MoveDoor(door_state);
5331 unsigned int GetDoorState(void)
5333 return MoveDoor(DOOR_GET_STATE);
5336 unsigned int SetDoorState(unsigned int door_state)
5338 return MoveDoor(door_state | DOOR_SET_STATE);
5341 static int euclid(int a, int b)
5343 return (b ? euclid(b, a % b) : a);
5346 unsigned int MoveDoor(unsigned int door_state)
5348 struct Rect door_rect_list[] =
5350 { DX, DY, DXSIZE, DYSIZE },
5351 { VX, VY, VXSIZE, VYSIZE }
5353 static int door1 = DOOR_CLOSE_1;
5354 static int door2 = DOOR_CLOSE_2;
5355 DelayCounter door_delay = { 0 };
5358 if (door_state == DOOR_GET_STATE)
5359 return (door1 | door2);
5361 if (door_state & DOOR_SET_STATE)
5363 if (door_state & DOOR_ACTION_1)
5364 door1 = door_state & DOOR_ACTION_1;
5365 if (door_state & DOOR_ACTION_2)
5366 door2 = door_state & DOOR_ACTION_2;
5368 return (door1 | door2);
5371 if (!(door_state & DOOR_FORCE_REDRAW))
5373 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5374 door_state &= ~DOOR_OPEN_1;
5375 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5376 door_state &= ~DOOR_CLOSE_1;
5377 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5378 door_state &= ~DOOR_OPEN_2;
5379 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5380 door_state &= ~DOOR_CLOSE_2;
5383 if (global.autoplay_leveldir)
5385 door_state |= DOOR_NO_DELAY;
5386 door_state &= ~DOOR_CLOSE_ALL;
5389 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5390 door_state |= DOOR_NO_DELAY;
5392 if (door_state & DOOR_ACTION)
5394 boolean door_panel_drawn[NUM_DOORS];
5395 boolean panel_has_doors[NUM_DOORS];
5396 boolean door_part_skip[MAX_DOOR_PARTS];
5397 boolean door_part_done[MAX_DOOR_PARTS];
5398 boolean door_part_done_all;
5399 int num_steps[MAX_DOOR_PARTS];
5400 int max_move_delay = 0; // delay for complete animations of all doors
5401 int max_step_delay = 0; // delay (ms) between two animation frames
5402 int num_move_steps = 0; // number of animation steps for all doors
5403 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5404 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5408 for (i = 0; i < NUM_DOORS; i++)
5409 panel_has_doors[i] = FALSE;
5411 for (i = 0; i < MAX_DOOR_PARTS; i++)
5413 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5414 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5415 int door_token = dpc->door_token;
5417 door_part_done[i] = FALSE;
5418 door_part_skip[i] = (!(door_state & door_token) ||
5422 for (i = 0; i < MAX_DOOR_PARTS; i++)
5424 int nr = door_part_order[i].nr;
5425 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5426 struct DoorPartPosInfo *pos = dpc->pos;
5427 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5428 int door_token = dpc->door_token;
5429 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5430 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5431 int step_xoffset = ABS(pos->step_xoffset);
5432 int step_yoffset = ABS(pos->step_yoffset);
5433 int step_delay = pos->step_delay;
5434 int current_door_state = door_state & door_token;
5435 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5436 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5437 boolean part_opening = (is_panel ? door_closing : door_opening);
5438 int start_step = (part_opening ? pos->start_step_opening :
5439 pos->start_step_closing);
5440 float move_xsize = (step_xoffset ? g->width : 0);
5441 float move_ysize = (step_yoffset ? g->height : 0);
5442 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5443 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5444 int move_steps = (move_xsteps && move_ysteps ?
5445 MIN(move_xsteps, move_ysteps) :
5446 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5447 int move_delay = move_steps * step_delay;
5449 if (door_part_skip[nr])
5452 max_move_delay = MAX(max_move_delay, move_delay);
5453 max_step_delay = (max_step_delay == 0 ? step_delay :
5454 euclid(max_step_delay, step_delay));
5455 num_steps[nr] = move_steps;
5459 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5461 panel_has_doors[door_index] = TRUE;
5465 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5467 num_move_steps = max_move_delay / max_step_delay;
5468 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5470 door_delay.value = max_step_delay;
5472 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5474 start = num_move_steps - 1;
5478 // opening door sound has priority over simultaneously closing door
5479 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5481 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5483 if (door_state & DOOR_OPEN_1)
5484 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5485 if (door_state & DOOR_OPEN_2)
5486 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5488 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5490 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5492 if (door_state & DOOR_CLOSE_1)
5493 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5494 if (door_state & DOOR_CLOSE_2)
5495 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5499 for (k = start; k < num_move_steps; k++)
5501 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5503 door_part_done_all = TRUE;
5505 for (i = 0; i < NUM_DOORS; i++)
5506 door_panel_drawn[i] = FALSE;
5508 for (i = 0; i < MAX_DOOR_PARTS; i++)
5510 int nr = door_part_order[i].nr;
5511 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5512 struct DoorPartPosInfo *pos = dpc->pos;
5513 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5514 int door_token = dpc->door_token;
5515 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5516 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5517 boolean is_panel_and_door_has_closed = FALSE;
5518 struct Rect *door_rect = &door_rect_list[door_index];
5519 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5521 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5522 int current_door_state = door_state & door_token;
5523 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5524 boolean door_closing = !door_opening;
5525 boolean part_opening = (is_panel ? door_closing : door_opening);
5526 boolean part_closing = !part_opening;
5527 int start_step = (part_opening ? pos->start_step_opening :
5528 pos->start_step_closing);
5529 int step_delay = pos->step_delay;
5530 int step_factor = step_delay / max_step_delay;
5531 int k1 = (step_factor ? k / step_factor + 1 : k);
5532 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5533 int kk = MAX(0, k2);
5536 int src_x, src_y, src_xx, src_yy;
5537 int dst_x, dst_y, dst_xx, dst_yy;
5540 if (door_part_skip[nr])
5543 if (!(door_state & door_token))
5551 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5552 int kk_door = MAX(0, k2_door);
5553 int sync_frame = kk_door * door_delay.value;
5554 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5556 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5557 &g_src_x, &g_src_y);
5562 if (!door_panel_drawn[door_index])
5564 ClearRectangle(drawto, door_rect->x, door_rect->y,
5565 door_rect->width, door_rect->height);
5567 door_panel_drawn[door_index] = TRUE;
5570 // draw opening or closing door parts
5572 if (pos->step_xoffset < 0) // door part on right side
5575 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5578 if (dst_xx + width > door_rect->width)
5579 width = door_rect->width - dst_xx;
5581 else // door part on left side
5584 dst_xx = pos->x - kk * pos->step_xoffset;
5588 src_xx = ABS(dst_xx);
5592 width = g->width - src_xx;
5594 if (width > door_rect->width)
5595 width = door_rect->width;
5597 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5600 if (pos->step_yoffset < 0) // door part on bottom side
5603 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5606 if (dst_yy + height > door_rect->height)
5607 height = door_rect->height - dst_yy;
5609 else // door part on top side
5612 dst_yy = pos->y - kk * pos->step_yoffset;
5616 src_yy = ABS(dst_yy);
5620 height = g->height - src_yy;
5623 src_x = g_src_x + src_xx;
5624 src_y = g_src_y + src_yy;
5626 dst_x = door_rect->x + dst_xx;
5627 dst_y = door_rect->y + dst_yy;
5629 is_panel_and_door_has_closed =
5632 panel_has_doors[door_index] &&
5633 k >= num_move_steps_doors_only - 1);
5635 if (width >= 0 && width <= g->width &&
5636 height >= 0 && height <= g->height &&
5637 !is_panel_and_door_has_closed)
5639 if (is_panel || !pos->draw_masked)
5640 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5643 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5647 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5649 if ((part_opening && (width < 0 || height < 0)) ||
5650 (part_closing && (width >= g->width && height >= g->height)))
5651 door_part_done[nr] = TRUE;
5653 // continue door part animations, but not panel after door has closed
5654 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5655 door_part_done_all = FALSE;
5658 if (!(door_state & DOOR_NO_DELAY))
5662 SkipUntilDelayReached(&door_delay, &k, last_frame);
5664 // prevent OS (Windows) from complaining about program not responding
5668 if (door_part_done_all)
5672 if (!(door_state & DOOR_NO_DELAY))
5674 // wait for specified door action post delay
5675 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5676 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5677 else if (door_state & DOOR_ACTION_1)
5678 door_delay.value = door_1.post_delay;
5679 else if (door_state & DOOR_ACTION_2)
5680 door_delay.value = door_2.post_delay;
5682 while (!DelayReached(&door_delay))
5687 if (door_state & DOOR_ACTION_1)
5688 door1 = door_state & DOOR_ACTION_1;
5689 if (door_state & DOOR_ACTION_2)
5690 door2 = door_state & DOOR_ACTION_2;
5692 // draw masked border over door area
5693 DrawMaskedBorder(REDRAW_DOOR_1);
5694 DrawMaskedBorder(REDRAW_DOOR_2);
5696 ClearAutoRepeatKeyEvents();
5698 return (door1 | door2);
5701 static boolean useSpecialEditorDoor(void)
5703 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5704 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5706 // do not draw special editor door if editor border defined or redefined
5707 if (graphic_info[graphic].bitmap != NULL || redefined)
5710 // do not draw special editor door if global border defined to be empty
5711 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5714 // do not draw special editor door if viewport definitions do not match
5718 EY + EYSIZE != VY + VYSIZE)
5724 void DrawSpecialEditorDoor(void)
5726 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5727 int top_border_width = gfx1->width;
5728 int top_border_height = gfx1->height;
5729 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5730 int ex = EX - outer_border;
5731 int ey = EY - outer_border;
5732 int vy = VY - outer_border;
5733 int exsize = EXSIZE + 2 * outer_border;
5735 if (!useSpecialEditorDoor())
5738 // draw bigger level editor toolbox window
5739 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5740 top_border_width, top_border_height, ex, ey - top_border_height);
5741 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5742 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5744 redraw_mask |= REDRAW_ALL;
5747 void UndrawSpecialEditorDoor(void)
5749 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5750 int top_border_width = gfx1->width;
5751 int top_border_height = gfx1->height;
5752 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5753 int ex = EX - outer_border;
5754 int ey = EY - outer_border;
5755 int ey_top = ey - top_border_height;
5756 int exsize = EXSIZE + 2 * outer_border;
5757 int eysize = EYSIZE + 2 * outer_border;
5759 if (!useSpecialEditorDoor())
5762 // draw normal tape recorder window
5763 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5765 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5766 ex, ey_top, top_border_width, top_border_height,
5768 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5769 ex, ey, exsize, eysize, ex, ey);
5773 // if screen background is set to "[NONE]", clear editor toolbox window
5774 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5775 ClearRectangle(drawto, ex, ey, exsize, eysize);
5778 redraw_mask |= REDRAW_ALL;
5782 // ---------- new tool button stuff -------------------------------------------
5787 struct TextPosInfo *pos;
5789 boolean is_touch_button;
5791 } toolbutton_info[NUM_TOOL_BUTTONS] =
5794 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5795 TOOL_CTRL_ID_YES, FALSE, "yes"
5798 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5799 TOOL_CTRL_ID_NO, FALSE, "no"
5802 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5803 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5806 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5807 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5810 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5811 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5814 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5815 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5818 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5819 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5822 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5823 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5826 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5827 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5830 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5831 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5835 void CreateToolButtons(void)
5839 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5841 int graphic = toolbutton_info[i].graphic;
5842 struct GraphicInfo *gfx = &graphic_info[graphic];
5843 struct TextPosInfo *pos = toolbutton_info[i].pos;
5844 struct GadgetInfo *gi;
5845 Bitmap *deco_bitmap = None;
5846 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5847 unsigned int event_mask = GD_EVENT_RELEASED;
5848 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5849 int base_x = (is_touch_button ? 0 : DX);
5850 int base_y = (is_touch_button ? 0 : DY);
5851 int gd_x = gfx->src_x;
5852 int gd_y = gfx->src_y;
5853 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5854 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5859 // do not use touch buttons if overlay touch buttons are disabled
5860 if (is_touch_button && !setup.touch.overlay_buttons)
5863 if (global.use_envelope_request && !is_touch_button)
5865 setRequestPosition(&base_x, &base_y, TRUE);
5867 // check if request buttons are outside of envelope and fix, if needed
5868 if (x < 0 || x + gfx->width > request.width ||
5869 y < 0 || y + gfx->height > request.height)
5871 if (id == TOOL_CTRL_ID_YES)
5874 y = request.height - 2 * request.border_size - gfx->height;
5876 else if (id == TOOL_CTRL_ID_NO)
5878 x = request.width - 2 * request.border_size - gfx->width;
5879 y = request.height - 2 * request.border_size - gfx->height;
5881 else if (id == TOOL_CTRL_ID_CONFIRM)
5883 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5884 y = request.height - 2 * request.border_size - gfx->height;
5886 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5888 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5890 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5891 y = request.height - 2 * request.border_size - gfx->height * 2;
5893 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5894 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5899 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5901 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5903 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5904 pos->size, &deco_bitmap, &deco_x, &deco_y);
5905 deco_xpos = (gfx->width - pos->size) / 2;
5906 deco_ypos = (gfx->height - pos->size) / 2;
5909 gi = CreateGadget(GDI_CUSTOM_ID, id,
5910 GDI_IMAGE_ID, graphic,
5911 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5914 GDI_WIDTH, gfx->width,
5915 GDI_HEIGHT, gfx->height,
5916 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5917 GDI_STATE, GD_BUTTON_UNPRESSED,
5918 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5919 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5920 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5921 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5922 GDI_DECORATION_SIZE, pos->size, pos->size,
5923 GDI_DECORATION_SHIFTING, 1, 1,
5924 GDI_DIRECT_DRAW, FALSE,
5925 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5926 GDI_EVENT_MASK, event_mask,
5927 GDI_CALLBACK_ACTION, HandleToolButtons,
5931 Fail("cannot create gadget");
5933 tool_gadget[id] = gi;
5937 void FreeToolButtons(void)
5941 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5942 FreeGadget(tool_gadget[i]);
5945 static void UnmapToolButtons(void)
5949 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5950 UnmapGadget(tool_gadget[i]);
5953 static void HandleToolButtons(struct GadgetInfo *gi)
5955 request_gadget_id = gi->custom_id;
5958 static struct Mapping_EM_to_RND_object
5961 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5962 boolean is_backside; // backside of moving element
5968 em_object_mapping_list[GAME_TILE_MAX + 1] =
5971 Zborder, FALSE, FALSE,
5975 Zplayer, FALSE, FALSE,
5984 Ztank, FALSE, FALSE,
5988 Zeater, FALSE, FALSE,
5992 Zdynamite, FALSE, FALSE,
5996 Zboom, FALSE, FALSE,
6001 Xchain, FALSE, FALSE,
6002 EL_DEFAULT, ACTION_EXPLODING, -1
6005 Xboom_bug, FALSE, FALSE,
6006 EL_BUG, ACTION_EXPLODING, -1
6009 Xboom_tank, FALSE, FALSE,
6010 EL_SPACESHIP, ACTION_EXPLODING, -1
6013 Xboom_android, FALSE, FALSE,
6014 EL_EMC_ANDROID, ACTION_OTHER, -1
6017 Xboom_1, FALSE, FALSE,
6018 EL_DEFAULT, ACTION_EXPLODING, -1
6021 Xboom_2, FALSE, FALSE,
6022 EL_DEFAULT, ACTION_EXPLODING, -1
6026 Xblank, TRUE, FALSE,
6031 Xsplash_e, FALSE, FALSE,
6032 EL_ACID_SPLASH_RIGHT, -1, -1
6035 Xsplash_w, FALSE, FALSE,
6036 EL_ACID_SPLASH_LEFT, -1, -1
6040 Xplant, TRUE, FALSE,
6041 EL_EMC_PLANT, -1, -1
6044 Yplant, FALSE, FALSE,
6045 EL_EMC_PLANT, -1, -1
6049 Xacid_1, TRUE, FALSE,
6053 Xacid_2, FALSE, FALSE,
6057 Xacid_3, FALSE, FALSE,
6061 Xacid_4, FALSE, FALSE,
6065 Xacid_5, FALSE, FALSE,
6069 Xacid_6, FALSE, FALSE,
6073 Xacid_7, FALSE, FALSE,
6077 Xacid_8, FALSE, FALSE,
6082 Xfake_acid_1, TRUE, FALSE,
6083 EL_EMC_FAKE_ACID, -1, -1
6086 Xfake_acid_2, FALSE, FALSE,
6087 EL_EMC_FAKE_ACID, -1, -1
6090 Xfake_acid_3, FALSE, FALSE,
6091 EL_EMC_FAKE_ACID, -1, -1
6094 Xfake_acid_4, FALSE, FALSE,
6095 EL_EMC_FAKE_ACID, -1, -1
6098 Xfake_acid_5, FALSE, FALSE,
6099 EL_EMC_FAKE_ACID, -1, -1
6102 Xfake_acid_6, FALSE, FALSE,
6103 EL_EMC_FAKE_ACID, -1, -1
6106 Xfake_acid_7, FALSE, FALSE,
6107 EL_EMC_FAKE_ACID, -1, -1
6110 Xfake_acid_8, FALSE, FALSE,
6111 EL_EMC_FAKE_ACID, -1, -1
6115 Xfake_acid_1_player, FALSE, FALSE,
6116 EL_EMC_FAKE_ACID, -1, -1
6119 Xfake_acid_2_player, FALSE, FALSE,
6120 EL_EMC_FAKE_ACID, -1, -1
6123 Xfake_acid_3_player, FALSE, FALSE,
6124 EL_EMC_FAKE_ACID, -1, -1
6127 Xfake_acid_4_player, FALSE, FALSE,
6128 EL_EMC_FAKE_ACID, -1, -1
6131 Xfake_acid_5_player, FALSE, FALSE,
6132 EL_EMC_FAKE_ACID, -1, -1
6135 Xfake_acid_6_player, FALSE, FALSE,
6136 EL_EMC_FAKE_ACID, -1, -1
6139 Xfake_acid_7_player, FALSE, FALSE,
6140 EL_EMC_FAKE_ACID, -1, -1
6143 Xfake_acid_8_player, FALSE, FALSE,
6144 EL_EMC_FAKE_ACID, -1, -1
6148 Xgrass, TRUE, FALSE,
6149 EL_EMC_GRASS, -1, -1
6152 Ygrass_nB, FALSE, FALSE,
6153 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6156 Ygrass_eB, FALSE, FALSE,
6157 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6160 Ygrass_sB, FALSE, FALSE,
6161 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6164 Ygrass_wB, FALSE, FALSE,
6165 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6173 Ydirt_nB, FALSE, FALSE,
6174 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6177 Ydirt_eB, FALSE, FALSE,
6178 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6181 Ydirt_sB, FALSE, FALSE,
6182 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6185 Ydirt_wB, FALSE, FALSE,
6186 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6190 Xandroid, TRUE, FALSE,
6191 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6194 Xandroid_1_n, FALSE, FALSE,
6195 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6198 Xandroid_2_n, FALSE, FALSE,
6199 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6202 Xandroid_1_e, FALSE, FALSE,
6203 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6206 Xandroid_2_e, FALSE, FALSE,
6207 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6210 Xandroid_1_w, FALSE, FALSE,
6211 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6214 Xandroid_2_w, FALSE, FALSE,
6215 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6218 Xandroid_1_s, FALSE, FALSE,
6219 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6222 Xandroid_2_s, FALSE, FALSE,
6223 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6226 Yandroid_n, FALSE, FALSE,
6227 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6230 Yandroid_nB, FALSE, TRUE,
6231 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6234 Yandroid_ne, FALSE, FALSE,
6235 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6238 Yandroid_neB, FALSE, TRUE,
6239 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6242 Yandroid_e, FALSE, FALSE,
6243 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6246 Yandroid_eB, FALSE, TRUE,
6247 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6250 Yandroid_se, FALSE, FALSE,
6251 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6254 Yandroid_seB, FALSE, TRUE,
6255 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6258 Yandroid_s, FALSE, FALSE,
6259 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6262 Yandroid_sB, FALSE, TRUE,
6263 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6266 Yandroid_sw, FALSE, FALSE,
6267 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6270 Yandroid_swB, FALSE, TRUE,
6271 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6274 Yandroid_w, FALSE, FALSE,
6275 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6278 Yandroid_wB, FALSE, TRUE,
6279 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6282 Yandroid_nw, FALSE, FALSE,
6283 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6286 Yandroid_nwB, FALSE, TRUE,
6287 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6291 Xeater_n, TRUE, FALSE,
6292 EL_YAMYAM_UP, -1, -1
6295 Xeater_e, TRUE, FALSE,
6296 EL_YAMYAM_RIGHT, -1, -1
6299 Xeater_w, TRUE, FALSE,
6300 EL_YAMYAM_LEFT, -1, -1
6303 Xeater_s, TRUE, FALSE,
6304 EL_YAMYAM_DOWN, -1, -1
6307 Yeater_n, FALSE, FALSE,
6308 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6311 Yeater_nB, FALSE, TRUE,
6312 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6315 Yeater_e, FALSE, FALSE,
6316 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6319 Yeater_eB, FALSE, TRUE,
6320 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6323 Yeater_s, FALSE, FALSE,
6324 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6327 Yeater_sB, FALSE, TRUE,
6328 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6331 Yeater_w, FALSE, FALSE,
6332 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6335 Yeater_wB, FALSE, TRUE,
6336 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6339 Yeater_stone, FALSE, FALSE,
6340 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6343 Yeater_spring, FALSE, FALSE,
6344 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6348 Xalien, TRUE, FALSE,
6352 Xalien_pause, FALSE, FALSE,
6356 Yalien_n, FALSE, FALSE,
6357 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6360 Yalien_nB, FALSE, TRUE,
6361 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6364 Yalien_e, FALSE, FALSE,
6365 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6368 Yalien_eB, FALSE, TRUE,
6369 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6372 Yalien_s, FALSE, FALSE,
6373 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6376 Yalien_sB, FALSE, TRUE,
6377 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6380 Yalien_w, FALSE, FALSE,
6381 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6384 Yalien_wB, FALSE, TRUE,
6385 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6388 Yalien_stone, FALSE, FALSE,
6389 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6392 Yalien_spring, FALSE, FALSE,
6393 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6397 Xbug_1_n, TRUE, FALSE,
6401 Xbug_1_e, TRUE, FALSE,
6402 EL_BUG_RIGHT, -1, -1
6405 Xbug_1_s, TRUE, FALSE,
6409 Xbug_1_w, TRUE, FALSE,
6413 Xbug_2_n, FALSE, FALSE,
6417 Xbug_2_e, FALSE, FALSE,
6418 EL_BUG_RIGHT, -1, -1
6421 Xbug_2_s, FALSE, FALSE,
6425 Xbug_2_w, FALSE, FALSE,
6429 Ybug_n, FALSE, FALSE,
6430 EL_BUG, ACTION_MOVING, MV_BIT_UP
6433 Ybug_nB, FALSE, TRUE,
6434 EL_BUG, ACTION_MOVING, MV_BIT_UP
6437 Ybug_e, FALSE, FALSE,
6438 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6441 Ybug_eB, FALSE, TRUE,
6442 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6445 Ybug_s, FALSE, FALSE,
6446 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6449 Ybug_sB, FALSE, TRUE,
6450 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6453 Ybug_w, FALSE, FALSE,
6454 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6457 Ybug_wB, FALSE, TRUE,
6458 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6461 Ybug_w_n, FALSE, FALSE,
6462 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6465 Ybug_n_e, FALSE, FALSE,
6466 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6469 Ybug_e_s, FALSE, FALSE,
6470 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6473 Ybug_s_w, FALSE, FALSE,
6474 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6477 Ybug_e_n, FALSE, FALSE,
6478 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6481 Ybug_s_e, FALSE, FALSE,
6482 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6485 Ybug_w_s, FALSE, FALSE,
6486 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6489 Ybug_n_w, FALSE, FALSE,
6490 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6493 Ybug_stone, FALSE, FALSE,
6494 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6497 Ybug_spring, FALSE, FALSE,
6498 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6502 Xtank_1_n, TRUE, FALSE,
6503 EL_SPACESHIP_UP, -1, -1
6506 Xtank_1_e, TRUE, FALSE,
6507 EL_SPACESHIP_RIGHT, -1, -1
6510 Xtank_1_s, TRUE, FALSE,
6511 EL_SPACESHIP_DOWN, -1, -1
6514 Xtank_1_w, TRUE, FALSE,
6515 EL_SPACESHIP_LEFT, -1, -1
6518 Xtank_2_n, FALSE, FALSE,
6519 EL_SPACESHIP_UP, -1, -1
6522 Xtank_2_e, FALSE, FALSE,
6523 EL_SPACESHIP_RIGHT, -1, -1
6526 Xtank_2_s, FALSE, FALSE,
6527 EL_SPACESHIP_DOWN, -1, -1
6530 Xtank_2_w, FALSE, FALSE,
6531 EL_SPACESHIP_LEFT, -1, -1
6534 Ytank_n, FALSE, FALSE,
6535 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6538 Ytank_nB, FALSE, TRUE,
6539 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6542 Ytank_e, FALSE, FALSE,
6543 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6546 Ytank_eB, FALSE, TRUE,
6547 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6550 Ytank_s, FALSE, FALSE,
6551 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6554 Ytank_sB, FALSE, TRUE,
6555 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6558 Ytank_w, FALSE, FALSE,
6559 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6562 Ytank_wB, FALSE, TRUE,
6563 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6566 Ytank_w_n, FALSE, FALSE,
6567 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6570 Ytank_n_e, FALSE, FALSE,
6571 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6574 Ytank_e_s, FALSE, FALSE,
6575 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6578 Ytank_s_w, FALSE, FALSE,
6579 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6582 Ytank_e_n, FALSE, FALSE,
6583 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6586 Ytank_s_e, FALSE, FALSE,
6587 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6590 Ytank_w_s, FALSE, FALSE,
6591 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6594 Ytank_n_w, FALSE, FALSE,
6595 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6598 Ytank_stone, FALSE, FALSE,
6599 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6602 Ytank_spring, FALSE, FALSE,
6603 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6607 Xemerald, TRUE, FALSE,
6611 Xemerald_pause, FALSE, FALSE,
6615 Xemerald_fall, FALSE, FALSE,
6619 Xemerald_shine, FALSE, FALSE,
6620 EL_EMERALD, ACTION_TWINKLING, -1
6623 Yemerald_s, FALSE, FALSE,
6624 EL_EMERALD, ACTION_FALLING, -1
6627 Yemerald_sB, FALSE, TRUE,
6628 EL_EMERALD, ACTION_FALLING, -1
6631 Yemerald_e, FALSE, FALSE,
6632 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6635 Yemerald_eB, FALSE, TRUE,
6636 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6639 Yemerald_w, FALSE, FALSE,
6640 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6643 Yemerald_wB, FALSE, TRUE,
6644 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6647 Yemerald_blank, FALSE, FALSE,
6648 EL_EMERALD, ACTION_COLLECTING, -1
6652 Xdiamond, TRUE, FALSE,
6656 Xdiamond_pause, FALSE, FALSE,
6660 Xdiamond_fall, FALSE, FALSE,
6664 Xdiamond_shine, FALSE, FALSE,
6665 EL_DIAMOND, ACTION_TWINKLING, -1
6668 Ydiamond_s, FALSE, FALSE,
6669 EL_DIAMOND, ACTION_FALLING, -1
6672 Ydiamond_sB, FALSE, TRUE,
6673 EL_DIAMOND, ACTION_FALLING, -1
6676 Ydiamond_e, FALSE, FALSE,
6677 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6680 Ydiamond_eB, FALSE, TRUE,
6681 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6684 Ydiamond_w, FALSE, FALSE,
6685 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6688 Ydiamond_wB, FALSE, TRUE,
6689 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6692 Ydiamond_blank, FALSE, FALSE,
6693 EL_DIAMOND, ACTION_COLLECTING, -1
6696 Ydiamond_stone, FALSE, FALSE,
6697 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6701 Xstone, TRUE, FALSE,
6705 Xstone_pause, FALSE, FALSE,
6709 Xstone_fall, FALSE, FALSE,
6713 Ystone_s, FALSE, FALSE,
6714 EL_ROCK, ACTION_FALLING, -1
6717 Ystone_sB, FALSE, TRUE,
6718 EL_ROCK, ACTION_FALLING, -1
6721 Ystone_e, FALSE, FALSE,
6722 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6725 Ystone_eB, FALSE, TRUE,
6726 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6729 Ystone_w, FALSE, FALSE,
6730 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6733 Ystone_wB, FALSE, TRUE,
6734 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6742 Xbomb_pause, FALSE, FALSE,
6746 Xbomb_fall, FALSE, FALSE,
6750 Ybomb_s, FALSE, FALSE,
6751 EL_BOMB, ACTION_FALLING, -1
6754 Ybomb_sB, FALSE, TRUE,
6755 EL_BOMB, ACTION_FALLING, -1
6758 Ybomb_e, FALSE, FALSE,
6759 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6762 Ybomb_eB, FALSE, TRUE,
6763 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6766 Ybomb_w, FALSE, FALSE,
6767 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6770 Ybomb_wB, FALSE, TRUE,
6771 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6774 Ybomb_blank, FALSE, FALSE,
6775 EL_BOMB, ACTION_ACTIVATING, -1
6783 Xnut_pause, FALSE, FALSE,
6787 Xnut_fall, FALSE, FALSE,
6791 Ynut_s, FALSE, FALSE,
6792 EL_NUT, ACTION_FALLING, -1
6795 Ynut_sB, FALSE, TRUE,
6796 EL_NUT, ACTION_FALLING, -1
6799 Ynut_e, FALSE, FALSE,
6800 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6803 Ynut_eB, FALSE, TRUE,
6804 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6807 Ynut_w, FALSE, FALSE,
6808 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6811 Ynut_wB, FALSE, TRUE,
6812 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6815 Ynut_stone, FALSE, FALSE,
6816 EL_NUT, ACTION_BREAKING, -1
6820 Xspring, TRUE, FALSE,
6824 Xspring_pause, FALSE, FALSE,
6828 Xspring_e, TRUE, FALSE,
6829 EL_SPRING_RIGHT, -1, -1
6832 Xspring_w, TRUE, FALSE,
6833 EL_SPRING_LEFT, -1, -1
6836 Xspring_fall, FALSE, FALSE,
6840 Yspring_s, FALSE, FALSE,
6841 EL_SPRING, ACTION_FALLING, -1
6844 Yspring_sB, FALSE, TRUE,
6845 EL_SPRING, ACTION_FALLING, -1
6848 Yspring_e, FALSE, FALSE,
6849 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6852 Yspring_eB, FALSE, TRUE,
6853 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6856 Yspring_w, FALSE, FALSE,
6857 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6860 Yspring_wB, FALSE, TRUE,
6861 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6864 Yspring_alien_e, FALSE, FALSE,
6865 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6868 Yspring_alien_eB, FALSE, TRUE,
6869 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6872 Yspring_alien_w, FALSE, FALSE,
6873 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6876 Yspring_alien_wB, FALSE, TRUE,
6877 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6881 Xpush_emerald_e, FALSE, FALSE,
6882 EL_EMERALD, -1, MV_BIT_RIGHT
6885 Xpush_emerald_w, FALSE, FALSE,
6886 EL_EMERALD, -1, MV_BIT_LEFT
6889 Xpush_diamond_e, FALSE, FALSE,
6890 EL_DIAMOND, -1, MV_BIT_RIGHT
6893 Xpush_diamond_w, FALSE, FALSE,
6894 EL_DIAMOND, -1, MV_BIT_LEFT
6897 Xpush_stone_e, FALSE, FALSE,
6898 EL_ROCK, -1, MV_BIT_RIGHT
6901 Xpush_stone_w, FALSE, FALSE,
6902 EL_ROCK, -1, MV_BIT_LEFT
6905 Xpush_bomb_e, FALSE, FALSE,
6906 EL_BOMB, -1, MV_BIT_RIGHT
6909 Xpush_bomb_w, FALSE, FALSE,
6910 EL_BOMB, -1, MV_BIT_LEFT
6913 Xpush_nut_e, FALSE, FALSE,
6914 EL_NUT, -1, MV_BIT_RIGHT
6917 Xpush_nut_w, FALSE, FALSE,
6918 EL_NUT, -1, MV_BIT_LEFT
6921 Xpush_spring_e, FALSE, FALSE,
6922 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6925 Xpush_spring_w, FALSE, FALSE,
6926 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6930 Xdynamite, TRUE, FALSE,
6931 EL_EM_DYNAMITE, -1, -1
6934 Ydynamite_blank, FALSE, FALSE,
6935 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6938 Xdynamite_1, TRUE, FALSE,
6939 EL_EM_DYNAMITE_ACTIVE, -1, -1
6942 Xdynamite_2, FALSE, FALSE,
6943 EL_EM_DYNAMITE_ACTIVE, -1, -1
6946 Xdynamite_3, FALSE, FALSE,
6947 EL_EM_DYNAMITE_ACTIVE, -1, -1
6950 Xdynamite_4, FALSE, FALSE,
6951 EL_EM_DYNAMITE_ACTIVE, -1, -1
6955 Xkey_1, TRUE, FALSE,
6959 Xkey_2, TRUE, FALSE,
6963 Xkey_3, TRUE, FALSE,
6967 Xkey_4, TRUE, FALSE,
6971 Xkey_5, TRUE, FALSE,
6972 EL_EMC_KEY_5, -1, -1
6975 Xkey_6, TRUE, FALSE,
6976 EL_EMC_KEY_6, -1, -1
6979 Xkey_7, TRUE, FALSE,
6980 EL_EMC_KEY_7, -1, -1
6983 Xkey_8, TRUE, FALSE,
6984 EL_EMC_KEY_8, -1, -1
6988 Xdoor_1, TRUE, FALSE,
6989 EL_EM_GATE_1, -1, -1
6992 Xdoor_2, TRUE, FALSE,
6993 EL_EM_GATE_2, -1, -1
6996 Xdoor_3, TRUE, FALSE,
6997 EL_EM_GATE_3, -1, -1
7000 Xdoor_4, TRUE, FALSE,
7001 EL_EM_GATE_4, -1, -1
7004 Xdoor_5, TRUE, FALSE,
7005 EL_EMC_GATE_5, -1, -1
7008 Xdoor_6, TRUE, FALSE,
7009 EL_EMC_GATE_6, -1, -1
7012 Xdoor_7, TRUE, FALSE,
7013 EL_EMC_GATE_7, -1, -1
7016 Xdoor_8, TRUE, FALSE,
7017 EL_EMC_GATE_8, -1, -1
7021 Xfake_door_1, TRUE, FALSE,
7022 EL_EM_GATE_1_GRAY, -1, -1
7025 Xfake_door_2, TRUE, FALSE,
7026 EL_EM_GATE_2_GRAY, -1, -1
7029 Xfake_door_3, TRUE, FALSE,
7030 EL_EM_GATE_3_GRAY, -1, -1
7033 Xfake_door_4, TRUE, FALSE,
7034 EL_EM_GATE_4_GRAY, -1, -1
7037 Xfake_door_5, TRUE, FALSE,
7038 EL_EMC_GATE_5_GRAY, -1, -1
7041 Xfake_door_6, TRUE, FALSE,
7042 EL_EMC_GATE_6_GRAY, -1, -1
7045 Xfake_door_7, TRUE, FALSE,
7046 EL_EMC_GATE_7_GRAY, -1, -1
7049 Xfake_door_8, TRUE, FALSE,
7050 EL_EMC_GATE_8_GRAY, -1, -1
7054 Xballoon, TRUE, FALSE,
7058 Yballoon_n, FALSE, FALSE,
7059 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7062 Yballoon_nB, FALSE, TRUE,
7063 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7066 Yballoon_e, FALSE, FALSE,
7067 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7070 Yballoon_eB, FALSE, TRUE,
7071 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7074 Yballoon_s, FALSE, FALSE,
7075 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7078 Yballoon_sB, FALSE, TRUE,
7079 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7082 Yballoon_w, FALSE, FALSE,
7083 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7086 Yballoon_wB, FALSE, TRUE,
7087 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7091 Xball_1, TRUE, FALSE,
7092 EL_EMC_MAGIC_BALL, -1, -1
7095 Yball_1, FALSE, FALSE,
7096 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7099 Xball_2, FALSE, FALSE,
7100 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7103 Yball_2, FALSE, FALSE,
7104 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7107 Yball_blank, FALSE, FALSE,
7108 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7112 Xamoeba_1, TRUE, FALSE,
7113 EL_AMOEBA_DRY, ACTION_OTHER, -1
7116 Xamoeba_2, FALSE, FALSE,
7117 EL_AMOEBA_DRY, ACTION_OTHER, -1
7120 Xamoeba_3, FALSE, FALSE,
7121 EL_AMOEBA_DRY, ACTION_OTHER, -1
7124 Xamoeba_4, FALSE, FALSE,
7125 EL_AMOEBA_DRY, ACTION_OTHER, -1
7128 Xamoeba_5, TRUE, FALSE,
7129 EL_AMOEBA_WET, ACTION_OTHER, -1
7132 Xamoeba_6, FALSE, FALSE,
7133 EL_AMOEBA_WET, ACTION_OTHER, -1
7136 Xamoeba_7, FALSE, FALSE,
7137 EL_AMOEBA_WET, ACTION_OTHER, -1
7140 Xamoeba_8, FALSE, FALSE,
7141 EL_AMOEBA_WET, ACTION_OTHER, -1
7146 EL_AMOEBA_DROP, ACTION_GROWING, -1
7149 Xdrip_fall, FALSE, FALSE,
7150 EL_AMOEBA_DROP, -1, -1
7153 Xdrip_stretch, FALSE, FALSE,
7154 EL_AMOEBA_DROP, ACTION_FALLING, -1
7157 Xdrip_stretchB, FALSE, TRUE,
7158 EL_AMOEBA_DROP, ACTION_FALLING, -1
7161 Ydrip_1_s, FALSE, FALSE,
7162 EL_AMOEBA_DROP, ACTION_FALLING, -1
7165 Ydrip_1_sB, FALSE, TRUE,
7166 EL_AMOEBA_DROP, ACTION_FALLING, -1
7169 Ydrip_2_s, FALSE, FALSE,
7170 EL_AMOEBA_DROP, ACTION_FALLING, -1
7173 Ydrip_2_sB, FALSE, TRUE,
7174 EL_AMOEBA_DROP, ACTION_FALLING, -1
7178 Xwonderwall, TRUE, FALSE,
7179 EL_MAGIC_WALL, -1, -1
7182 Ywonderwall, FALSE, FALSE,
7183 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7187 Xwheel, TRUE, FALSE,
7188 EL_ROBOT_WHEEL, -1, -1
7191 Ywheel, FALSE, FALSE,
7192 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7196 Xswitch, TRUE, FALSE,
7197 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7200 Yswitch, FALSE, FALSE,
7201 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7205 Xbumper, TRUE, FALSE,
7206 EL_EMC_SPRING_BUMPER, -1, -1
7209 Ybumper, FALSE, FALSE,
7210 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7214 Xacid_nw, TRUE, FALSE,
7215 EL_ACID_POOL_TOPLEFT, -1, -1
7218 Xacid_ne, TRUE, FALSE,
7219 EL_ACID_POOL_TOPRIGHT, -1, -1
7222 Xacid_sw, TRUE, FALSE,
7223 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7226 Xacid_s, TRUE, FALSE,
7227 EL_ACID_POOL_BOTTOM, -1, -1
7230 Xacid_se, TRUE, FALSE,
7231 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7235 Xfake_blank, TRUE, FALSE,
7236 EL_INVISIBLE_WALL, -1, -1
7239 Yfake_blank, FALSE, FALSE,
7240 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7244 Xfake_grass, TRUE, FALSE,
7245 EL_EMC_FAKE_GRASS, -1, -1
7248 Yfake_grass, FALSE, FALSE,
7249 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7253 Xfake_amoeba, TRUE, FALSE,
7254 EL_EMC_DRIPPER, -1, -1
7257 Yfake_amoeba, FALSE, FALSE,
7258 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7262 Xlenses, TRUE, FALSE,
7263 EL_EMC_LENSES, -1, -1
7267 Xmagnify, TRUE, FALSE,
7268 EL_EMC_MAGNIFIER, -1, -1
7273 EL_QUICKSAND_EMPTY, -1, -1
7276 Xsand_stone, TRUE, FALSE,
7277 EL_QUICKSAND_FULL, -1, -1
7280 Xsand_stonein_1, FALSE, TRUE,
7281 EL_ROCK, ACTION_FILLING, -1
7284 Xsand_stonein_2, FALSE, TRUE,
7285 EL_ROCK, ACTION_FILLING, -1
7288 Xsand_stonein_3, FALSE, TRUE,
7289 EL_ROCK, ACTION_FILLING, -1
7292 Xsand_stonein_4, FALSE, TRUE,
7293 EL_ROCK, ACTION_FILLING, -1
7296 Xsand_sandstone_1, FALSE, FALSE,
7297 EL_QUICKSAND_FILLING, -1, -1
7300 Xsand_sandstone_2, FALSE, FALSE,
7301 EL_QUICKSAND_FILLING, -1, -1
7304 Xsand_sandstone_3, FALSE, FALSE,
7305 EL_QUICKSAND_FILLING, -1, -1
7308 Xsand_sandstone_4, FALSE, FALSE,
7309 EL_QUICKSAND_FILLING, -1, -1
7312 Xsand_stonesand_1, FALSE, FALSE,
7313 EL_QUICKSAND_EMPTYING, -1, -1
7316 Xsand_stonesand_2, FALSE, FALSE,
7317 EL_QUICKSAND_EMPTYING, -1, -1
7320 Xsand_stonesand_3, FALSE, FALSE,
7321 EL_QUICKSAND_EMPTYING, -1, -1
7324 Xsand_stonesand_4, FALSE, FALSE,
7325 EL_QUICKSAND_EMPTYING, -1, -1
7328 Xsand_stoneout_1, FALSE, FALSE,
7329 EL_ROCK, ACTION_EMPTYING, -1
7332 Xsand_stoneout_2, FALSE, FALSE,
7333 EL_ROCK, ACTION_EMPTYING, -1
7336 Xsand_stonesand_quickout_1, FALSE, FALSE,
7337 EL_QUICKSAND_EMPTYING, -1, -1
7340 Xsand_stonesand_quickout_2, FALSE, FALSE,
7341 EL_QUICKSAND_EMPTYING, -1, -1
7345 Xslide_ns, TRUE, FALSE,
7346 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7349 Yslide_ns_blank, FALSE, FALSE,
7350 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7353 Xslide_ew, TRUE, FALSE,
7354 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7357 Yslide_ew_blank, FALSE, FALSE,
7358 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7362 Xwind_n, TRUE, FALSE,
7363 EL_BALLOON_SWITCH_UP, -1, -1
7366 Xwind_e, TRUE, FALSE,
7367 EL_BALLOON_SWITCH_RIGHT, -1, -1
7370 Xwind_s, TRUE, FALSE,
7371 EL_BALLOON_SWITCH_DOWN, -1, -1
7374 Xwind_w, TRUE, FALSE,
7375 EL_BALLOON_SWITCH_LEFT, -1, -1
7378 Xwind_any, TRUE, FALSE,
7379 EL_BALLOON_SWITCH_ANY, -1, -1
7382 Xwind_stop, TRUE, FALSE,
7383 EL_BALLOON_SWITCH_NONE, -1, -1
7388 EL_EM_EXIT_CLOSED, -1, -1
7391 Xexit_1, TRUE, FALSE,
7392 EL_EM_EXIT_OPEN, -1, -1
7395 Xexit_2, FALSE, FALSE,
7396 EL_EM_EXIT_OPEN, -1, -1
7399 Xexit_3, FALSE, FALSE,
7400 EL_EM_EXIT_OPEN, -1, -1
7404 Xpause, FALSE, FALSE,
7409 Xwall_1, TRUE, FALSE,
7413 Xwall_2, TRUE, FALSE,
7414 EL_EMC_WALL_14, -1, -1
7417 Xwall_3, TRUE, FALSE,
7418 EL_EMC_WALL_15, -1, -1
7421 Xwall_4, TRUE, FALSE,
7422 EL_EMC_WALL_16, -1, -1
7426 Xroundwall_1, TRUE, FALSE,
7427 EL_WALL_SLIPPERY, -1, -1
7430 Xroundwall_2, TRUE, FALSE,
7431 EL_EMC_WALL_SLIPPERY_2, -1, -1
7434 Xroundwall_3, TRUE, FALSE,
7435 EL_EMC_WALL_SLIPPERY_3, -1, -1
7438 Xroundwall_4, TRUE, FALSE,
7439 EL_EMC_WALL_SLIPPERY_4, -1, -1
7443 Xsteel_1, TRUE, FALSE,
7444 EL_STEELWALL, -1, -1
7447 Xsteel_2, TRUE, FALSE,
7448 EL_EMC_STEELWALL_2, -1, -1
7451 Xsteel_3, TRUE, FALSE,
7452 EL_EMC_STEELWALL_3, -1, -1
7455 Xsteel_4, TRUE, FALSE,
7456 EL_EMC_STEELWALL_4, -1, -1
7460 Xdecor_1, TRUE, FALSE,
7461 EL_EMC_WALL_8, -1, -1
7464 Xdecor_2, TRUE, FALSE,
7465 EL_EMC_WALL_6, -1, -1
7468 Xdecor_3, TRUE, FALSE,
7469 EL_EMC_WALL_4, -1, -1
7472 Xdecor_4, TRUE, FALSE,
7473 EL_EMC_WALL_7, -1, -1
7476 Xdecor_5, TRUE, FALSE,
7477 EL_EMC_WALL_5, -1, -1
7480 Xdecor_6, TRUE, FALSE,
7481 EL_EMC_WALL_9, -1, -1
7484 Xdecor_7, TRUE, FALSE,
7485 EL_EMC_WALL_10, -1, -1
7488 Xdecor_8, TRUE, FALSE,
7489 EL_EMC_WALL_1, -1, -1
7492 Xdecor_9, TRUE, FALSE,
7493 EL_EMC_WALL_2, -1, -1
7496 Xdecor_10, TRUE, FALSE,
7497 EL_EMC_WALL_3, -1, -1
7500 Xdecor_11, TRUE, FALSE,
7501 EL_EMC_WALL_11, -1, -1
7504 Xdecor_12, TRUE, FALSE,
7505 EL_EMC_WALL_12, -1, -1
7509 Xalpha_0, TRUE, FALSE,
7510 EL_CHAR('0'), -1, -1
7513 Xalpha_1, TRUE, FALSE,
7514 EL_CHAR('1'), -1, -1
7517 Xalpha_2, TRUE, FALSE,
7518 EL_CHAR('2'), -1, -1
7521 Xalpha_3, TRUE, FALSE,
7522 EL_CHAR('3'), -1, -1
7525 Xalpha_4, TRUE, FALSE,
7526 EL_CHAR('4'), -1, -1
7529 Xalpha_5, TRUE, FALSE,
7530 EL_CHAR('5'), -1, -1
7533 Xalpha_6, TRUE, FALSE,
7534 EL_CHAR('6'), -1, -1
7537 Xalpha_7, TRUE, FALSE,
7538 EL_CHAR('7'), -1, -1
7541 Xalpha_8, TRUE, FALSE,
7542 EL_CHAR('8'), -1, -1
7545 Xalpha_9, TRUE, FALSE,
7546 EL_CHAR('9'), -1, -1
7549 Xalpha_excla, TRUE, FALSE,
7550 EL_CHAR('!'), -1, -1
7553 Xalpha_apost, TRUE, FALSE,
7554 EL_CHAR('\''), -1, -1
7557 Xalpha_comma, TRUE, FALSE,
7558 EL_CHAR(','), -1, -1
7561 Xalpha_minus, TRUE, FALSE,
7562 EL_CHAR('-'), -1, -1
7565 Xalpha_perio, TRUE, FALSE,
7566 EL_CHAR('.'), -1, -1
7569 Xalpha_colon, TRUE, FALSE,
7570 EL_CHAR(':'), -1, -1
7573 Xalpha_quest, TRUE, FALSE,
7574 EL_CHAR('?'), -1, -1
7577 Xalpha_a, TRUE, FALSE,
7578 EL_CHAR('A'), -1, -1
7581 Xalpha_b, TRUE, FALSE,
7582 EL_CHAR('B'), -1, -1
7585 Xalpha_c, TRUE, FALSE,
7586 EL_CHAR('C'), -1, -1
7589 Xalpha_d, TRUE, FALSE,
7590 EL_CHAR('D'), -1, -1
7593 Xalpha_e, TRUE, FALSE,
7594 EL_CHAR('E'), -1, -1
7597 Xalpha_f, TRUE, FALSE,
7598 EL_CHAR('F'), -1, -1
7601 Xalpha_g, TRUE, FALSE,
7602 EL_CHAR('G'), -1, -1
7605 Xalpha_h, TRUE, FALSE,
7606 EL_CHAR('H'), -1, -1
7609 Xalpha_i, TRUE, FALSE,
7610 EL_CHAR('I'), -1, -1
7613 Xalpha_j, TRUE, FALSE,
7614 EL_CHAR('J'), -1, -1
7617 Xalpha_k, TRUE, FALSE,
7618 EL_CHAR('K'), -1, -1
7621 Xalpha_l, TRUE, FALSE,
7622 EL_CHAR('L'), -1, -1
7625 Xalpha_m, TRUE, FALSE,
7626 EL_CHAR('M'), -1, -1
7629 Xalpha_n, TRUE, FALSE,
7630 EL_CHAR('N'), -1, -1
7633 Xalpha_o, TRUE, FALSE,
7634 EL_CHAR('O'), -1, -1
7637 Xalpha_p, TRUE, FALSE,
7638 EL_CHAR('P'), -1, -1
7641 Xalpha_q, TRUE, FALSE,
7642 EL_CHAR('Q'), -1, -1
7645 Xalpha_r, TRUE, FALSE,
7646 EL_CHAR('R'), -1, -1
7649 Xalpha_s, TRUE, FALSE,
7650 EL_CHAR('S'), -1, -1
7653 Xalpha_t, TRUE, FALSE,
7654 EL_CHAR('T'), -1, -1
7657 Xalpha_u, TRUE, FALSE,
7658 EL_CHAR('U'), -1, -1
7661 Xalpha_v, TRUE, FALSE,
7662 EL_CHAR('V'), -1, -1
7665 Xalpha_w, TRUE, FALSE,
7666 EL_CHAR('W'), -1, -1
7669 Xalpha_x, TRUE, FALSE,
7670 EL_CHAR('X'), -1, -1
7673 Xalpha_y, TRUE, FALSE,
7674 EL_CHAR('Y'), -1, -1
7677 Xalpha_z, TRUE, FALSE,
7678 EL_CHAR('Z'), -1, -1
7681 Xalpha_arrow_e, TRUE, FALSE,
7682 EL_CHAR('>'), -1, -1
7685 Xalpha_arrow_w, TRUE, FALSE,
7686 EL_CHAR('<'), -1, -1
7689 Xalpha_copyr, TRUE, FALSE,
7690 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7694 Ykey_1_blank, FALSE, FALSE,
7695 EL_EM_KEY_1, ACTION_COLLECTING, -1
7698 Ykey_2_blank, FALSE, FALSE,
7699 EL_EM_KEY_2, ACTION_COLLECTING, -1
7702 Ykey_3_blank, FALSE, FALSE,
7703 EL_EM_KEY_3, ACTION_COLLECTING, -1
7706 Ykey_4_blank, FALSE, FALSE,
7707 EL_EM_KEY_4, ACTION_COLLECTING, -1
7710 Ykey_5_blank, FALSE, FALSE,
7711 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7714 Ykey_6_blank, FALSE, FALSE,
7715 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7718 Ykey_7_blank, FALSE, FALSE,
7719 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7722 Ykey_8_blank, FALSE, FALSE,
7723 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7726 Ylenses_blank, FALSE, FALSE,
7727 EL_EMC_LENSES, ACTION_COLLECTING, -1
7730 Ymagnify_blank, FALSE, FALSE,
7731 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7734 Ygrass_blank, FALSE, FALSE,
7735 EL_EMC_GRASS, ACTION_SNAPPING, -1
7738 Ydirt_blank, FALSE, FALSE,
7739 EL_SAND, ACTION_SNAPPING, -1
7748 static struct Mapping_EM_to_RND_player
7757 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7761 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7765 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7769 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7773 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7777 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7781 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7785 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7789 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7793 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7797 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7801 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7805 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7809 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7813 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7817 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7821 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7825 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7829 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7833 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7837 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7841 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7845 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7849 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7853 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7857 EL_PLAYER_1, ACTION_DEFAULT, -1,
7861 EL_PLAYER_2, ACTION_DEFAULT, -1,
7865 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7869 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7873 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7877 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7881 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7885 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7889 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7893 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7897 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7901 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7905 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7909 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7913 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7917 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7921 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7925 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7929 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7933 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7937 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7941 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7945 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7949 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7953 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7957 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7961 EL_PLAYER_3, ACTION_DEFAULT, -1,
7965 EL_PLAYER_4, ACTION_DEFAULT, -1,
7974 int map_element_RND_to_EM_cave(int element_rnd)
7976 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7977 static boolean mapping_initialized = FALSE;
7979 if (!mapping_initialized)
7983 // return "Xalpha_quest" for all undefined elements in mapping array
7984 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7985 mapping_RND_to_EM[i] = Xalpha_quest;
7987 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7988 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7989 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7990 em_object_mapping_list[i].element_em;
7992 mapping_initialized = TRUE;
7995 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7997 Warn("invalid RND level element %d", element_rnd);
8002 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
8005 int map_element_EM_to_RND_cave(int element_em_cave)
8007 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8008 static boolean mapping_initialized = FALSE;
8010 if (!mapping_initialized)
8014 // return "EL_UNKNOWN" for all undefined elements in mapping array
8015 for (i = 0; i < GAME_TILE_MAX; i++)
8016 mapping_EM_to_RND[i] = EL_UNKNOWN;
8018 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8019 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8020 em_object_mapping_list[i].element_rnd;
8022 mapping_initialized = TRUE;
8025 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8027 Warn("invalid EM cave element %d", element_em_cave);
8032 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8035 int map_element_EM_to_RND_game(int element_em_game)
8037 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8038 static boolean mapping_initialized = FALSE;
8040 if (!mapping_initialized)
8044 // return "EL_UNKNOWN" for all undefined elements in mapping array
8045 for (i = 0; i < GAME_TILE_MAX; i++)
8046 mapping_EM_to_RND[i] = EL_UNKNOWN;
8048 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8049 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8050 em_object_mapping_list[i].element_rnd;
8052 mapping_initialized = TRUE;
8055 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8057 Warn("invalid EM game element %d", element_em_game);
8062 return mapping_EM_to_RND[element_em_game];
8065 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8067 struct LevelInfo_EM *level_em = level->native_em_level;
8068 struct CAVE *cav = level_em->cav;
8071 for (i = 0; i < GAME_TILE_MAX; i++)
8072 cav->android_array[i] = Cblank;
8074 for (i = 0; i < level->num_android_clone_elements; i++)
8076 int element_rnd = level->android_clone_element[i];
8077 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8079 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8080 if (em_object_mapping_list[j].element_rnd == element_rnd)
8081 cav->android_array[em_object_mapping_list[j].element_em] =
8086 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8088 struct LevelInfo_EM *level_em = level->native_em_level;
8089 struct CAVE *cav = level_em->cav;
8092 level->num_android_clone_elements = 0;
8094 for (i = 0; i < GAME_TILE_MAX; i++)
8096 int element_em_cave = cav->android_array[i];
8098 boolean element_found = FALSE;
8100 if (element_em_cave == Cblank)
8103 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8105 for (j = 0; j < level->num_android_clone_elements; j++)
8106 if (level->android_clone_element[j] == element_rnd)
8107 element_found = TRUE;
8111 level->android_clone_element[level->num_android_clone_elements++] =
8114 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8119 if (level->num_android_clone_elements == 0)
8121 level->num_android_clone_elements = 1;
8122 level->android_clone_element[0] = EL_EMPTY;
8126 int map_direction_RND_to_EM(int direction)
8128 return (direction == MV_UP ? 0 :
8129 direction == MV_RIGHT ? 1 :
8130 direction == MV_DOWN ? 2 :
8131 direction == MV_LEFT ? 3 :
8135 int map_direction_EM_to_RND(int direction)
8137 return (direction == 0 ? MV_UP :
8138 direction == 1 ? MV_RIGHT :
8139 direction == 2 ? MV_DOWN :
8140 direction == 3 ? MV_LEFT :
8144 int map_element_RND_to_SP(int element_rnd)
8146 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8148 if (element_rnd >= EL_SP_START &&
8149 element_rnd <= EL_SP_END)
8150 element_sp = element_rnd - EL_SP_START;
8151 else if (element_rnd == EL_EMPTY_SPACE)
8153 else if (element_rnd == EL_INVISIBLE_WALL)
8159 int map_element_SP_to_RND(int element_sp)
8161 int element_rnd = EL_UNKNOWN;
8163 if (element_sp >= 0x00 &&
8165 element_rnd = EL_SP_START + element_sp;
8166 else if (element_sp == 0x28)
8167 element_rnd = EL_INVISIBLE_WALL;
8172 int map_action_SP_to_RND(int action_sp)
8176 case actActive: return ACTION_ACTIVE;
8177 case actImpact: return ACTION_IMPACT;
8178 case actExploding: return ACTION_EXPLODING;
8179 case actDigging: return ACTION_DIGGING;
8180 case actSnapping: return ACTION_SNAPPING;
8181 case actCollecting: return ACTION_COLLECTING;
8182 case actPassing: return ACTION_PASSING;
8183 case actPushing: return ACTION_PUSHING;
8184 case actDropping: return ACTION_DROPPING;
8186 default: return ACTION_DEFAULT;
8190 int map_element_RND_to_MM(int element_rnd)
8192 return (element_rnd >= EL_MM_START_1 &&
8193 element_rnd <= EL_MM_END_1 ?
8194 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8196 element_rnd >= EL_MM_START_2 &&
8197 element_rnd <= EL_MM_END_2 ?
8198 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8200 element_rnd >= EL_CHAR_START &&
8201 element_rnd <= EL_CHAR_END ?
8202 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8204 element_rnd >= EL_MM_RUNTIME_START &&
8205 element_rnd <= EL_MM_RUNTIME_END ?
8206 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8208 element_rnd >= EL_MM_DUMMY_START &&
8209 element_rnd <= EL_MM_DUMMY_END ?
8210 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
8212 EL_MM_EMPTY_NATIVE);
8215 int map_element_MM_to_RND(int element_mm)
8217 return (element_mm == EL_MM_EMPTY_NATIVE ||
8218 element_mm == EL_DF_EMPTY_NATIVE ?
8221 element_mm >= EL_MM_START_1_NATIVE &&
8222 element_mm <= EL_MM_END_1_NATIVE ?
8223 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8225 element_mm >= EL_MM_START_2_NATIVE &&
8226 element_mm <= EL_MM_END_2_NATIVE ?
8227 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8229 element_mm >= EL_MM_CHAR_START_NATIVE &&
8230 element_mm <= EL_MM_CHAR_END_NATIVE ?
8231 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8233 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8234 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8235 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8237 element_mm >= EL_MM_DUMMY_START_NATIVE &&
8238 element_mm <= EL_MM_DUMMY_END_NATIVE ?
8239 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
8244 int map_action_MM_to_RND(int action_mm)
8246 // all MM actions are defined to exactly match their RND counterparts
8250 int map_sound_MM_to_RND(int sound_mm)
8254 case SND_MM_GAME_LEVELTIME_CHARGING:
8255 return SND_GAME_LEVELTIME_CHARGING;
8257 case SND_MM_GAME_HEALTH_CHARGING:
8258 return SND_GAME_HEALTH_CHARGING;
8261 return SND_UNDEFINED;
8265 int map_mm_wall_element(int element)
8267 return (element >= EL_MM_STEEL_WALL_START &&
8268 element <= EL_MM_STEEL_WALL_END ?
8271 element >= EL_MM_WOODEN_WALL_START &&
8272 element <= EL_MM_WOODEN_WALL_END ?
8275 element >= EL_MM_ICE_WALL_START &&
8276 element <= EL_MM_ICE_WALL_END ?
8279 element >= EL_MM_AMOEBA_WALL_START &&
8280 element <= EL_MM_AMOEBA_WALL_END ?
8283 element >= EL_DF_STEEL_WALL_START &&
8284 element <= EL_DF_STEEL_WALL_END ?
8287 element >= EL_DF_WOODEN_WALL_START &&
8288 element <= EL_DF_WOODEN_WALL_END ?
8294 int map_mm_wall_element_editor(int element)
8298 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8299 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8300 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8301 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8302 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8303 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8305 default: return element;
8309 int get_next_element(int element)
8313 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8314 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8315 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8316 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8317 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8318 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8319 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8320 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8321 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8322 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8323 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8325 default: return element;
8329 int el2img_mm(int element_mm)
8331 return el2img(map_element_MM_to_RND(element_mm));
8334 int el_act_dir2img(int element, int action, int direction)
8336 element = GFX_ELEMENT(element);
8337 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8339 // direction_graphic[][] == graphic[] for undefined direction graphics
8340 return element_info[element].direction_graphic[action][direction];
8343 static int el_act_dir2crm(int element, int action, int direction)
8345 element = GFX_ELEMENT(element);
8346 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8348 // direction_graphic[][] == graphic[] for undefined direction graphics
8349 return element_info[element].direction_crumbled[action][direction];
8352 int el_act2img(int element, int action)
8354 element = GFX_ELEMENT(element);
8356 return element_info[element].graphic[action];
8359 int el_act2crm(int element, int action)
8361 element = GFX_ELEMENT(element);
8363 return element_info[element].crumbled[action];
8366 int el_dir2img(int element, int direction)
8368 element = GFX_ELEMENT(element);
8370 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8373 int el2baseimg(int element)
8375 return element_info[element].graphic[ACTION_DEFAULT];
8378 int el2img(int element)
8380 element = GFX_ELEMENT(element);
8382 return element_info[element].graphic[ACTION_DEFAULT];
8385 int el2edimg(int element)
8387 element = GFX_ELEMENT(element);
8389 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8392 int el2preimg(int element)
8394 element = GFX_ELEMENT(element);
8396 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8399 int el2panelimg(int element)
8401 element = GFX_ELEMENT(element);
8403 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8406 int font2baseimg(int font_nr)
8408 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8411 int getBeltNrFromBeltElement(int element)
8413 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8414 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8415 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8418 int getBeltNrFromBeltActiveElement(int element)
8420 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8421 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8422 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8425 int getBeltNrFromBeltSwitchElement(int element)
8427 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8428 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8429 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8432 int getBeltDirNrFromBeltElement(int element)
8434 static int belt_base_element[4] =
8436 EL_CONVEYOR_BELT_1_LEFT,
8437 EL_CONVEYOR_BELT_2_LEFT,
8438 EL_CONVEYOR_BELT_3_LEFT,
8439 EL_CONVEYOR_BELT_4_LEFT
8442 int belt_nr = getBeltNrFromBeltElement(element);
8443 int belt_dir_nr = element - belt_base_element[belt_nr];
8445 return (belt_dir_nr % 3);
8448 int getBeltDirNrFromBeltSwitchElement(int element)
8450 static int belt_base_element[4] =
8452 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8453 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8454 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8455 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8458 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8459 int belt_dir_nr = element - belt_base_element[belt_nr];
8461 return (belt_dir_nr % 3);
8464 int getBeltDirFromBeltElement(int element)
8466 static int belt_move_dir[3] =
8473 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8475 return belt_move_dir[belt_dir_nr];
8478 int getBeltDirFromBeltSwitchElement(int element)
8480 static int belt_move_dir[3] =
8487 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8489 return belt_move_dir[belt_dir_nr];
8492 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8494 static int belt_base_element[4] =
8496 EL_CONVEYOR_BELT_1_LEFT,
8497 EL_CONVEYOR_BELT_2_LEFT,
8498 EL_CONVEYOR_BELT_3_LEFT,
8499 EL_CONVEYOR_BELT_4_LEFT
8502 return belt_base_element[belt_nr] + belt_dir_nr;
8505 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8507 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8509 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8512 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8514 static int belt_base_element[4] =
8516 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8517 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8518 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8519 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8522 return belt_base_element[belt_nr] + belt_dir_nr;
8525 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8527 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8529 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8532 boolean swapTiles_EM(boolean is_pre_emc_cave)
8534 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8537 boolean getTeamMode_EM(void)
8539 return game.team_mode || network_playing;
8542 boolean isActivePlayer_EM(int player_nr)
8544 return stored_player[player_nr].active;
8547 unsigned int InitRND(int seed)
8549 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8550 return InitEngineRandom_EM(seed);
8551 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8552 return InitEngineRandom_SP(seed);
8553 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8554 return InitEngineRandom_MM(seed);
8556 return InitEngineRandom_RND(seed);
8559 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8560 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8562 static int get_effective_element_EM(int tile, int frame_em)
8564 int element = object_mapping[tile].element_rnd;
8565 int action = object_mapping[tile].action;
8566 boolean is_backside = object_mapping[tile].is_backside;
8567 boolean action_removing = (action == ACTION_DIGGING ||
8568 action == ACTION_SNAPPING ||
8569 action == ACTION_COLLECTING);
8577 return (frame_em > 5 ? EL_EMPTY : element);
8583 else // frame_em == 7
8594 case Ydiamond_stone:
8598 case Xdrip_stretchB:
8614 case Ymagnify_blank:
8617 case Xsand_stonein_1:
8618 case Xsand_stonein_2:
8619 case Xsand_stonein_3:
8620 case Xsand_stonein_4:
8624 return (is_backside || action_removing ? EL_EMPTY : element);
8629 static boolean check_linear_animation_EM(int tile)
8633 case Xsand_stonesand_1:
8634 case Xsand_stonesand_quickout_1:
8635 case Xsand_sandstone_1:
8636 case Xsand_stonein_1:
8637 case Xsand_stoneout_1:
8665 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8666 boolean has_crumbled_graphics,
8667 int crumbled, int sync_frame)
8669 // if element can be crumbled, but certain action graphics are just empty
8670 // space (like instantly snapping sand to empty space in 1 frame), do not
8671 // treat these empty space graphics as crumbled graphics in EMC engine
8672 if (crumbled == IMG_EMPTY_SPACE)
8673 has_crumbled_graphics = FALSE;
8675 if (has_crumbled_graphics)
8677 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8678 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8679 g_crumbled->anim_delay,
8680 g_crumbled->anim_mode,
8681 g_crumbled->anim_start_frame,
8684 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8685 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8687 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8688 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8690 g_em->has_crumbled_graphics = TRUE;
8694 g_em->crumbled_bitmap = NULL;
8695 g_em->crumbled_src_x = 0;
8696 g_em->crumbled_src_y = 0;
8697 g_em->crumbled_border_size = 0;
8698 g_em->crumbled_tile_size = 0;
8700 g_em->has_crumbled_graphics = FALSE;
8705 void ResetGfxAnimation_EM(int x, int y, int tile)
8711 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8712 int tile, int frame_em, int x, int y)
8714 int action = object_mapping[tile].action;
8715 int direction = object_mapping[tile].direction;
8716 int effective_element = get_effective_element_EM(tile, frame_em);
8717 int graphic = (direction == MV_NONE ?
8718 el_act2img(effective_element, action) :
8719 el_act_dir2img(effective_element, action, direction));
8720 struct GraphicInfo *g = &graphic_info[graphic];
8722 boolean action_removing = (action == ACTION_DIGGING ||
8723 action == ACTION_SNAPPING ||
8724 action == ACTION_COLLECTING);
8725 boolean action_moving = (action == ACTION_FALLING ||
8726 action == ACTION_MOVING ||
8727 action == ACTION_PUSHING ||
8728 action == ACTION_EATING ||
8729 action == ACTION_FILLING ||
8730 action == ACTION_EMPTYING);
8731 boolean action_falling = (action == ACTION_FALLING ||
8732 action == ACTION_FILLING ||
8733 action == ACTION_EMPTYING);
8735 // special case: graphic uses "2nd movement tile" and has defined
8736 // 7 frames for movement animation (or less) => use default graphic
8737 // for last (8th) frame which ends the movement animation
8738 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8740 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8741 graphic = (direction == MV_NONE ?
8742 el_act2img(effective_element, action) :
8743 el_act_dir2img(effective_element, action, direction));
8745 g = &graphic_info[graphic];
8748 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8752 else if (action_moving)
8754 boolean is_backside = object_mapping[tile].is_backside;
8758 int direction = object_mapping[tile].direction;
8759 int move_dir = (action_falling ? MV_DOWN : direction);
8764 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8765 if (g->double_movement && frame_em == 0)
8769 if (move_dir == MV_LEFT)
8770 GfxFrame[x - 1][y] = GfxFrame[x][y];
8771 else if (move_dir == MV_RIGHT)
8772 GfxFrame[x + 1][y] = GfxFrame[x][y];
8773 else if (move_dir == MV_UP)
8774 GfxFrame[x][y - 1] = GfxFrame[x][y];
8775 else if (move_dir == MV_DOWN)
8776 GfxFrame[x][y + 1] = GfxFrame[x][y];
8783 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8784 if (tile == Xsand_stonesand_quickout_1 ||
8785 tile == Xsand_stonesand_quickout_2)
8789 if (graphic_info[graphic].anim_global_sync)
8790 sync_frame = FrameCounter;
8791 else if (graphic_info[graphic].anim_global_anim_sync)
8792 sync_frame = getGlobalAnimSyncFrame();
8793 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8794 sync_frame = GfxFrame[x][y];
8796 sync_frame = 0; // playfield border (pseudo steel)
8798 SetRandomAnimationValue(x, y);
8800 int frame = getAnimationFrame(g->anim_frames,
8803 g->anim_start_frame,
8806 g_em->unique_identifier =
8807 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8810 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8811 int tile, int frame_em, int x, int y)
8813 int action = object_mapping[tile].action;
8814 int direction = object_mapping[tile].direction;
8815 boolean is_backside = object_mapping[tile].is_backside;
8816 int effective_element = get_effective_element_EM(tile, frame_em);
8817 int effective_action = action;
8818 int graphic = (direction == MV_NONE ?
8819 el_act2img(effective_element, effective_action) :
8820 el_act_dir2img(effective_element, effective_action,
8822 int crumbled = (direction == MV_NONE ?
8823 el_act2crm(effective_element, effective_action) :
8824 el_act_dir2crm(effective_element, effective_action,
8826 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8827 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8828 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8829 struct GraphicInfo *g = &graphic_info[graphic];
8832 // special case: graphic uses "2nd movement tile" and has defined
8833 // 7 frames for movement animation (or less) => use default graphic
8834 // for last (8th) frame which ends the movement animation
8835 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8837 effective_action = ACTION_DEFAULT;
8838 graphic = (direction == MV_NONE ?
8839 el_act2img(effective_element, effective_action) :
8840 el_act_dir2img(effective_element, effective_action,
8842 crumbled = (direction == MV_NONE ?
8843 el_act2crm(effective_element, effective_action) :
8844 el_act_dir2crm(effective_element, effective_action,
8847 g = &graphic_info[graphic];
8850 if (graphic_info[graphic].anim_global_sync)
8851 sync_frame = FrameCounter;
8852 else if (graphic_info[graphic].anim_global_anim_sync)
8853 sync_frame = getGlobalAnimSyncFrame();
8854 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8855 sync_frame = GfxFrame[x][y];
8857 sync_frame = 0; // playfield border (pseudo steel)
8859 SetRandomAnimationValue(x, y);
8861 int frame = getAnimationFrame(g->anim_frames,
8864 g->anim_start_frame,
8867 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8868 g->double_movement && is_backside);
8870 // (updating the "crumbled" graphic definitions is probably not really needed,
8871 // as animations for crumbled graphics can't be longer than one EMC cycle)
8872 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8876 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8877 int player_nr, int anim, int frame_em)
8879 int element = player_mapping[player_nr][anim].element_rnd;
8880 int action = player_mapping[player_nr][anim].action;
8881 int direction = player_mapping[player_nr][anim].direction;
8882 int graphic = (direction == MV_NONE ?
8883 el_act2img(element, action) :
8884 el_act_dir2img(element, action, direction));
8885 struct GraphicInfo *g = &graphic_info[graphic];
8888 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8890 stored_player[player_nr].StepFrame = frame_em;
8892 sync_frame = stored_player[player_nr].Frame;
8894 int frame = getAnimationFrame(g->anim_frames,
8897 g->anim_start_frame,
8900 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8901 &g_em->src_x, &g_em->src_y, FALSE);
8904 void InitGraphicInfo_EM(void)
8908 // always start with reliable default values
8909 for (i = 0; i < GAME_TILE_MAX; i++)
8911 object_mapping[i].element_rnd = EL_UNKNOWN;
8912 object_mapping[i].is_backside = FALSE;
8913 object_mapping[i].action = ACTION_DEFAULT;
8914 object_mapping[i].direction = MV_NONE;
8917 // always start with reliable default values
8918 for (p = 0; p < MAX_PLAYERS; p++)
8920 for (i = 0; i < PLY_MAX; i++)
8922 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8923 player_mapping[p][i].action = ACTION_DEFAULT;
8924 player_mapping[p][i].direction = MV_NONE;
8928 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8930 int e = em_object_mapping_list[i].element_em;
8932 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8933 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8935 if (em_object_mapping_list[i].action != -1)
8936 object_mapping[e].action = em_object_mapping_list[i].action;
8938 if (em_object_mapping_list[i].direction != -1)
8939 object_mapping[e].direction =
8940 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8943 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8945 int a = em_player_mapping_list[i].action_em;
8946 int p = em_player_mapping_list[i].player_nr;
8948 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8950 if (em_player_mapping_list[i].action != -1)
8951 player_mapping[p][a].action = em_player_mapping_list[i].action;
8953 if (em_player_mapping_list[i].direction != -1)
8954 player_mapping[p][a].direction =
8955 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8958 for (i = 0; i < GAME_TILE_MAX; i++)
8960 int element = object_mapping[i].element_rnd;
8961 int action = object_mapping[i].action;
8962 int direction = object_mapping[i].direction;
8963 boolean is_backside = object_mapping[i].is_backside;
8964 boolean action_exploding = ((action == ACTION_EXPLODING ||
8965 action == ACTION_SMASHED_BY_ROCK ||
8966 action == ACTION_SMASHED_BY_SPRING) &&
8967 element != EL_DIAMOND);
8968 boolean action_active = (action == ACTION_ACTIVE);
8969 boolean action_other = (action == ACTION_OTHER);
8971 for (j = 0; j < 8; j++)
8973 int effective_element = get_effective_element_EM(i, j);
8974 int effective_action = (j < 7 ? action :
8975 i == Xdrip_stretch ? action :
8976 i == Xdrip_stretchB ? action :
8977 i == Ydrip_1_s ? action :
8978 i == Ydrip_1_sB ? action :
8979 i == Yball_1 ? action :
8980 i == Xball_2 ? action :
8981 i == Yball_2 ? action :
8982 i == Yball_blank ? action :
8983 i == Ykey_1_blank ? action :
8984 i == Ykey_2_blank ? action :
8985 i == Ykey_3_blank ? action :
8986 i == Ykey_4_blank ? action :
8987 i == Ykey_5_blank ? action :
8988 i == Ykey_6_blank ? action :
8989 i == Ykey_7_blank ? action :
8990 i == Ykey_8_blank ? action :
8991 i == Ylenses_blank ? action :
8992 i == Ymagnify_blank ? action :
8993 i == Ygrass_blank ? action :
8994 i == Ydirt_blank ? action :
8995 i == Xsand_stonein_1 ? action :
8996 i == Xsand_stonein_2 ? action :
8997 i == Xsand_stonein_3 ? action :
8998 i == Xsand_stonein_4 ? action :
8999 i == Xsand_stoneout_1 ? action :
9000 i == Xsand_stoneout_2 ? action :
9001 i == Xboom_android ? ACTION_EXPLODING :
9002 action_exploding ? ACTION_EXPLODING :
9003 action_active ? action :
9004 action_other ? action :
9006 int graphic = (el_act_dir2img(effective_element, effective_action,
9008 int crumbled = (el_act_dir2crm(effective_element, effective_action,
9010 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
9011 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
9012 boolean has_action_graphics = (graphic != base_graphic);
9013 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9014 struct GraphicInfo *g = &graphic_info[graphic];
9015 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9018 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9019 boolean special_animation = (action != ACTION_DEFAULT &&
9020 g->anim_frames == 3 &&
9021 g->anim_delay == 2 &&
9022 g->anim_mode & ANIM_LINEAR);
9023 int sync_frame = (i == Xdrip_stretch ? 7 :
9024 i == Xdrip_stretchB ? 7 :
9025 i == Ydrip_2_s ? j + 8 :
9026 i == Ydrip_2_sB ? j + 8 :
9035 i == Xfake_acid_1 ? 0 :
9036 i == Xfake_acid_2 ? 10 :
9037 i == Xfake_acid_3 ? 20 :
9038 i == Xfake_acid_4 ? 30 :
9039 i == Xfake_acid_5 ? 40 :
9040 i == Xfake_acid_6 ? 50 :
9041 i == Xfake_acid_7 ? 60 :
9042 i == Xfake_acid_8 ? 70 :
9043 i == Xfake_acid_1_player ? 0 :
9044 i == Xfake_acid_2_player ? 10 :
9045 i == Xfake_acid_3_player ? 20 :
9046 i == Xfake_acid_4_player ? 30 :
9047 i == Xfake_acid_5_player ? 40 :
9048 i == Xfake_acid_6_player ? 50 :
9049 i == Xfake_acid_7_player ? 60 :
9050 i == Xfake_acid_8_player ? 70 :
9052 i == Yball_2 ? j + 8 :
9053 i == Yball_blank ? j + 1 :
9054 i == Ykey_1_blank ? j + 1 :
9055 i == Ykey_2_blank ? j + 1 :
9056 i == Ykey_3_blank ? j + 1 :
9057 i == Ykey_4_blank ? j + 1 :
9058 i == Ykey_5_blank ? j + 1 :
9059 i == Ykey_6_blank ? j + 1 :
9060 i == Ykey_7_blank ? j + 1 :
9061 i == Ykey_8_blank ? j + 1 :
9062 i == Ylenses_blank ? j + 1 :
9063 i == Ymagnify_blank ? j + 1 :
9064 i == Ygrass_blank ? j + 1 :
9065 i == Ydirt_blank ? j + 1 :
9066 i == Xamoeba_1 ? 0 :
9067 i == Xamoeba_2 ? 1 :
9068 i == Xamoeba_3 ? 2 :
9069 i == Xamoeba_4 ? 3 :
9070 i == Xamoeba_5 ? 0 :
9071 i == Xamoeba_6 ? 1 :
9072 i == Xamoeba_7 ? 2 :
9073 i == Xamoeba_8 ? 3 :
9074 i == Xexit_2 ? j + 8 :
9075 i == Xexit_3 ? j + 16 :
9076 i == Xdynamite_1 ? 0 :
9077 i == Xdynamite_2 ? 8 :
9078 i == Xdynamite_3 ? 16 :
9079 i == Xdynamite_4 ? 24 :
9080 i == Xsand_stonein_1 ? j + 1 :
9081 i == Xsand_stonein_2 ? j + 9 :
9082 i == Xsand_stonein_3 ? j + 17 :
9083 i == Xsand_stonein_4 ? j + 25 :
9084 i == Xsand_stoneout_1 && j == 0 ? 0 :
9085 i == Xsand_stoneout_1 && j == 1 ? 0 :
9086 i == Xsand_stoneout_1 && j == 2 ? 1 :
9087 i == Xsand_stoneout_1 && j == 3 ? 2 :
9088 i == Xsand_stoneout_1 && j == 4 ? 2 :
9089 i == Xsand_stoneout_1 && j == 5 ? 3 :
9090 i == Xsand_stoneout_1 && j == 6 ? 4 :
9091 i == Xsand_stoneout_1 && j == 7 ? 4 :
9092 i == Xsand_stoneout_2 && j == 0 ? 5 :
9093 i == Xsand_stoneout_2 && j == 1 ? 6 :
9094 i == Xsand_stoneout_2 && j == 2 ? 7 :
9095 i == Xsand_stoneout_2 && j == 3 ? 8 :
9096 i == Xsand_stoneout_2 && j == 4 ? 9 :
9097 i == Xsand_stoneout_2 && j == 5 ? 11 :
9098 i == Xsand_stoneout_2 && j == 6 ? 13 :
9099 i == Xsand_stoneout_2 && j == 7 ? 15 :
9100 i == Xboom_bug && j == 1 ? 2 :
9101 i == Xboom_bug && j == 2 ? 2 :
9102 i == Xboom_bug && j == 3 ? 4 :
9103 i == Xboom_bug && j == 4 ? 4 :
9104 i == Xboom_bug && j == 5 ? 2 :
9105 i == Xboom_bug && j == 6 ? 2 :
9106 i == Xboom_bug && j == 7 ? 0 :
9107 i == Xboom_tank && j == 1 ? 2 :
9108 i == Xboom_tank && j == 2 ? 2 :
9109 i == Xboom_tank && j == 3 ? 4 :
9110 i == Xboom_tank && j == 4 ? 4 :
9111 i == Xboom_tank && j == 5 ? 2 :
9112 i == Xboom_tank && j == 6 ? 2 :
9113 i == Xboom_tank && j == 7 ? 0 :
9114 i == Xboom_android && j == 7 ? 6 :
9115 i == Xboom_1 && j == 1 ? 2 :
9116 i == Xboom_1 && j == 2 ? 2 :
9117 i == Xboom_1 && j == 3 ? 4 :
9118 i == Xboom_1 && j == 4 ? 4 :
9119 i == Xboom_1 && j == 5 ? 6 :
9120 i == Xboom_1 && j == 6 ? 6 :
9121 i == Xboom_1 && j == 7 ? 8 :
9122 i == Xboom_2 && j == 0 ? 8 :
9123 i == Xboom_2 && j == 1 ? 8 :
9124 i == Xboom_2 && j == 2 ? 10 :
9125 i == Xboom_2 && j == 3 ? 10 :
9126 i == Xboom_2 && j == 4 ? 10 :
9127 i == Xboom_2 && j == 5 ? 12 :
9128 i == Xboom_2 && j == 6 ? 12 :
9129 i == Xboom_2 && j == 7 ? 12 :
9130 special_animation && j == 4 ? 3 :
9131 effective_action != action ? 0 :
9133 int frame = getAnimationFrame(g->anim_frames,
9136 g->anim_start_frame,
9139 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9140 g->double_movement && is_backside);
9142 g_em->bitmap = src_bitmap;
9143 g_em->src_x = src_x;
9144 g_em->src_y = src_y;
9145 g_em->src_offset_x = 0;
9146 g_em->src_offset_y = 0;
9147 g_em->dst_offset_x = 0;
9148 g_em->dst_offset_y = 0;
9149 g_em->width = TILEX;
9150 g_em->height = TILEY;
9152 g_em->preserve_background = FALSE;
9154 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9157 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9158 effective_action == ACTION_MOVING ||
9159 effective_action == ACTION_PUSHING ||
9160 effective_action == ACTION_EATING)) ||
9161 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9162 effective_action == ACTION_EMPTYING)))
9165 (effective_action == ACTION_FALLING ||
9166 effective_action == ACTION_FILLING ||
9167 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9168 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9169 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9170 int num_steps = (i == Ydrip_1_s ? 16 :
9171 i == Ydrip_1_sB ? 16 :
9172 i == Ydrip_2_s ? 16 :
9173 i == Ydrip_2_sB ? 16 :
9174 i == Xsand_stonein_1 ? 32 :
9175 i == Xsand_stonein_2 ? 32 :
9176 i == Xsand_stonein_3 ? 32 :
9177 i == Xsand_stonein_4 ? 32 :
9178 i == Xsand_stoneout_1 ? 16 :
9179 i == Xsand_stoneout_2 ? 16 : 8);
9180 int cx = ABS(dx) * (TILEX / num_steps);
9181 int cy = ABS(dy) * (TILEY / num_steps);
9182 int step_frame = (i == Ydrip_2_s ? j + 8 :
9183 i == Ydrip_2_sB ? j + 8 :
9184 i == Xsand_stonein_2 ? j + 8 :
9185 i == Xsand_stonein_3 ? j + 16 :
9186 i == Xsand_stonein_4 ? j + 24 :
9187 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9188 int step = (is_backside ? step_frame : num_steps - step_frame);
9190 if (is_backside) // tile where movement starts
9192 if (dx < 0 || dy < 0)
9194 g_em->src_offset_x = cx * step;
9195 g_em->src_offset_y = cy * step;
9199 g_em->dst_offset_x = cx * step;
9200 g_em->dst_offset_y = cy * step;
9203 else // tile where movement ends
9205 if (dx < 0 || dy < 0)
9207 g_em->dst_offset_x = cx * step;
9208 g_em->dst_offset_y = cy * step;
9212 g_em->src_offset_x = cx * step;
9213 g_em->src_offset_y = cy * step;
9217 g_em->width = TILEX - cx * step;
9218 g_em->height = TILEY - cy * step;
9221 // create unique graphic identifier to decide if tile must be redrawn
9222 /* bit 31 - 16 (16 bit): EM style graphic
9223 bit 15 - 12 ( 4 bit): EM style frame
9224 bit 11 - 6 ( 6 bit): graphic width
9225 bit 5 - 0 ( 6 bit): graphic height */
9226 g_em->unique_identifier =
9227 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9231 for (i = 0; i < GAME_TILE_MAX; i++)
9233 for (j = 0; j < 8; j++)
9235 int element = object_mapping[i].element_rnd;
9236 int action = object_mapping[i].action;
9237 int direction = object_mapping[i].direction;
9238 boolean is_backside = object_mapping[i].is_backside;
9239 int graphic_action = el_act_dir2img(element, action, direction);
9240 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9242 if ((action == ACTION_SMASHED_BY_ROCK ||
9243 action == ACTION_SMASHED_BY_SPRING ||
9244 action == ACTION_EATING) &&
9245 graphic_action == graphic_default)
9247 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9248 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9249 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9250 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9253 // no separate animation for "smashed by rock" -- use rock instead
9254 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9255 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9257 g_em->bitmap = g_xx->bitmap;
9258 g_em->src_x = g_xx->src_x;
9259 g_em->src_y = g_xx->src_y;
9260 g_em->src_offset_x = g_xx->src_offset_x;
9261 g_em->src_offset_y = g_xx->src_offset_y;
9262 g_em->dst_offset_x = g_xx->dst_offset_x;
9263 g_em->dst_offset_y = g_xx->dst_offset_y;
9264 g_em->width = g_xx->width;
9265 g_em->height = g_xx->height;
9266 g_em->unique_identifier = g_xx->unique_identifier;
9269 g_em->preserve_background = TRUE;
9274 for (p = 0; p < MAX_PLAYERS; p++)
9276 for (i = 0; i < PLY_MAX; i++)
9278 int element = player_mapping[p][i].element_rnd;
9279 int action = player_mapping[p][i].action;
9280 int direction = player_mapping[p][i].direction;
9282 for (j = 0; j < 8; j++)
9284 int effective_element = element;
9285 int effective_action = action;
9286 int graphic = (direction == MV_NONE ?
9287 el_act2img(effective_element, effective_action) :
9288 el_act_dir2img(effective_element, effective_action,
9290 struct GraphicInfo *g = &graphic_info[graphic];
9291 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9295 int frame = getAnimationFrame(g->anim_frames,
9298 g->anim_start_frame,
9301 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9303 g_em->bitmap = src_bitmap;
9304 g_em->src_x = src_x;
9305 g_em->src_y = src_y;
9306 g_em->src_offset_x = 0;
9307 g_em->src_offset_y = 0;
9308 g_em->dst_offset_x = 0;
9309 g_em->dst_offset_y = 0;
9310 g_em->width = TILEX;
9311 g_em->height = TILEY;
9317 static void CheckSaveEngineSnapshot_EM(int frame,
9318 boolean any_player_moving,
9319 boolean any_player_snapping,
9320 boolean any_player_dropping)
9322 if (frame == 7 && !any_player_dropping)
9324 if (!local_player->was_waiting)
9326 if (!CheckSaveEngineSnapshotToList())
9329 local_player->was_waiting = TRUE;
9332 else if (any_player_moving || any_player_snapping || any_player_dropping)
9334 local_player->was_waiting = FALSE;
9338 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9339 boolean murphy_is_dropping)
9341 if (murphy_is_waiting)
9343 if (!local_player->was_waiting)
9345 if (!CheckSaveEngineSnapshotToList())
9348 local_player->was_waiting = TRUE;
9353 local_player->was_waiting = FALSE;
9357 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9358 boolean button_released)
9360 if (button_released)
9362 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9363 CheckSaveEngineSnapshotToList();
9365 else if (element_clicked)
9367 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9368 CheckSaveEngineSnapshotToList();
9370 game.snapshot.changed_action = TRUE;
9374 boolean CheckSingleStepMode_EM(int frame,
9375 boolean any_player_moving,
9376 boolean any_player_snapping,
9377 boolean any_player_dropping)
9379 if (tape.single_step && tape.recording && !tape.pausing)
9380 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9381 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9383 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9384 any_player_snapping, any_player_dropping);
9386 return tape.pausing;
9389 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9390 boolean murphy_is_dropping)
9392 boolean murphy_starts_dropping = FALSE;
9395 for (i = 0; i < MAX_PLAYERS; i++)
9396 if (stored_player[i].force_dropping)
9397 murphy_starts_dropping = TRUE;
9399 if (tape.single_step && tape.recording && !tape.pausing)
9400 if (murphy_is_waiting && !murphy_starts_dropping)
9401 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9403 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9406 void CheckSingleStepMode_MM(boolean element_clicked,
9407 boolean button_released)
9409 if (tape.single_step && tape.recording && !tape.pausing)
9410 if (button_released)
9411 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9413 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9416 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9417 int graphic, int sync_frame)
9419 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9421 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9424 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9426 return (IS_NEXT_FRAME(sync_frame, graphic));
9429 int getGraphicInfo_Delay(int graphic)
9431 return graphic_info[graphic].anim_delay;
9434 void PlayMenuSoundExt(int sound)
9436 if (sound == SND_UNDEFINED)
9439 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9440 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9443 if (IS_LOOP_SOUND(sound))
9444 PlaySoundLoop(sound);
9449 void PlayMenuSound(void)
9451 PlayMenuSoundExt(menu.sound[game_status]);
9454 void PlayMenuSoundStereo(int sound, int stereo_position)
9456 if (sound == SND_UNDEFINED)
9459 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9460 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9463 if (IS_LOOP_SOUND(sound))
9464 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9466 PlaySoundStereo(sound, stereo_position);
9469 void PlayMenuSoundIfLoopExt(int sound)
9471 if (sound == SND_UNDEFINED)
9474 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9475 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9478 if (IS_LOOP_SOUND(sound))
9479 PlaySoundLoop(sound);
9482 void PlayMenuSoundIfLoop(void)
9484 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9487 void PlayMenuMusicExt(int music)
9489 if (music == MUS_UNDEFINED)
9492 if (!setup.sound_music)
9495 if (IS_LOOP_MUSIC(music))
9496 PlayMusicLoop(music);
9501 void PlayMenuMusic(void)
9503 char *curr_music = getCurrentlyPlayingMusicFilename();
9504 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9506 if (!strEqual(curr_music, next_music))
9507 PlayMenuMusicExt(menu.music[game_status]);
9510 void PlayMenuSoundsAndMusic(void)
9516 static void FadeMenuSounds(void)
9521 static void FadeMenuMusic(void)
9523 char *curr_music = getCurrentlyPlayingMusicFilename();
9524 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9526 if (!strEqual(curr_music, next_music))
9530 void FadeMenuSoundsAndMusic(void)
9536 void PlaySoundActivating(void)
9539 PlaySound(SND_MENU_ITEM_ACTIVATING);
9543 void PlaySoundSelecting(void)
9546 PlaySound(SND_MENU_ITEM_SELECTING);
9550 void ToggleFullscreenIfNeeded(void)
9552 // if setup and video fullscreen state are already matching, nothing do do
9553 if (setup.fullscreen == video.fullscreen_enabled ||
9554 !video.fullscreen_available)
9557 SDLSetWindowFullscreen(setup.fullscreen);
9559 // set setup value according to successfully changed fullscreen mode
9560 setup.fullscreen = video.fullscreen_enabled;
9563 void ChangeWindowScalingIfNeeded(void)
9565 // if setup and video window scaling are already matching, nothing do do
9566 if (setup.window_scaling_percent == video.window_scaling_percent ||
9567 video.fullscreen_enabled)
9570 SDLSetWindowScaling(setup.window_scaling_percent);
9572 // set setup value according to successfully changed window scaling
9573 setup.window_scaling_percent = video.window_scaling_percent;
9576 void ChangeVsyncModeIfNeeded(void)
9578 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9579 int video_vsync_mode = video.vsync_mode;
9581 // if setup and video vsync mode are already matching, nothing do do
9582 if (setup_vsync_mode == video_vsync_mode)
9585 // if renderer is using OpenGL, vsync mode can directly be changed
9586 SDLSetScreenVsyncMode(setup.vsync_mode);
9588 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9589 if (video.vsync_mode == video_vsync_mode)
9591 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9593 // save backbuffer content which gets lost when re-creating screen
9594 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9596 // force re-creating screen and renderer to set new vsync mode
9597 video.fullscreen_enabled = !setup.fullscreen;
9599 // when creating new renderer, destroy textures linked to old renderer
9600 FreeAllImageTextures(); // needs old renderer to free the textures
9602 // re-create screen and renderer (including change of vsync mode)
9603 ChangeVideoModeIfNeeded(setup.fullscreen);
9605 // set setup value according to successfully changed fullscreen mode
9606 setup.fullscreen = video.fullscreen_enabled;
9608 // restore backbuffer content from temporary backbuffer backup bitmap
9609 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9610 FreeBitmap(tmp_backbuffer);
9612 // update visible window/screen
9613 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9615 // when changing vsync mode, re-create textures for new renderer
9616 InitImageTextures();
9619 // set setup value according to successfully changed vsync mode
9620 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9623 static void JoinRectangles(int *x, int *y, int *width, int *height,
9624 int x2, int y2, int width2, int height2)
9626 // do not join with "off-screen" rectangle
9627 if (x2 == -1 || y2 == -1)
9632 *width = MAX(*width, width2);
9633 *height = MAX(*height, height2);
9636 void SetAnimStatus(int anim_status_new)
9638 if (anim_status_new == GAME_MODE_MAIN)
9639 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9640 else if (anim_status_new == GAME_MODE_NAMES)
9641 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9642 else if (anim_status_new == GAME_MODE_SCORES)
9643 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9645 global.anim_status_next = anim_status_new;
9647 // directly set screen modes that are entered without fading
9648 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9649 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9650 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9651 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9652 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9653 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9654 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9655 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9656 global.anim_status = global.anim_status_next;
9659 void SetGameStatus(int game_status_new)
9661 if (game_status_new != game_status)
9662 game_status_last_screen = game_status;
9664 game_status = game_status_new;
9666 SetAnimStatus(game_status_new);
9669 void SetFontStatus(int game_status_new)
9671 static int last_game_status = -1;
9673 if (game_status_new != -1)
9675 // set game status for font use after storing last game status
9676 last_game_status = game_status;
9677 game_status = game_status_new;
9681 // reset game status after font use from last stored game status
9682 game_status = last_game_status;
9686 void ResetFontStatus(void)
9691 void SetLevelSetInfo(char *identifier, int level_nr)
9693 setString(&levelset.identifier, identifier);
9695 levelset.level_nr = level_nr;
9698 boolean CheckIfAllViewportsHaveChanged(void)
9700 // if game status has not changed, viewports have not changed either
9701 if (game_status == game_status_last)
9704 // check if all viewports have changed with current game status
9706 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9707 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9708 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9709 int new_real_sx = vp_playfield->x;
9710 int new_real_sy = vp_playfield->y;
9711 int new_full_sxsize = vp_playfield->width;
9712 int new_full_sysize = vp_playfield->height;
9713 int new_dx = vp_door_1->x;
9714 int new_dy = vp_door_1->y;
9715 int new_dxsize = vp_door_1->width;
9716 int new_dysize = vp_door_1->height;
9717 int new_vx = vp_door_2->x;
9718 int new_vy = vp_door_2->y;
9719 int new_vxsize = vp_door_2->width;
9720 int new_vysize = vp_door_2->height;
9722 boolean playfield_viewport_has_changed =
9723 (new_real_sx != REAL_SX ||
9724 new_real_sy != REAL_SY ||
9725 new_full_sxsize != FULL_SXSIZE ||
9726 new_full_sysize != FULL_SYSIZE);
9728 boolean door_1_viewport_has_changed =
9731 new_dxsize != DXSIZE ||
9732 new_dysize != DYSIZE);
9734 boolean door_2_viewport_has_changed =
9737 new_vxsize != VXSIZE ||
9738 new_vysize != VYSIZE ||
9739 game_status_last == GAME_MODE_EDITOR);
9741 return (playfield_viewport_has_changed &&
9742 door_1_viewport_has_changed &&
9743 door_2_viewport_has_changed);
9746 boolean CheckFadeAll(void)
9748 return (CheckIfGlobalBorderHasChanged() ||
9749 CheckIfAllViewportsHaveChanged());
9752 void ChangeViewportPropertiesIfNeeded(void)
9754 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9755 FALSE : setup.small_game_graphics);
9756 int gfx_game_mode = getGlobalGameStatus(game_status);
9757 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9759 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9760 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9761 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9762 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9763 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9764 int new_win_xsize = vp_window->width;
9765 int new_win_ysize = vp_window->height;
9766 int border_left = vp_playfield->border_left;
9767 int border_right = vp_playfield->border_right;
9768 int border_top = vp_playfield->border_top;
9769 int border_bottom = vp_playfield->border_bottom;
9770 int new_sx = vp_playfield->x + border_left;
9771 int new_sy = vp_playfield->y + border_top;
9772 int new_sxsize = vp_playfield->width - border_left - border_right;
9773 int new_sysize = vp_playfield->height - border_top - border_bottom;
9774 int new_real_sx = vp_playfield->x;
9775 int new_real_sy = vp_playfield->y;
9776 int new_full_sxsize = vp_playfield->width;
9777 int new_full_sysize = vp_playfield->height;
9778 int new_dx = vp_door_1->x;
9779 int new_dy = vp_door_1->y;
9780 int new_dxsize = vp_door_1->width;
9781 int new_dysize = vp_door_1->height;
9782 int new_vx = vp_door_2->x;
9783 int new_vy = vp_door_2->y;
9784 int new_vxsize = vp_door_2->width;
9785 int new_vysize = vp_door_2->height;
9786 int new_ex = vp_door_3->x;
9787 int new_ey = vp_door_3->y;
9788 int new_exsize = vp_door_3->width;
9789 int new_eysize = vp_door_3->height;
9790 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9791 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9792 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9793 int new_scr_fieldx = new_sxsize / tilesize;
9794 int new_scr_fieldy = new_sysize / tilesize;
9795 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9796 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9797 boolean init_gfx_buffers = FALSE;
9798 boolean init_video_buffer = FALSE;
9799 boolean init_gadgets_and_anims = FALSE;
9800 boolean init_em_graphics = FALSE;
9802 if (new_win_xsize != WIN_XSIZE ||
9803 new_win_ysize != WIN_YSIZE)
9805 WIN_XSIZE = new_win_xsize;
9806 WIN_YSIZE = new_win_ysize;
9808 init_video_buffer = TRUE;
9809 init_gfx_buffers = TRUE;
9810 init_gadgets_and_anims = TRUE;
9812 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9815 if (new_scr_fieldx != SCR_FIELDX ||
9816 new_scr_fieldy != SCR_FIELDY)
9818 // this always toggles between MAIN and GAME when using small tile size
9820 SCR_FIELDX = new_scr_fieldx;
9821 SCR_FIELDY = new_scr_fieldy;
9823 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9834 new_sxsize != SXSIZE ||
9835 new_sysize != SYSIZE ||
9836 new_dxsize != DXSIZE ||
9837 new_dysize != DYSIZE ||
9838 new_vxsize != VXSIZE ||
9839 new_vysize != VYSIZE ||
9840 new_exsize != EXSIZE ||
9841 new_eysize != EYSIZE ||
9842 new_real_sx != REAL_SX ||
9843 new_real_sy != REAL_SY ||
9844 new_full_sxsize != FULL_SXSIZE ||
9845 new_full_sysize != FULL_SYSIZE ||
9846 new_tilesize_var != TILESIZE_VAR
9849 // ------------------------------------------------------------------------
9850 // determine next fading area for changed viewport definitions
9851 // ------------------------------------------------------------------------
9853 // start with current playfield area (default fading area)
9856 FADE_SXSIZE = FULL_SXSIZE;
9857 FADE_SYSIZE = FULL_SYSIZE;
9859 // add new playfield area if position or size has changed
9860 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9861 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9863 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9864 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9867 // add current and new door 1 area if position or size has changed
9868 if (new_dx != DX || new_dy != DY ||
9869 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9871 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9872 DX, DY, DXSIZE, DYSIZE);
9873 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9874 new_dx, new_dy, new_dxsize, new_dysize);
9877 // add current and new door 2 area if position or size has changed
9878 if (new_vx != VX || new_vy != VY ||
9879 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9881 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9882 VX, VY, VXSIZE, VYSIZE);
9883 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9884 new_vx, new_vy, new_vxsize, new_vysize);
9887 // ------------------------------------------------------------------------
9888 // handle changed tile size
9889 // ------------------------------------------------------------------------
9891 if (new_tilesize_var != TILESIZE_VAR)
9893 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9895 // changing tile size invalidates scroll values of engine snapshots
9896 FreeEngineSnapshotSingle();
9898 // changing tile size requires update of graphic mapping for EM engine
9899 init_em_graphics = TRUE;
9910 SXSIZE = new_sxsize;
9911 SYSIZE = new_sysize;
9912 DXSIZE = new_dxsize;
9913 DYSIZE = new_dysize;
9914 VXSIZE = new_vxsize;
9915 VYSIZE = new_vysize;
9916 EXSIZE = new_exsize;
9917 EYSIZE = new_eysize;
9918 REAL_SX = new_real_sx;
9919 REAL_SY = new_real_sy;
9920 FULL_SXSIZE = new_full_sxsize;
9921 FULL_SYSIZE = new_full_sysize;
9922 TILESIZE_VAR = new_tilesize_var;
9924 init_gfx_buffers = TRUE;
9925 init_gadgets_and_anims = TRUE;
9927 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9928 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9931 if (init_gfx_buffers)
9933 // Debug("tools:viewport", "init_gfx_buffers");
9935 SCR_FIELDX = new_scr_fieldx_buffers;
9936 SCR_FIELDY = new_scr_fieldy_buffers;
9940 SCR_FIELDX = new_scr_fieldx;
9941 SCR_FIELDY = new_scr_fieldy;
9943 SetDrawDeactivationMask(REDRAW_NONE);
9944 SetDrawBackgroundMask(REDRAW_FIELD);
9947 if (init_video_buffer)
9949 // Debug("tools:viewport", "init_video_buffer");
9951 FreeAllImageTextures(); // needs old renderer to free the textures
9953 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9954 InitImageTextures();
9957 if (init_gadgets_and_anims)
9959 // Debug("tools:viewport", "init_gadgets_and_anims");
9962 InitGlobalAnimations();
9965 if (init_em_graphics)
9967 InitGraphicInfo_EM();
9971 void OpenURL(char *url)
9973 #if SDL_VERSION_ATLEAST(2,0,14)
9976 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
9977 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
9978 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
9982 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
9984 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
9988 // ============================================================================
9990 // ============================================================================
9992 #if defined(PLATFORM_WINDOWS)
9993 /* FILETIME of Jan 1 1970 00:00:00. */
9994 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
9997 * timezone information is stored outside the kernel so tzp isn't used anymore.
9999 * Note: this function is not for Win32 high precision timing purpose. See
10002 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
10004 FILETIME file_time;
10005 SYSTEMTIME system_time;
10006 ULARGE_INTEGER ularge;
10008 GetSystemTime(&system_time);
10009 SystemTimeToFileTime(&system_time, &file_time);
10010 ularge.LowPart = file_time.dwLowDateTime;
10011 ularge.HighPart = file_time.dwHighDateTime;
10013 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10014 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10020 static char *test_init_uuid_random_function_simple(void)
10022 static char seed_text[100];
10023 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10025 sprintf(seed_text, "%d", seed);
10030 static char *test_init_uuid_random_function_better(void)
10032 static char seed_text[100];
10033 struct timeval current_time;
10035 gettimeofday(¤t_time, NULL);
10037 prng_seed_bytes(¤t_time, sizeof(current_time));
10039 sprintf(seed_text, "%ld.%ld",
10040 (long)current_time.tv_sec,
10041 (long)current_time.tv_usec);
10046 #if defined(PLATFORM_WINDOWS)
10047 static char *test_init_uuid_random_function_better_windows(void)
10049 static char seed_text[100];
10050 struct timeval current_time;
10052 gettimeofday_windows(¤t_time, NULL);
10054 prng_seed_bytes(¤t_time, sizeof(current_time));
10056 sprintf(seed_text, "%ld.%ld",
10057 (long)current_time.tv_sec,
10058 (long)current_time.tv_usec);
10064 static unsigned int test_uuid_random_function_simple(int max)
10066 return GetSimpleRandom(max);
10069 static unsigned int test_uuid_random_function_better(int max)
10071 return (max > 0 ? prng_get_uint() % max : 0);
10074 #if defined(PLATFORM_WINDOWS)
10075 #define NUM_UUID_TESTS 3
10077 #define NUM_UUID_TESTS 2
10080 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10082 struct hashtable *hash_seeds =
10083 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10084 struct hashtable *hash_uuids =
10085 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10086 static char message[100];
10089 char *random_name = (nr == 0 ? "simple" : "better");
10090 char *random_type = (always_seed ? "always" : "only once");
10091 char *(*init_random_function)(void) =
10093 test_init_uuid_random_function_simple :
10094 test_init_uuid_random_function_better);
10095 unsigned int (*random_function)(int) =
10097 test_uuid_random_function_simple :
10098 test_uuid_random_function_better);
10101 #if defined(PLATFORM_WINDOWS)
10104 random_name = "windows";
10105 init_random_function = test_init_uuid_random_function_better_windows;
10111 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10112 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10114 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10115 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10116 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10118 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10122 // always initialize random number generator at least once
10123 init_random_function();
10125 unsigned int time_start = SDL_GetTicks();
10127 for (i = 0; i < num_uuids; i++)
10131 char *seed = getStringCopy(init_random_function());
10133 hashtable_remove(hash_seeds, seed);
10134 hashtable_insert(hash_seeds, seed, "1");
10137 char *uuid = getStringCopy(getUUIDExt(random_function));
10139 hashtable_remove(hash_uuids, uuid);
10140 hashtable_insert(hash_uuids, uuid, "1");
10143 int num_unique_seeds = hashtable_count(hash_seeds);
10144 int num_unique_uuids = hashtable_count(hash_uuids);
10146 unsigned int time_needed = SDL_GetTicks() - time_start;
10148 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10150 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10153 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10155 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10156 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10158 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10160 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10162 Request(message, REQ_CONFIRM);
10164 hashtable_destroy(hash_seeds, 0);
10165 hashtable_destroy(hash_uuids, 0);
10168 void TestGeneratingUUIDs(void)
10170 int num_uuids = 1000000;
10173 for (i = 0; i < NUM_UUID_TESTS; i++)
10174 for (j = 0; j < 2; j++)
10175 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10177 CloseAllAndExit(0);