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;
1496 return getAnimationFrame(graphic_info[graphic].anim_frames,
1497 graphic_info[graphic].anim_delay,
1498 graphic_info[graphic].anim_mode,
1499 graphic_info[graphic].anim_start_frame,
1503 int getGraphicAnimationFrameXY(int graphic, int lx, int ly)
1505 if (graphic_info[graphic].anim_mode & ANIM_TILED)
1507 struct GraphicInfo *g = &graphic_info[graphic];
1508 int xsize = MAX(1, g->anim_frames_per_line);
1509 int ysize = MAX(1, g->anim_frames / xsize);
1510 int xoffset = g->anim_start_frame % xsize;
1511 int yoffset = g->anim_start_frame % ysize;
1512 // may be needed if screen field is significantly larger than playfield
1513 int x = (lx + xoffset + SCR_FIELDX * xsize) % xsize;
1514 int y = (ly + yoffset + SCR_FIELDY * ysize) % ysize;
1515 int sync_frame = y * xsize + x;
1517 return sync_frame % g->anim_frames;
1519 else if (graphic_info[graphic].anim_mode & ANIM_RANDOM_STATIC)
1521 struct GraphicInfo *g = &graphic_info[graphic];
1522 // may be needed if screen field is significantly larger than playfield
1523 int x = (lx + SCR_FIELDX * lev_fieldx) % lev_fieldx;
1524 int y = (ly + SCR_FIELDY * lev_fieldy) % lev_fieldy;
1525 int sync_frame = GfxRandomStatic[x][y];
1527 return sync_frame % g->anim_frames;
1531 int sync_frame = (IN_LEV_FIELD(lx, ly) ? GfxFrame[lx][ly] : -1);
1533 return getGraphicAnimationFrame(graphic, sync_frame);
1537 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1539 struct GraphicInfo *g = &graphic_info[graphic];
1540 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1542 if (tilesize == gfx.standard_tile_size)
1543 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1544 else if (tilesize == game.tile_size)
1545 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1547 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1550 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1551 boolean get_backside)
1553 struct GraphicInfo *g = &graphic_info[graphic];
1554 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1555 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1557 if (g->offset_y == 0) // frames are ordered horizontally
1559 int max_width = g->anim_frames_per_line * g->width;
1560 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1562 *x = pos % max_width;
1563 *y = src_y % g->height + pos / max_width * g->height;
1565 else if (g->offset_x == 0) // frames are ordered vertically
1567 int max_height = g->anim_frames_per_line * g->height;
1568 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1570 *x = src_x % g->width + pos / max_height * g->width;
1571 *y = pos % max_height;
1573 else // frames are ordered diagonally
1575 *x = src_x + frame * g->offset_x;
1576 *y = src_y + frame * g->offset_y;
1580 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1581 Bitmap **bitmap, int *x, int *y,
1582 boolean get_backside)
1584 struct GraphicInfo *g = &graphic_info[graphic];
1586 // if no graphics defined at all, use fallback graphics
1587 if (g->bitmaps == NULL)
1588 *g = graphic_info[IMG_CHAR_EXCLAM];
1590 // if no in-game graphics defined, always use standard graphic size
1591 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1592 tilesize = TILESIZE;
1594 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1595 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1597 *x = *x * tilesize / g->tile_size;
1598 *y = *y * tilesize / g->tile_size;
1601 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1602 Bitmap **bitmap, int *x, int *y)
1604 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1607 void getFixedGraphicSource(int graphic, int frame,
1608 Bitmap **bitmap, int *x, int *y)
1610 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1613 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1615 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1618 void getGlobalAnimGraphicSource(int graphic, int frame,
1619 Bitmap **bitmap, int *x, int *y)
1621 struct GraphicInfo *g = &graphic_info[graphic];
1623 // if no graphics defined at all, use fallback graphics
1624 if (g->bitmaps == NULL)
1625 *g = graphic_info[IMG_CHAR_EXCLAM];
1627 // use original size graphics, if existing, else use standard size graphics
1628 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1629 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1631 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1633 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1636 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1637 int *x, int *y, boolean get_backside)
1639 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1643 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1645 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1648 void DrawGraphic(int x, int y, int graphic, int frame)
1651 if (!IN_SCR_FIELD(x, y))
1653 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1654 Debug("draw:DrawGraphic", "This should never happen!");
1660 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1663 MarkTileDirty(x, y);
1666 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1669 if (!IN_SCR_FIELD(x, y))
1671 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1673 Debug("draw:DrawFixedGraphic", "This should never happen!");
1679 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1681 MarkTileDirty(x, y);
1684 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1690 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1692 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1695 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1701 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1702 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1705 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1708 if (!IN_SCR_FIELD(x, y))
1710 Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1712 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1718 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1721 MarkTileDirty(x, y);
1724 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1727 if (!IN_SCR_FIELD(x, y))
1729 Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1731 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1737 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1739 MarkTileDirty(x, y);
1742 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1748 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1750 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1754 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1755 int graphic, int frame)
1760 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1762 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1766 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1768 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1770 MarkTileDirty(x / tilesize, y / tilesize);
1773 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1776 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1777 graphic, frame, tilesize);
1778 MarkTileDirty(x / tilesize, y / tilesize);
1781 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1787 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1788 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1791 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1792 int frame, int tilesize)
1797 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1798 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1801 void DrawMiniGraphic(int x, int y, int graphic)
1803 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1804 MarkTileDirty(x / 2, y / 2);
1807 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1812 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1813 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1816 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1817 int graphic, int frame,
1818 int cut_mode, int mask_mode)
1823 int width = TILEX, height = TILEY;
1826 if (dx || dy) // shifted graphic
1828 if (x < BX1) // object enters playfield from the left
1835 else if (x > BX2) // object enters playfield from the right
1841 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1847 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1849 else if (dx) // general horizontal movement
1850 MarkTileDirty(x + SIGN(dx), y);
1852 if (y < BY1) // object enters playfield from the top
1854 if (cut_mode == CUT_BELOW) // object completely above top border
1862 else if (y > BY2) // object enters playfield from the bottom
1868 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1874 else if (dy > 0 && cut_mode == CUT_ABOVE)
1876 if (y == BY2) // object completely above bottom border
1882 MarkTileDirty(x, y + 1);
1883 } // object leaves playfield to the bottom
1884 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1886 else if (dy) // general vertical movement
1887 MarkTileDirty(x, y + SIGN(dy));
1891 if (!IN_SCR_FIELD(x, y))
1893 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1895 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1901 width = width * TILESIZE_VAR / TILESIZE;
1902 height = height * TILESIZE_VAR / TILESIZE;
1903 cx = cx * TILESIZE_VAR / TILESIZE;
1904 cy = cy * TILESIZE_VAR / TILESIZE;
1905 dx = dx * TILESIZE_VAR / TILESIZE;
1906 dy = dy * TILESIZE_VAR / TILESIZE;
1908 if (width > 0 && height > 0)
1910 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1915 dst_x = FX + x * TILEX_VAR + dx;
1916 dst_y = FY + y * TILEY_VAR + dy;
1918 if (mask_mode == USE_MASKING)
1919 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1922 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1925 MarkTileDirty(x, y);
1929 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1930 int graphic, int frame,
1931 int cut_mode, int mask_mode)
1936 int width = TILEX_VAR, height = TILEY_VAR;
1939 int x2 = x + SIGN(dx);
1940 int y2 = y + SIGN(dy);
1942 // movement with two-tile animations must be sync'ed with movement position,
1943 // not with current GfxFrame (which can be higher when using slow movement)
1944 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1945 int anim_frames = graphic_info[graphic].anim_frames;
1947 // (we also need anim_delay here for movement animations with less frames)
1948 int anim_delay = graphic_info[graphic].anim_delay;
1949 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1951 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1952 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1954 // re-calculate animation frame for two-tile movement animation
1955 frame = getGraphicAnimationFrame(graphic, sync_frame);
1957 // check if movement start graphic inside screen area and should be drawn
1958 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1960 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1962 dst_x = FX + x1 * TILEX_VAR;
1963 dst_y = FY + y1 * TILEY_VAR;
1965 if (mask_mode == USE_MASKING)
1966 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1969 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1972 MarkTileDirty(x1, y1);
1975 // check if movement end graphic inside screen area and should be drawn
1976 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1978 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1980 dst_x = FX + x2 * TILEX_VAR;
1981 dst_y = FY + y2 * TILEY_VAR;
1983 if (mask_mode == USE_MASKING)
1984 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1987 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1990 MarkTileDirty(x2, y2);
1994 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1995 int graphic, int frame,
1996 int cut_mode, int mask_mode)
2000 DrawGraphic(x, y, graphic, frame);
2005 if (graphic_info[graphic].double_movement) // EM style movement images
2006 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
2008 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
2011 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
2012 int graphic, int frame, int cut_mode)
2014 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
2017 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
2018 int cut_mode, int mask_mode)
2020 int lx = LEVELX(x), ly = LEVELY(y);
2024 if (IN_LEV_FIELD(lx, ly))
2026 if (element == EL_EMPTY)
2027 element = GfxElementEmpty[lx][ly];
2029 SetRandomAnimationValue(lx, ly);
2031 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2032 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2034 // do not use double (EM style) movement graphic when not moving
2035 if (graphic_info[graphic].double_movement && !dx && !dy)
2037 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2038 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2041 if (game.use_masked_elements && (dx || dy))
2042 mask_mode = USE_MASKING;
2044 else // border element
2046 graphic = el2img(element);
2047 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2050 if (element == EL_EXPANDABLE_WALL)
2052 boolean left_stopped = FALSE, right_stopped = FALSE;
2054 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2055 left_stopped = TRUE;
2056 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2057 right_stopped = TRUE;
2059 if (left_stopped && right_stopped)
2061 else if (left_stopped)
2063 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2064 frame = graphic_info[graphic].anim_frames - 1;
2066 else if (right_stopped)
2068 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2069 frame = graphic_info[graphic].anim_frames - 1;
2074 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2075 else if (mask_mode == USE_MASKING)
2076 DrawGraphicThruMask(x, y, graphic, frame);
2078 DrawGraphic(x, y, graphic, frame);
2081 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2082 int cut_mode, int mask_mode)
2084 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2085 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2086 cut_mode, mask_mode);
2089 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2092 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2095 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2098 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2101 void DrawLevelElementThruMask(int x, int y, int element)
2103 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2106 void DrawLevelFieldThruMask(int x, int y)
2108 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2111 // !!! implementation of quicksand is totally broken !!!
2112 #define IS_CRUMBLED_TILE(x, y, e) \
2113 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2114 !IS_MOVING(x, y) || \
2115 (e) == EL_QUICKSAND_EMPTYING || \
2116 (e) == EL_QUICKSAND_FAST_EMPTYING))
2118 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2123 int width, height, cx, cy;
2124 int sx = SCREENX(x), sy = SCREENY(y);
2125 int crumbled_border_size = graphic_info[graphic].border_size;
2126 int crumbled_tile_size = graphic_info[graphic].tile_size;
2127 int crumbled_border_size_var =
2128 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2131 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2133 for (i = 1; i < 4; i++)
2135 int dxx = (i & 1 ? dx : 0);
2136 int dyy = (i & 2 ? dy : 0);
2139 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2142 // check if neighbour field is of same crumble type
2143 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2144 graphic_info[graphic].class ==
2145 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2147 // return if check prevents inner corner
2148 if (same == (dxx == dx && dyy == dy))
2152 // if we reach this point, we have an inner corner
2154 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2156 width = crumbled_border_size_var;
2157 height = crumbled_border_size_var;
2158 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2159 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2161 if (game.use_masked_elements)
2163 int graphic0 = el2img(EL_EMPTY);
2164 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2165 Bitmap *src_bitmap0;
2168 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2170 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2172 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2174 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2176 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2179 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2181 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2184 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2189 int width, height, bx, by, cx, cy;
2190 int sx = SCREENX(x), sy = SCREENY(y);
2191 int crumbled_border_size = graphic_info[graphic].border_size;
2192 int crumbled_tile_size = graphic_info[graphic].tile_size;
2193 int crumbled_border_size_var =
2194 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2195 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2198 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2200 // only needed when using masked elements
2201 int graphic0 = el2img(EL_EMPTY);
2202 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2203 Bitmap *src_bitmap0;
2206 if (game.use_masked_elements)
2207 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2209 // draw simple, sloppy, non-corner-accurate crumbled border
2211 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2212 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2213 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2214 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2216 if (game.use_masked_elements)
2218 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2220 FX + sx * TILEX_VAR + cx,
2221 FY + sy * TILEY_VAR + cy);
2223 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2225 FX + sx * TILEX_VAR + cx,
2226 FY + sy * TILEY_VAR + cy);
2229 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2231 FX + sx * TILEX_VAR + cx,
2232 FY + sy * TILEY_VAR + cy);
2234 // (remaining middle border part must be at least as big as corner part)
2235 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2236 crumbled_border_size_var >= TILESIZE_VAR / 3)
2239 // correct corners of crumbled border, if needed
2241 for (i = -1; i <= 1; i += 2)
2243 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2244 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2245 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2248 // check if neighbour field is of same crumble type
2249 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2250 graphic_info[graphic].class ==
2251 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2253 // no crumbled corner, but continued crumbled border
2255 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2256 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2257 int b1 = (i == 1 ? crumbled_border_size_var :
2258 TILESIZE_VAR - 2 * crumbled_border_size_var);
2260 width = crumbled_border_size_var;
2261 height = crumbled_border_size_var;
2263 if (dir == 1 || dir == 2)
2278 if (game.use_masked_elements)
2280 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2282 FX + sx * TILEX_VAR + cx,
2283 FY + sy * TILEY_VAR + cy);
2285 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2287 FX + sx * TILEX_VAR + cx,
2288 FY + sy * TILEY_VAR + cy);
2291 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2293 FX + sx * TILEX_VAR + cx,
2294 FY + sy * TILEY_VAR + cy);
2299 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2301 int sx = SCREENX(x), sy = SCREENY(y);
2304 struct XY *xy = xy_topdown;
2306 if (!IN_LEV_FIELD(x, y))
2309 element = TILE_GFX_ELEMENT(x, y);
2311 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2313 if (!IN_SCR_FIELD(sx, sy))
2316 // crumble field borders towards direct neighbour fields
2317 for (i = 0; i < 4; i++)
2319 int xx = x + xy[i].x;
2320 int yy = y + xy[i].y;
2322 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2325 // check if neighbour field is of same crumble type
2326 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2327 graphic_info[graphic].class ==
2328 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2331 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2334 // crumble inner field corners towards corner neighbour fields
2335 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2336 graphic_info[graphic].anim_frames == 2)
2338 for (i = 0; i < 4; i++)
2340 int dx = (i & 1 ? +1 : -1);
2341 int dy = (i & 2 ? +1 : -1);
2343 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2347 MarkTileDirty(sx, sy);
2349 else // center field is not crumbled -- crumble neighbour fields
2351 // crumble field borders of direct neighbour fields
2352 for (i = 0; i < 4; i++)
2354 int xx = x + xy[i].x;
2355 int yy = y + xy[i].y;
2356 int sxx = sx + xy[i].x;
2357 int syy = sy + xy[i].y;
2359 if (!IN_LEV_FIELD(xx, yy) ||
2360 !IN_SCR_FIELD(sxx, syy))
2363 // do not crumble fields that are being digged or snapped
2364 if (Tile[xx][yy] == EL_EMPTY ||
2365 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2368 element = TILE_GFX_ELEMENT(xx, yy);
2370 if (!IS_CRUMBLED_TILE(xx, yy, element))
2373 graphic = el_act2crm(element, ACTION_DEFAULT);
2375 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2377 MarkTileDirty(sxx, syy);
2380 // crumble inner field corners of corner neighbour fields
2381 for (i = 0; i < 4; i++)
2383 int dx = (i & 1 ? +1 : -1);
2384 int dy = (i & 2 ? +1 : -1);
2390 if (!IN_LEV_FIELD(xx, yy) ||
2391 !IN_SCR_FIELD(sxx, syy))
2394 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2397 element = TILE_GFX_ELEMENT(xx, yy);
2399 if (!IS_CRUMBLED_TILE(xx, yy, element))
2402 graphic = el_act2crm(element, ACTION_DEFAULT);
2404 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2405 graphic_info[graphic].anim_frames == 2)
2406 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2408 MarkTileDirty(sxx, syy);
2413 void DrawLevelFieldCrumbled(int x, int y)
2417 if (!IN_LEV_FIELD(x, y))
2420 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2421 GfxElement[x][y] != EL_UNDEFINED &&
2422 GFX_CRUMBLED(GfxElement[x][y]))
2424 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2429 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2431 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2434 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2437 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2438 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2439 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2440 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2441 int sx = SCREENX(x), sy = SCREENY(y);
2443 DrawScreenGraphic(sx, sy, graphic1, frame1);
2444 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2447 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2449 int sx = SCREENX(x), sy = SCREENY(y);
2450 struct XY *xy = xy_topdown;
2453 // crumble direct neighbour fields (required for field borders)
2454 for (i = 0; i < 4; i++)
2456 int xx = x + xy[i].x;
2457 int yy = y + xy[i].y;
2458 int sxx = sx + xy[i].x;
2459 int syy = sy + xy[i].y;
2461 if (!IN_LEV_FIELD(xx, yy) ||
2462 !IN_SCR_FIELD(sxx, syy) ||
2463 !GFX_CRUMBLED(Tile[xx][yy]) ||
2467 DrawLevelField(xx, yy);
2470 // crumble corner neighbour fields (required for inner field corners)
2471 for (i = 0; i < 4; i++)
2473 int dx = (i & 1 ? +1 : -1);
2474 int dy = (i & 2 ? +1 : -1);
2480 if (!IN_LEV_FIELD(xx, yy) ||
2481 !IN_SCR_FIELD(sxx, syy) ||
2482 !GFX_CRUMBLED(Tile[xx][yy]) ||
2486 int element = TILE_GFX_ELEMENT(xx, yy);
2487 int graphic = el_act2crm(element, ACTION_DEFAULT);
2489 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2490 graphic_info[graphic].anim_frames == 2)
2491 DrawLevelField(xx, yy);
2495 static int getBorderElement(int x, int y)
2499 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2500 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2501 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2502 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2503 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2504 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2505 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2507 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2508 int steel_position = (x == -1 && y == -1 ? 0 :
2509 x == lev_fieldx && y == -1 ? 1 :
2510 x == -1 && y == lev_fieldy ? 2 :
2511 x == lev_fieldx && y == lev_fieldy ? 3 :
2512 x == -1 || x == lev_fieldx ? 4 :
2513 y == -1 || y == lev_fieldy ? 5 : 6);
2515 return border[steel_position][steel_type];
2518 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2520 if (game.use_masked_elements)
2522 if (graphic != el2img(EL_EMPTY))
2523 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2525 DrawGraphicThruMask(x, y, graphic, frame);
2529 DrawGraphic(x, y, graphic, frame);
2533 void DrawLevelGraphic(int x, int y, int graphic, int frame)
2535 DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2538 void DrawScreenElement(int x, int y, int element)
2540 int mask_mode = NO_MASKING;
2542 if (game.use_masked_elements)
2544 int lx = LEVELX(x), ly = LEVELY(y);
2546 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2548 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2550 mask_mode = USE_MASKING;
2554 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2555 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2558 void DrawLevelElement(int x, int y, int element)
2560 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2561 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2564 void DrawScreenField(int x, int y)
2566 int lx = LEVELX(x), ly = LEVELY(y);
2567 int element, content;
2569 if (!IN_LEV_FIELD(lx, ly))
2571 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2574 element = getBorderElement(lx, ly);
2576 DrawScreenElement(x, y, element);
2581 element = Tile[lx][ly];
2582 content = Store[lx][ly];
2584 if (IS_MOVING(lx, ly))
2586 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2587 boolean cut_mode = NO_CUTTING;
2589 if (element == EL_QUICKSAND_EMPTYING ||
2590 element == EL_QUICKSAND_FAST_EMPTYING ||
2591 element == EL_MAGIC_WALL_EMPTYING ||
2592 element == EL_BD_MAGIC_WALL_EMPTYING ||
2593 element == EL_DC_MAGIC_WALL_EMPTYING ||
2594 element == EL_AMOEBA_DROPPING)
2595 cut_mode = CUT_ABOVE;
2596 else if (element == EL_QUICKSAND_FILLING ||
2597 element == EL_QUICKSAND_FAST_FILLING ||
2598 element == EL_MAGIC_WALL_FILLING ||
2599 element == EL_BD_MAGIC_WALL_FILLING ||
2600 element == EL_DC_MAGIC_WALL_FILLING)
2601 cut_mode = CUT_BELOW;
2603 if (cut_mode == CUT_ABOVE)
2604 DrawScreenElement(x, y, element);
2606 DrawScreenElement(x, y, EL_EMPTY);
2608 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2610 int dir = MovDir[lx][ly];
2611 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2612 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2614 if (IN_SCR_FIELD(newx, newy))
2615 DrawScreenElement(newx, newy, EL_EMPTY);
2619 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2620 else if (cut_mode == NO_CUTTING)
2621 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2624 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2626 if (cut_mode == CUT_BELOW &&
2627 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2628 DrawLevelElement(lx, ly + 1, element);
2631 if (content == EL_ACID)
2633 int dir = MovDir[lx][ly];
2634 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2635 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2637 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2639 // prevent target field from being drawn again (but without masking)
2640 // (this would happen if target field is scanned after moving element)
2641 Stop[newlx][newly] = TRUE;
2644 else if (IS_BLOCKED(lx, ly))
2649 boolean cut_mode = NO_CUTTING;
2650 int element_old, content_old;
2652 Blocked2Moving(lx, ly, &oldx, &oldy);
2655 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2656 MovDir[oldx][oldy] == MV_RIGHT);
2658 element_old = Tile[oldx][oldy];
2659 content_old = Store[oldx][oldy];
2661 if (element_old == EL_QUICKSAND_EMPTYING ||
2662 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2663 element_old == EL_MAGIC_WALL_EMPTYING ||
2664 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2665 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2666 element_old == EL_AMOEBA_DROPPING)
2667 cut_mode = CUT_ABOVE;
2669 DrawScreenElement(x, y, EL_EMPTY);
2672 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2674 else if (cut_mode == NO_CUTTING)
2675 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2678 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2681 else if (IS_DRAWABLE(element))
2682 DrawScreenElement(x, y, element);
2684 DrawScreenElement(x, y, EL_EMPTY);
2687 void DrawLevelField(int x, int y)
2689 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2690 DrawScreenField(SCREENX(x), SCREENY(y));
2691 else if (IS_MOVING(x, y))
2695 Moving2Blocked(x, y, &newx, &newy);
2696 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2697 DrawScreenField(SCREENX(newx), SCREENY(newy));
2699 else if (IS_BLOCKED(x, y))
2703 Blocked2Moving(x, y, &oldx, &oldy);
2704 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2705 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2709 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2710 int (*el2img_function)(int), boolean masked,
2711 int element_bits_draw)
2713 int element_base = map_mm_wall_element(element);
2714 int element_bits = (IS_DF_WALL(element) ?
2715 element - EL_DF_WALL_START :
2716 IS_MM_WALL(element) ?
2717 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2718 int graphic = el2img_function(element_base);
2719 int tilesize_draw = tilesize / 2;
2724 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2726 for (i = 0; i < 4; i++)
2728 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2729 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2731 if (!(element_bits_draw & (1 << i)))
2734 if (element_bits & (1 << i))
2737 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2738 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2740 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2741 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2746 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2747 tilesize_draw, tilesize_draw);
2752 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2753 boolean masked, int element_bits_draw)
2755 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2756 element, tilesize, el2edimg, masked, element_bits_draw);
2759 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2760 int (*el2img_function)(int))
2762 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2766 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2769 if (IS_MM_WALL(element))
2771 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2772 element, tilesize, el2edimg, masked, 0x000f);
2776 int graphic = el2edimg(element);
2779 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2781 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2785 void DrawSizedElement(int x, int y, int element, int tilesize)
2787 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2790 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2792 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2795 void DrawMiniElement(int x, int y, int element)
2799 graphic = el2edimg(element);
2800 DrawMiniGraphic(x, y, graphic);
2803 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2806 int x = sx + scroll_x, y = sy + scroll_y;
2808 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2809 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2810 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2811 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2813 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2816 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2818 int x = sx + scroll_x, y = sy + scroll_y;
2820 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2821 DrawMiniElement(sx, sy, EL_EMPTY);
2822 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2823 DrawMiniElement(sx, sy, Tile[x][y]);
2825 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2828 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2829 int x, int y, int xsize, int ysize,
2830 int tile_width, int tile_height)
2834 int dst_x = startx + x * tile_width;
2835 int dst_y = starty + y * tile_height;
2836 int width = graphic_info[graphic].width;
2837 int height = graphic_info[graphic].height;
2838 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2839 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2840 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2841 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2842 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2843 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2844 boolean draw_masked = graphic_info[graphic].draw_masked;
2846 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2848 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2850 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2854 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2855 inner_sx + (x - 1) * tile_width % inner_width);
2856 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2857 inner_sy + (y - 1) * tile_height % inner_height);
2860 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2863 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2867 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2868 int x, int y, int xsize, int ysize,
2871 int font_width = getFontWidth(font_nr);
2872 int font_height = getFontHeight(font_nr);
2874 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2875 font_width, font_height);
2878 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2880 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2881 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2882 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2883 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2884 boolean no_delay = (tape.warp_forward);
2885 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2886 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2887 DelayCounter anim_delay = { anim_delay_value };
2888 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2889 int font_width = getFontWidth(font_nr);
2890 int font_height = getFontHeight(font_nr);
2891 int max_xsize = level.envelope[envelope_nr].xsize;
2892 int max_ysize = level.envelope[envelope_nr].ysize;
2893 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2894 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2895 int xend = max_xsize;
2896 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2897 int xstep = (xstart < xend ? 1 : 0);
2898 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2900 int end = MAX(xend - xstart, yend - ystart);
2903 for (i = start; i <= end; i++)
2905 int last_frame = end; // last frame of this "for" loop
2906 int x = xstart + i * xstep;
2907 int y = ystart + i * ystep;
2908 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2909 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2910 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2911 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2914 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2916 BlitScreenToBitmap(backbuffer);
2918 SetDrawtoField(DRAW_TO_BACKBUFFER);
2920 for (yy = 0; yy < ysize; yy++)
2921 for (xx = 0; xx < xsize; xx++)
2922 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2924 DrawTextArea(sx + font_width, sy + font_height,
2925 level.envelope[envelope_nr].text, font_nr, max_xsize,
2926 xsize - 2, ysize - 2, 0, mask_mode,
2927 level.envelope[envelope_nr].autowrap,
2928 level.envelope[envelope_nr].centered, FALSE);
2930 redraw_mask |= REDRAW_FIELD;
2933 SkipUntilDelayReached(&anim_delay, &i, last_frame);
2936 ClearAutoRepeatKeyEvents();
2939 void ShowEnvelope(int envelope_nr)
2941 int element = EL_ENVELOPE_1 + envelope_nr;
2942 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2943 int sound_opening = element_info[element].sound[ACTION_OPENING];
2944 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2945 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2946 boolean no_delay = (tape.warp_forward);
2947 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2948 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2949 int anim_mode = graphic_info[graphic].anim_mode;
2950 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2951 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2952 boolean overlay_enabled = GetOverlayEnabled();
2954 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2956 SetOverlayEnabled(FALSE);
2959 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2961 if (anim_mode == ANIM_DEFAULT)
2962 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2964 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2967 Delay_WithScreenUpdates(wait_delay_value);
2969 WaitForEventToContinue();
2972 SetOverlayEnabled(overlay_enabled);
2974 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2976 if (anim_mode != ANIM_NONE)
2977 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2979 if (anim_mode == ANIM_DEFAULT)
2980 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2982 game.envelope_active = FALSE;
2984 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2986 redraw_mask |= REDRAW_FIELD;
2990 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
2991 int xsize, int ysize)
2993 if (!global.use_envelope_request ||
2994 request.sort_priority <= 0)
2997 if (request.bitmap == NULL ||
2998 xsize > request.xsize ||
2999 ysize > request.ysize)
3001 if (request.bitmap != NULL)
3002 FreeBitmap(request.bitmap);
3004 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
3006 SDL_Surface *surface = request.bitmap->surface;
3008 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
3009 Fail("SDLGetNativeSurface() failed");
3012 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3014 SDLFreeBitmapTextures(request.bitmap);
3015 SDLCreateBitmapTextures(request.bitmap);
3017 // set envelope request run-time values
3020 request.xsize = xsize;
3021 request.ysize = ysize;
3024 void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage)
3026 if (global.use_envelope_request &&
3027 game.request_active_or_moving &&
3028 request.sort_priority > 0 &&
3029 drawing_target == DRAW_TO_SCREEN &&
3030 drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
3032 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3033 request.sx, request.sy);
3037 static void setRequestBasePosition(int *x, int *y)
3039 int sx_base, sy_base;
3041 if (request.x != -1)
3042 sx_base = request.x;
3043 else if (request.align == ALIGN_LEFT)
3045 else if (request.align == ALIGN_RIGHT)
3046 sx_base = SX + SXSIZE;
3048 sx_base = SX + SXSIZE / 2;
3050 if (request.y != -1)
3051 sy_base = request.y;
3052 else if (request.valign == VALIGN_TOP)
3054 else if (request.valign == VALIGN_BOTTOM)
3055 sy_base = SY + SYSIZE;
3057 sy_base = SY + SYSIZE / 2;
3063 static void setRequestPositionExt(int *x, int *y, int width, int height,
3064 boolean add_border_size)
3066 int border_size = request.border_size;
3067 int sx_base, sy_base;
3070 setRequestBasePosition(&sx_base, &sy_base);
3072 if (request.align == ALIGN_LEFT)
3074 else if (request.align == ALIGN_RIGHT)
3075 sx = sx_base - width;
3077 sx = sx_base - width / 2;
3079 if (request.valign == VALIGN_TOP)
3081 else if (request.valign == VALIGN_BOTTOM)
3082 sy = sy_base - height;
3084 sy = sy_base - height / 2;
3086 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3087 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3089 if (add_border_size)
3099 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3101 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3104 static void DrawEnvelopeRequest(char *text)
3106 char *text_final = text;
3107 char *text_door_style = NULL;
3108 int graphic = IMG_BACKGROUND_REQUEST;
3109 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3110 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3111 int font_nr = FONT_REQUEST;
3112 int font_width = getFontWidth(font_nr);
3113 int font_height = getFontHeight(font_nr);
3114 int border_size = request.border_size;
3115 int line_spacing = request.line_spacing;
3116 int line_height = font_height + line_spacing;
3117 int max_text_width = request.width - 2 * border_size;
3118 int max_text_height = request.height - 2 * border_size;
3119 int line_length = max_text_width / font_width;
3120 int max_lines = max_text_height / line_height;
3121 int text_width = line_length * font_width;
3122 int width = request.width;
3123 int height = request.height;
3124 int tile_size = MAX(request.step_offset, 1);
3125 int x_steps = width / tile_size;
3126 int y_steps = height / tile_size;
3127 int sx_offset = border_size;
3128 int sy_offset = border_size;
3132 if (request.centered)
3133 sx_offset = (request.width - text_width) / 2;
3135 if (request.wrap_single_words && !request.autowrap)
3137 char *src_text_ptr, *dst_text_ptr;
3139 text_door_style = checked_malloc(2 * strlen(text) + 1);
3141 src_text_ptr = text;
3142 dst_text_ptr = text_door_style;
3144 while (*src_text_ptr)
3146 if (*src_text_ptr == ' ' ||
3147 *src_text_ptr == '?' ||
3148 *src_text_ptr == '!')
3149 *dst_text_ptr++ = '\n';
3151 if (*src_text_ptr != ' ')
3152 *dst_text_ptr++ = *src_text_ptr;
3157 *dst_text_ptr = '\0';
3159 text_final = text_door_style;
3162 setRequestPosition(&sx, &sy, FALSE);
3164 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3166 for (y = 0; y < y_steps; y++)
3167 for (x = 0; x < x_steps; x++)
3168 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3169 x, y, x_steps, y_steps,
3170 tile_size, tile_size);
3172 // force DOOR font inside door area
3173 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3175 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3176 line_length, -1, max_lines, line_spacing, mask_mode,
3177 request.autowrap, request.centered, FALSE);
3181 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3182 RedrawGadget(tool_gadget[i]);
3184 // store readily prepared envelope request for later use when animating
3185 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3187 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3189 if (text_door_style)
3190 free(text_door_style);
3193 static void AnimateEnvelopeRequest(int anim_mode, int action)
3195 int graphic = IMG_BACKGROUND_REQUEST;
3196 boolean draw_masked = graphic_info[graphic].draw_masked;
3197 int delay_value_normal = request.step_delay;
3198 int delay_value_fast = delay_value_normal / 2;
3199 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3200 boolean no_delay = (tape.warp_forward);
3201 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3202 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3203 DelayCounter anim_delay = { anim_delay_value };
3205 int tile_size = MAX(request.step_offset, 1);
3206 int max_xsize = request.width / tile_size;
3207 int max_ysize = request.height / tile_size;
3208 int max_xsize_inner = max_xsize - 2;
3209 int max_ysize_inner = max_ysize - 2;
3211 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3212 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3213 int xend = max_xsize_inner;
3214 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3215 int xstep = (xstart < xend ? 1 : 0);
3216 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3218 int end = MAX(xend - xstart, yend - ystart);
3221 if (setup.quick_doors)
3228 for (i = start; i <= end; i++)
3230 int last_frame = end; // last frame of this "for" loop
3231 int x = xstart + i * xstep;
3232 int y = ystart + i * ystep;
3233 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3234 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3235 int xsize_size_left = (xsize - 1) * tile_size;
3236 int ysize_size_top = (ysize - 1) * tile_size;
3237 int max_xsize_pos = (max_xsize - 1) * tile_size;
3238 int max_ysize_pos = (max_ysize - 1) * tile_size;
3239 int width = xsize * tile_size;
3240 int height = ysize * tile_size;
3245 setRequestPosition(&src_x, &src_y, FALSE);
3246 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3248 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3250 for (yy = 0; yy < 2; yy++)
3252 for (xx = 0; xx < 2; xx++)
3254 int src_xx = src_x + xx * max_xsize_pos;
3255 int src_yy = src_y + yy * max_ysize_pos;
3256 int dst_xx = dst_x + xx * xsize_size_left;
3257 int dst_yy = dst_y + yy * ysize_size_top;
3258 int xx_size = (xx ? tile_size : xsize_size_left);
3259 int yy_size = (yy ? tile_size : ysize_size_top);
3262 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3263 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3265 BlitBitmap(bitmap_db_store_2, backbuffer,
3266 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3270 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3272 redraw_mask |= REDRAW_FIELD;
3276 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3279 ClearAutoRepeatKeyEvents();
3282 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3284 int graphic = IMG_BACKGROUND_REQUEST;
3285 int sound_opening = SND_REQUEST_OPENING;
3286 int sound_closing = SND_REQUEST_CLOSING;
3287 int anim_mode_1 = request.anim_mode; // (higher priority)
3288 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3289 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3290 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3291 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3293 if (game_status == GAME_MODE_PLAYING)
3294 BlitScreenToBitmap(backbuffer);
3296 SetDrawtoField(DRAW_TO_BACKBUFFER);
3298 // SetDrawBackgroundMask(REDRAW_NONE);
3300 if (action == ACTION_OPENING)
3302 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3304 if (req_state & REQ_ASK)
3306 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3307 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3308 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3309 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3311 else if (req_state & REQ_CONFIRM)
3313 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3314 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3316 else if (req_state & REQ_PLAYER)
3318 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3319 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3320 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3321 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3324 DrawEnvelopeRequest(text);
3327 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3329 if (action == ACTION_OPENING)
3331 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3333 if (anim_mode == ANIM_DEFAULT)
3334 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3336 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3340 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3342 if (anim_mode != ANIM_NONE)
3343 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3345 if (anim_mode == ANIM_DEFAULT)
3346 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3349 game.envelope_active = FALSE;
3351 if (action == ACTION_CLOSING)
3352 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3354 // SetDrawBackgroundMask(last_draw_background_mask);
3356 redraw_mask |= REDRAW_FIELD;
3360 if (action == ACTION_CLOSING &&
3361 game_status == GAME_MODE_PLAYING &&
3362 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3363 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3366 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3368 if (IS_MM_WALL(element))
3370 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3376 int graphic = el2preimg(element);
3378 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3379 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3384 void DrawLevel(int draw_background_mask)
3388 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3389 SetDrawBackgroundMask(draw_background_mask);
3393 for (x = BX1; x <= BX2; x++)
3394 for (y = BY1; y <= BY2; y++)
3395 DrawScreenField(x, y);
3397 redraw_mask |= REDRAW_FIELD;
3400 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3405 for (x = 0; x < size_x; x++)
3406 for (y = 0; y < size_y; y++)
3407 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3409 redraw_mask |= REDRAW_FIELD;
3412 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3416 for (x = 0; x < size_x; x++)
3417 for (y = 0; y < size_y; y++)
3418 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3420 redraw_mask |= REDRAW_FIELD;
3423 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3425 boolean show_level_border = (BorderElement != EL_EMPTY);
3426 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3427 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3428 int tile_size = preview.tile_size;
3429 int preview_width = preview.xsize * tile_size;
3430 int preview_height = preview.ysize * tile_size;
3431 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3432 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3433 int real_preview_width = real_preview_xsize * tile_size;
3434 int real_preview_height = real_preview_ysize * tile_size;
3435 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3436 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3439 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3442 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3444 dst_x += (preview_width - real_preview_width) / 2;
3445 dst_y += (preview_height - real_preview_height) / 2;
3447 for (x = 0; x < real_preview_xsize; x++)
3449 for (y = 0; y < real_preview_ysize; y++)
3451 int lx = from_x + x + (show_level_border ? -1 : 0);
3452 int ly = from_y + y + (show_level_border ? -1 : 0);
3453 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3454 getBorderElement(lx, ly));
3456 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3457 element, tile_size);
3461 redraw_mask |= REDRAW_FIELD;
3464 #define MICROLABEL_EMPTY 0
3465 #define MICROLABEL_LEVEL_NAME 1
3466 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3467 #define MICROLABEL_LEVEL_AUTHOR 3
3468 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3469 #define MICROLABEL_IMPORTED_FROM 5
3470 #define MICROLABEL_IMPORTED_BY_HEAD 6
3471 #define MICROLABEL_IMPORTED_BY 7
3473 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3475 int max_text_width = SXSIZE;
3476 int font_width = getFontWidth(font_nr);
3478 if (pos->align == ALIGN_CENTER)
3479 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3480 else if (pos->align == ALIGN_RIGHT)
3481 max_text_width = pos->x;
3483 max_text_width = SXSIZE - pos->x;
3485 return max_text_width / font_width;
3488 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3490 char label_text[MAX_OUTPUT_LINESIZE + 1];
3491 int max_len_label_text;
3492 int font_nr = pos->font;
3495 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3498 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3499 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3500 mode == MICROLABEL_IMPORTED_BY_HEAD)
3501 font_nr = pos->font_alt;
3503 max_len_label_text = getMaxTextLength(pos, font_nr);
3505 if (pos->size != -1)
3506 max_len_label_text = pos->size;
3508 for (i = 0; i < max_len_label_text; i++)
3509 label_text[i] = ' ';
3510 label_text[max_len_label_text] = '\0';
3512 if (strlen(label_text) > 0)
3513 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3516 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3517 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3518 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3519 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3520 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3521 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3522 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3523 max_len_label_text);
3524 label_text[max_len_label_text] = '\0';
3526 if (strlen(label_text) > 0)
3527 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3529 redraw_mask |= REDRAW_FIELD;
3532 static void DrawPreviewLevelLabel(int mode)
3534 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3537 static void DrawPreviewLevelInfo(int mode)
3539 if (mode == MICROLABEL_LEVEL_NAME)
3540 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3541 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3542 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3545 static void DrawPreviewLevelExt(boolean restart)
3547 static DelayCounter scroll_delay = { 0 };
3548 static DelayCounter label_delay = { 0 };
3549 static int from_x, from_y, scroll_direction;
3550 static int label_state, label_counter;
3551 boolean show_level_border = (BorderElement != EL_EMPTY);
3552 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3553 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3555 scroll_delay.value = preview.step_delay;
3556 label_delay.value = MICROLEVEL_LABEL_DELAY;
3563 if (preview.anim_mode == ANIM_CENTERED)
3565 if (level_xsize > preview.xsize)
3566 from_x = (level_xsize - preview.xsize) / 2;
3567 if (level_ysize > preview.ysize)
3568 from_y = (level_ysize - preview.ysize) / 2;
3571 from_x += preview.xoffset;
3572 from_y += preview.yoffset;
3574 scroll_direction = MV_RIGHT;
3578 DrawPreviewLevelPlayfield(from_x, from_y);
3579 DrawPreviewLevelLabel(label_state);
3581 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3582 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3584 // initialize delay counters
3585 ResetDelayCounter(&scroll_delay);
3586 ResetDelayCounter(&label_delay);
3588 if (leveldir_current->name)
3590 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3591 char label_text[MAX_OUTPUT_LINESIZE + 1];
3592 int font_nr = pos->font;
3593 int max_len_label_text = getMaxTextLength(pos, font_nr);
3595 if (pos->size != -1)
3596 max_len_label_text = pos->size;
3598 strncpy(label_text, leveldir_current->name, max_len_label_text);
3599 label_text[max_len_label_text] = '\0';
3601 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3602 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3608 // scroll preview level, if needed
3609 if (preview.anim_mode != ANIM_NONE &&
3610 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3611 DelayReached(&scroll_delay))
3613 switch (scroll_direction)
3618 from_x -= preview.step_offset;
3619 from_x = (from_x < 0 ? 0 : from_x);
3622 scroll_direction = MV_UP;
3626 if (from_x < level_xsize - preview.xsize)
3628 from_x += preview.step_offset;
3629 from_x = (from_x > level_xsize - preview.xsize ?
3630 level_xsize - preview.xsize : from_x);
3633 scroll_direction = MV_DOWN;
3639 from_y -= preview.step_offset;
3640 from_y = (from_y < 0 ? 0 : from_y);
3643 scroll_direction = MV_RIGHT;
3647 if (from_y < level_ysize - preview.ysize)
3649 from_y += preview.step_offset;
3650 from_y = (from_y > level_ysize - preview.ysize ?
3651 level_ysize - preview.ysize : from_y);
3654 scroll_direction = MV_LEFT;
3661 DrawPreviewLevelPlayfield(from_x, from_y);
3664 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3665 // redraw micro level label, if needed
3666 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3667 !strEqual(level.author, ANONYMOUS_NAME) &&
3668 !strEqual(level.author, leveldir_current->name) &&
3669 DelayReached(&label_delay))
3671 int max_label_counter = 23;
3673 if (leveldir_current->imported_from != NULL &&
3674 strlen(leveldir_current->imported_from) > 0)
3675 max_label_counter += 14;
3676 if (leveldir_current->imported_by != NULL &&
3677 strlen(leveldir_current->imported_by) > 0)
3678 max_label_counter += 14;
3680 label_counter = (label_counter + 1) % max_label_counter;
3681 label_state = (label_counter >= 0 && label_counter <= 7 ?
3682 MICROLABEL_LEVEL_NAME :
3683 label_counter >= 9 && label_counter <= 12 ?
3684 MICROLABEL_LEVEL_AUTHOR_HEAD :
3685 label_counter >= 14 && label_counter <= 21 ?
3686 MICROLABEL_LEVEL_AUTHOR :
3687 label_counter >= 23 && label_counter <= 26 ?
3688 MICROLABEL_IMPORTED_FROM_HEAD :
3689 label_counter >= 28 && label_counter <= 35 ?
3690 MICROLABEL_IMPORTED_FROM :
3691 label_counter >= 37 && label_counter <= 40 ?
3692 MICROLABEL_IMPORTED_BY_HEAD :
3693 label_counter >= 42 && label_counter <= 49 ?
3694 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3696 if (leveldir_current->imported_from == NULL &&
3697 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3698 label_state == MICROLABEL_IMPORTED_FROM))
3699 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3700 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3702 DrawPreviewLevelLabel(label_state);
3706 void DrawPreviewPlayers(void)
3708 if (game_status != GAME_MODE_MAIN)
3711 // do not draw preview players if level preview redefined, but players aren't
3712 if (preview.redefined && !menu.main.preview_players.redefined)
3715 boolean player_found[MAX_PLAYERS];
3716 int num_players = 0;
3719 for (i = 0; i < MAX_PLAYERS; i++)
3720 player_found[i] = FALSE;
3722 // check which players can be found in the level (simple approach)
3723 for (x = 0; x < lev_fieldx; x++)
3725 for (y = 0; y < lev_fieldy; y++)
3727 int element = level.field[x][y];
3729 if (IS_PLAYER_ELEMENT(element))
3731 int player_nr = GET_PLAYER_NR(element);
3733 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3735 if (!player_found[player_nr])
3738 player_found[player_nr] = TRUE;
3743 struct TextPosInfo *pos = &menu.main.preview_players;
3744 int tile_size = pos->tile_size;
3745 int border_size = pos->border_size;
3746 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3747 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3748 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3749 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3750 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3751 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3752 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3753 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3754 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3755 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3756 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3757 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3759 // clear area in which the players will be drawn
3760 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3761 max_players_width, max_players_height);
3763 if (!network.enabled && !setup.team_mode)
3766 // only draw players if level is suited for team mode
3767 if (num_players < 2)
3770 // draw all players that were found in the level
3771 for (i = 0; i < MAX_PLAYERS; i++)
3773 if (player_found[i])
3775 int graphic = el2img(EL_PLAYER_1 + i);
3777 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3779 xpos += player_xoffset;
3780 ypos += player_yoffset;
3785 void DrawPreviewLevelInitial(void)
3787 DrawPreviewLevelExt(TRUE);
3788 DrawPreviewPlayers();
3791 void DrawPreviewLevelAnimation(void)
3793 DrawPreviewLevelExt(FALSE);
3796 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3797 int border_size, int font_nr)
3799 int graphic = el2img(EL_PLAYER_1 + player_nr);
3800 int font_height = getFontHeight(font_nr);
3801 int player_height = MAX(tile_size, font_height);
3802 int xoffset_text = tile_size + border_size;
3803 int yoffset_text = (player_height - font_height) / 2;
3804 int yoffset_graphic = (player_height - tile_size) / 2;
3805 char *player_name = getNetworkPlayerName(player_nr + 1);
3807 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3809 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3812 static void DrawNetworkPlayersExt(boolean force)
3814 if (game_status != GAME_MODE_MAIN)
3817 if (!network.connected && !force)
3820 // do not draw network players if level preview redefined, but players aren't
3821 if (preview.redefined && !menu.main.network_players.redefined)
3824 int num_players = 0;
3827 for (i = 0; i < MAX_PLAYERS; i++)
3828 if (stored_player[i].connected_network)
3831 struct TextPosInfo *pos = &menu.main.network_players;
3832 int tile_size = pos->tile_size;
3833 int border_size = pos->border_size;
3834 int xoffset_text = tile_size + border_size;
3835 int font_nr = pos->font;
3836 int font_width = getFontWidth(font_nr);
3837 int font_height = getFontHeight(font_nr);
3838 int player_height = MAX(tile_size, font_height);
3839 int player_yoffset = player_height + border_size;
3840 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3841 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3842 int all_players_height = num_players * player_yoffset - border_size;
3843 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3844 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3845 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3847 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3848 max_players_width, max_players_height);
3850 // first draw local network player ...
3851 for (i = 0; i < MAX_PLAYERS; i++)
3853 if (stored_player[i].connected_network &&
3854 stored_player[i].connected_locally)
3856 char *player_name = getNetworkPlayerName(i + 1);
3857 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3858 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3860 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3862 ypos += player_yoffset;
3866 // ... then draw all other network players
3867 for (i = 0; i < MAX_PLAYERS; i++)
3869 if (stored_player[i].connected_network &&
3870 !stored_player[i].connected_locally)
3872 char *player_name = getNetworkPlayerName(i + 1);
3873 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3874 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3876 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3878 ypos += player_yoffset;
3883 void DrawNetworkPlayers(void)
3885 DrawNetworkPlayersExt(FALSE);
3888 void ClearNetworkPlayers(void)
3890 DrawNetworkPlayersExt(TRUE);
3893 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3894 int graphic, int lx, int ly,
3897 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3899 if (mask_mode == USE_MASKING)
3900 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3902 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3905 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3906 int graphic, int sync_frame, int mask_mode)
3908 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3910 if (mask_mode == USE_MASKING)
3911 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3913 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3916 static void DrawGraphicAnimation(int x, int y, int graphic)
3918 int lx = LEVELX(x), ly = LEVELY(y);
3919 int mask_mode = NO_MASKING;
3921 if (!IN_SCR_FIELD(x, y))
3924 if (game.use_masked_elements)
3926 if (Tile[lx][ly] != EL_EMPTY)
3928 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3930 mask_mode = USE_MASKING;
3934 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3935 graphic, lx, ly, mask_mode);
3937 MarkTileDirty(x, y);
3940 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3942 int lx = LEVELX(x), ly = LEVELY(y);
3943 int mask_mode = NO_MASKING;
3945 if (!IN_SCR_FIELD(x, y))
3948 if (game.use_masked_elements)
3950 if (Tile[lx][ly] != EL_EMPTY)
3952 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3954 mask_mode = USE_MASKING;
3958 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3959 graphic, lx, ly, mask_mode);
3961 MarkTileDirty(x, y);
3964 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3966 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3969 void DrawLevelElementAnimation(int x, int y, int element)
3971 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3973 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3976 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3978 int sx = SCREENX(x), sy = SCREENY(y);
3980 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3983 if (Tile[x][y] == EL_EMPTY)
3984 graphic = el2img(GfxElementEmpty[x][y]);
3986 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3989 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
3992 DrawGraphicAnimation(sx, sy, graphic);
3995 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3996 DrawLevelFieldCrumbled(x, y);
3998 if (GFX_CRUMBLED(Tile[x][y]))
3999 DrawLevelFieldCrumbled(x, y);
4003 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4005 int sx = SCREENX(x), sy = SCREENY(y);
4008 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4011 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4013 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4016 DrawGraphicAnimation(sx, sy, graphic);
4018 if (GFX_CRUMBLED(element))
4019 DrawLevelFieldCrumbled(x, y);
4022 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4024 if (player->use_murphy)
4026 // this works only because currently only one player can be "murphy" ...
4027 static int last_horizontal_dir = MV_LEFT;
4028 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4030 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4031 last_horizontal_dir = move_dir;
4033 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4035 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4037 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4043 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
4046 static boolean equalGraphics(int graphic1, int graphic2)
4048 struct GraphicInfo *g1 = &graphic_info[graphic1];
4049 struct GraphicInfo *g2 = &graphic_info[graphic2];
4051 return (g1->bitmap == g2->bitmap &&
4052 g1->src_x == g2->src_x &&
4053 g1->src_y == g2->src_y &&
4054 g1->anim_frames == g2->anim_frames &&
4055 g1->anim_delay == g2->anim_delay &&
4056 g1->anim_mode == g2->anim_mode);
4059 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4063 DRAW_PLAYER_STAGE_INIT = 0,
4064 DRAW_PLAYER_STAGE_LAST_FIELD,
4065 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4066 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4067 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4068 DRAW_PLAYER_STAGE_PLAYER,
4070 DRAW_PLAYER_STAGE_PLAYER,
4071 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4073 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4074 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4076 NUM_DRAW_PLAYER_STAGES
4079 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4081 static int static_last_player_graphic[MAX_PLAYERS];
4082 static int static_last_player_frame[MAX_PLAYERS];
4083 static boolean static_player_is_opaque[MAX_PLAYERS];
4084 static boolean draw_player[MAX_PLAYERS];
4085 int pnr = player->index_nr;
4087 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4089 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4090 static_last_player_frame[pnr] = player->Frame;
4091 static_player_is_opaque[pnr] = FALSE;
4093 draw_player[pnr] = TRUE;
4096 if (!draw_player[pnr])
4100 if (!IN_LEV_FIELD(player->jx, player->jy))
4102 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4103 Debug("draw:DrawPlayerExt", "This should never happen!");
4105 draw_player[pnr] = FALSE;
4111 int last_player_graphic = static_last_player_graphic[pnr];
4112 int last_player_frame = static_last_player_frame[pnr];
4113 boolean player_is_opaque = static_player_is_opaque[pnr];
4115 int jx = player->jx;
4116 int jy = player->jy;
4117 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4118 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4119 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4120 int last_jx = (player->is_moving ? jx - dx : jx);
4121 int last_jy = (player->is_moving ? jy - dy : jy);
4122 int next_jx = jx + dx;
4123 int next_jy = jy + dy;
4124 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4125 int sx = SCREENX(jx);
4126 int sy = SCREENY(jy);
4127 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4128 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4129 int element = Tile[jx][jy];
4130 int last_element = Tile[last_jx][last_jy];
4131 int action = (player->is_pushing ? ACTION_PUSHING :
4132 player->is_digging ? ACTION_DIGGING :
4133 player->is_collecting ? ACTION_COLLECTING :
4134 player->is_moving ? ACTION_MOVING :
4135 player->is_snapping ? ACTION_SNAPPING :
4136 player->is_dropping ? ACTION_DROPPING :
4137 player->is_waiting ? player->action_waiting :
4140 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4142 // ------------------------------------------------------------------------
4143 // initialize drawing the player
4144 // ------------------------------------------------------------------------
4146 draw_player[pnr] = FALSE;
4148 // GfxElement[][] is set to the element the player is digging or collecting;
4149 // remove also for off-screen player if the player is not moving anymore
4150 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4151 GfxElement[jx][jy] = EL_UNDEFINED;
4153 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4156 if (element == EL_EXPLOSION)
4159 InitPlayerGfxAnimation(player, action, move_dir);
4161 draw_player[pnr] = TRUE;
4163 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4165 // ------------------------------------------------------------------------
4166 // draw things in the field the player is leaving, if needed
4167 // ------------------------------------------------------------------------
4169 if (!IN_SCR_FIELD(sx, sy))
4170 draw_player[pnr] = FALSE;
4172 if (!player->is_moving)
4175 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4177 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4179 if (last_element == EL_DYNAMITE_ACTIVE ||
4180 last_element == EL_EM_DYNAMITE_ACTIVE ||
4181 last_element == EL_SP_DISK_RED_ACTIVE)
4182 DrawDynamite(last_jx, last_jy);
4184 DrawLevelFieldThruMask(last_jx, last_jy);
4186 else if (last_element == EL_DYNAMITE_ACTIVE ||
4187 last_element == EL_EM_DYNAMITE_ACTIVE ||
4188 last_element == EL_SP_DISK_RED_ACTIVE)
4189 DrawDynamite(last_jx, last_jy);
4191 DrawLevelField(last_jx, last_jy);
4193 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
4194 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4196 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4198 // ------------------------------------------------------------------------
4199 // draw things behind the player, if needed
4200 // ------------------------------------------------------------------------
4204 DrawLevelElement(jx, jy, Back[jx][jy]);
4209 if (IS_ACTIVE_BOMB(element))
4211 DrawLevelElement(jx, jy, EL_EMPTY);
4216 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4218 int old_element = GfxElement[jx][jy];
4219 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4220 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4222 if (GFX_CRUMBLED(old_element))
4223 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4225 DrawScreenGraphic(sx, sy, old_graphic, frame);
4227 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4228 static_player_is_opaque[pnr] = TRUE;
4232 GfxElement[jx][jy] = EL_UNDEFINED;
4234 // make sure that pushed elements are drawn with correct frame rate
4235 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4237 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4238 GfxFrame[jx][jy] = player->StepFrame;
4240 DrawLevelField(jx, jy);
4243 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4245 // ------------------------------------------------------------------------
4246 // draw things the player is pushing, if needed
4247 // ------------------------------------------------------------------------
4249 if (!player->is_pushing || !player->is_moving)
4252 int gfx_frame = GfxFrame[jx][jy];
4254 if (!IS_MOVING(jx, jy)) // push movement already finished
4256 element = Tile[next_jx][next_jy];
4257 gfx_frame = GfxFrame[next_jx][next_jy];
4260 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4261 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4262 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4264 // draw background element under pushed element (like the Sokoban field)
4265 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4267 // this allows transparent pushing animation over non-black background
4270 DrawLevelElement(jx, jy, Back[jx][jy]);
4272 DrawLevelElement(jx, jy, EL_EMPTY);
4274 if (Back[next_jx][next_jy])
4275 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4277 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4279 else if (Back[next_jx][next_jy])
4280 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4282 int px = SCREENX(jx), py = SCREENY(jy);
4283 int pxx = (TILEX - ABS(sxx)) * dx;
4284 int pyy = (TILEY - ABS(syy)) * dy;
4287 // do not draw (EM style) pushing animation when pushing is finished
4288 // (two-tile animations usually do not contain start and end frame)
4289 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4290 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4292 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4294 // masked drawing is needed for EMC style (double) movement graphics
4295 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4296 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4299 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4301 // ------------------------------------------------------------------------
4302 // draw player himself
4303 // ------------------------------------------------------------------------
4305 int graphic = getPlayerGraphic(player, move_dir);
4307 // in the case of changed player action or direction, prevent the current
4308 // animation frame from being restarted for identical animations
4309 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4310 player->Frame = last_player_frame;
4312 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4314 if (player_is_opaque)
4315 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4317 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4319 if (SHIELD_ON(player))
4321 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4322 IMG_SHIELD_NORMAL_ACTIVE);
4323 frame = getGraphicAnimationFrame(graphic, -1);
4325 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4328 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4330 // ------------------------------------------------------------------------
4331 // draw things in front of player (active dynamite or dynabombs)
4332 // ------------------------------------------------------------------------
4334 if (IS_ACTIVE_BOMB(element))
4336 int graphic = el2img(element);
4337 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4339 if (game.emulation == EMU_SUPAPLEX)
4340 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4342 DrawGraphicThruMask(sx, sy, graphic, frame);
4345 if (player_is_moving && last_element == EL_EXPLOSION)
4347 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4348 GfxElement[last_jx][last_jy] : EL_EMPTY);
4349 int graphic = el_act2img(element, ACTION_EXPLODING);
4350 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4351 int phase = ExplodePhase[last_jx][last_jy] - 1;
4352 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4355 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4358 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4360 // ------------------------------------------------------------------------
4361 // draw elements the player is just walking/passing through/under
4362 // ------------------------------------------------------------------------
4364 if (player_is_moving)
4366 // handle the field the player is leaving ...
4367 if (IS_ACCESSIBLE_INSIDE(last_element))
4368 DrawLevelField(last_jx, last_jy);
4369 else if (IS_ACCESSIBLE_UNDER(last_element))
4370 DrawLevelFieldThruMask(last_jx, last_jy);
4373 // do not redraw accessible elements if the player is just pushing them
4374 if (!player_is_moving || !player->is_pushing)
4376 // ... and the field the player is entering
4377 if (IS_ACCESSIBLE_INSIDE(element))
4378 DrawLevelField(jx, jy);
4379 else if (IS_ACCESSIBLE_UNDER(element))
4380 DrawLevelFieldThruMask(jx, jy);
4383 MarkTileDirty(sx, sy);
4387 void DrawPlayer(struct PlayerInfo *player)
4391 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4392 DrawPlayerExt(player, i);
4395 void DrawAllPlayers(void)
4399 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4400 for (j = 0; j < MAX_PLAYERS; j++)
4401 if (stored_player[j].active)
4402 DrawPlayerExt(&stored_player[j], i);
4405 void DrawPlayerField(int x, int y)
4407 if (!IS_PLAYER(x, y))
4410 DrawPlayer(PLAYERINFO(x, y));
4413 // ----------------------------------------------------------------------------
4415 void WaitForEventToContinue(void)
4417 boolean first_wait = TRUE;
4418 boolean still_wait = TRUE;
4420 if (program.headless)
4423 // simulate releasing mouse button over last gadget, if still pressed
4425 HandleGadgets(-1, -1, 0);
4427 button_status = MB_RELEASED;
4430 ClearPlayerAction();
4436 if (NextValidEvent(&event))
4440 case EVENT_BUTTONPRESS:
4441 case EVENT_FINGERPRESS:
4445 case EVENT_BUTTONRELEASE:
4446 case EVENT_FINGERRELEASE:
4447 still_wait = first_wait;
4450 case EVENT_KEYPRESS:
4451 case SDL_CONTROLLERBUTTONDOWN:
4452 case SDL_JOYBUTTONDOWN:
4457 HandleOtherEvents(&event);
4461 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4466 if (!PendingEvent())
4471 #define MAX_REQUEST_LINES 13
4472 #define MAX_REQUEST_LINE_FONT1_LEN 7
4473 #define MAX_REQUEST_LINE_FONT2_LEN 10
4475 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4477 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4479 int draw_buffer_last = GetDrawtoField();
4480 int width = request.width;
4481 int height = request.height;
4485 // when showing request dialog after game ended, deactivate game panel
4486 if (game_just_ended)
4487 game.panel.active = FALSE;
4489 game.request_active = TRUE;
4491 setRequestPosition(&sx, &sy, FALSE);
4493 button_status = MB_RELEASED;
4495 request_gadget_id = -1;
4500 boolean event_handled = FALSE;
4502 if (game_just_ended)
4504 SetDrawtoField(draw_buffer_game);
4506 HandleGameActions();
4508 SetDrawtoField(DRAW_TO_BACKBUFFER);
4510 if (global.use_envelope_request)
4512 // copy current state of request area to middle of playfield area
4513 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4521 while (NextValidEvent(&event))
4523 event_handled = TRUE;
4527 case EVENT_BUTTONPRESS:
4528 case EVENT_BUTTONRELEASE:
4529 case EVENT_MOTIONNOTIFY:
4533 if (event.type == EVENT_MOTIONNOTIFY)
4538 motion_status = TRUE;
4539 mx = ((MotionEvent *) &event)->x;
4540 my = ((MotionEvent *) &event)->y;
4544 motion_status = FALSE;
4545 mx = ((ButtonEvent *) &event)->x;
4546 my = ((ButtonEvent *) &event)->y;
4547 if (event.type == EVENT_BUTTONPRESS)
4548 button_status = ((ButtonEvent *) &event)->button;
4550 button_status = MB_RELEASED;
4553 // this sets 'request_gadget_id'
4554 HandleGadgets(mx, my, button_status);
4556 switch (request_gadget_id)
4558 case TOOL_CTRL_ID_YES:
4559 case TOOL_CTRL_ID_TOUCH_YES:
4562 case TOOL_CTRL_ID_NO:
4563 case TOOL_CTRL_ID_TOUCH_NO:
4566 case TOOL_CTRL_ID_CONFIRM:
4567 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4568 result = TRUE | FALSE;
4571 case TOOL_CTRL_ID_PLAYER_1:
4574 case TOOL_CTRL_ID_PLAYER_2:
4577 case TOOL_CTRL_ID_PLAYER_3:
4580 case TOOL_CTRL_ID_PLAYER_4:
4585 // only check clickable animations if no request gadget clicked
4586 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4593 case SDL_WINDOWEVENT:
4594 HandleWindowEvent((WindowEvent *) &event);
4597 case SDL_APP_WILLENTERBACKGROUND:
4598 case SDL_APP_DIDENTERBACKGROUND:
4599 case SDL_APP_WILLENTERFOREGROUND:
4600 case SDL_APP_DIDENTERFOREGROUND:
4601 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4604 case EVENT_KEYPRESS:
4606 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4611 if (req_state & REQ_CONFIRM)
4620 #if defined(KSYM_Rewind)
4621 case KSYM_Rewind: // for Amazon Fire TV remote
4630 #if defined(KSYM_FastForward)
4631 case KSYM_FastForward: // for Amazon Fire TV remote
4637 HandleKeysDebug(key, KEY_PRESSED);
4641 if (req_state & REQ_PLAYER)
4643 int old_player_nr = setup.network_player_nr;
4646 result = old_player_nr + 1;
4651 result = old_player_nr + 1;
4682 case EVENT_FINGERRELEASE:
4683 case EVENT_KEYRELEASE:
4684 ClearPlayerAction();
4687 case SDL_CONTROLLERBUTTONDOWN:
4688 switch (event.cbutton.button)
4690 case SDL_CONTROLLER_BUTTON_A:
4691 case SDL_CONTROLLER_BUTTON_X:
4692 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4693 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4697 case SDL_CONTROLLER_BUTTON_B:
4698 case SDL_CONTROLLER_BUTTON_Y:
4699 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4700 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4701 case SDL_CONTROLLER_BUTTON_BACK:
4706 if (req_state & REQ_PLAYER)
4708 int old_player_nr = setup.network_player_nr;
4711 result = old_player_nr + 1;
4713 switch (event.cbutton.button)
4715 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4716 case SDL_CONTROLLER_BUTTON_Y:
4720 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4721 case SDL_CONTROLLER_BUTTON_B:
4725 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4726 case SDL_CONTROLLER_BUTTON_A:
4730 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4731 case SDL_CONTROLLER_BUTTON_X:
4742 case SDL_CONTROLLERBUTTONUP:
4743 HandleJoystickEvent(&event);
4744 ClearPlayerAction();
4748 HandleOtherEvents(&event);
4753 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4755 int joy = AnyJoystick();
4757 if (joy & JOY_BUTTON_1)
4759 else if (joy & JOY_BUTTON_2)
4762 else if (AnyJoystick())
4764 int joy = AnyJoystick();
4766 if (req_state & REQ_PLAYER)
4770 else if (joy & JOY_RIGHT)
4772 else if (joy & JOY_DOWN)
4774 else if (joy & JOY_LEFT)
4781 if (game_just_ended)
4783 if (global.use_envelope_request)
4785 // copy back current state of pressed buttons inside request area
4786 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4790 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4796 SetDrawtoField(draw_buffer_last);
4798 game.request_active = FALSE;
4803 static boolean RequestDoor(char *text, unsigned int req_state)
4805 int draw_buffer_last = GetDrawtoField();
4806 unsigned int old_door_state;
4807 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4808 int font_nr = FONT_TEXT_2;
4813 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4815 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4816 font_nr = FONT_TEXT_1;
4819 if (game_status == GAME_MODE_PLAYING)
4820 BlitScreenToBitmap(backbuffer);
4822 // disable deactivated drawing when quick-loading level tape recording
4823 if (tape.playing && tape.deactivate_display)
4824 TapeDeactivateDisplayOff(TRUE);
4826 SetMouseCursor(CURSOR_DEFAULT);
4828 // pause network game while waiting for request to answer
4829 if (network.enabled &&
4830 game_status == GAME_MODE_PLAYING &&
4831 !game.all_players_gone &&
4832 req_state & REQUEST_WAIT_FOR_INPUT)
4833 SendToServer_PausePlaying();
4835 old_door_state = GetDoorState();
4837 // simulate releasing mouse button over last gadget, if still pressed
4839 HandleGadgets(-1, -1, 0);
4843 // draw released gadget before proceeding
4846 if (old_door_state & DOOR_OPEN_1)
4848 CloseDoor(DOOR_CLOSE_1);
4850 // save old door content
4851 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4852 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4855 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4856 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4858 // clear door drawing field
4859 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4861 // force DOOR font inside door area
4862 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4864 // write text for request
4865 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4867 char text_line[max_request_line_len + 1];
4873 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4875 tc = *(text_ptr + tx);
4876 // if (!tc || tc == ' ')
4877 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4881 if ((tc == '?' || tc == '!') && tl == 0)
4891 strncpy(text_line, text_ptr, tl);
4894 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4895 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4896 text_line, font_nr);
4898 text_ptr += tl + (tc == ' ' ? 1 : 0);
4899 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4904 if (req_state & REQ_ASK)
4906 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4907 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4908 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4909 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4911 else if (req_state & REQ_CONFIRM)
4913 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4914 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4916 else if (req_state & REQ_PLAYER)
4918 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4919 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4920 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4921 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4924 // copy request gadgets to door backbuffer
4925 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4927 OpenDoor(DOOR_OPEN_1);
4929 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4931 if (game_status == GAME_MODE_PLAYING)
4933 SetPanelBackground();
4934 SetDrawBackgroundMask(REDRAW_DOOR_1);
4938 SetDrawBackgroundMask(REDRAW_FIELD);
4944 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4946 // ---------- handle request buttons ----------
4947 result = RequestHandleEvents(req_state, draw_buffer_last);
4951 if (!(req_state & REQ_STAY_OPEN))
4953 CloseDoor(DOOR_CLOSE_1);
4955 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4956 (req_state & REQ_REOPEN))
4957 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4962 if (game_status == GAME_MODE_PLAYING)
4964 SetPanelBackground();
4965 SetDrawBackgroundMask(REDRAW_DOOR_1);
4969 SetDrawBackgroundMask(REDRAW_FIELD);
4972 // continue network game after request
4973 if (network.enabled &&
4974 game_status == GAME_MODE_PLAYING &&
4975 !game.all_players_gone &&
4976 req_state & REQUEST_WAIT_FOR_INPUT)
4977 SendToServer_ContinuePlaying();
4979 // restore deactivated drawing when quick-loading level tape recording
4980 if (tape.playing && tape.deactivate_display)
4981 TapeDeactivateDisplayOn();
4986 static boolean RequestEnvelope(char *text, unsigned int req_state)
4988 int draw_buffer_last = GetDrawtoField();
4991 if (game_status == GAME_MODE_PLAYING)
4992 BlitScreenToBitmap(backbuffer);
4994 // disable deactivated drawing when quick-loading level tape recording
4995 if (tape.playing && tape.deactivate_display)
4996 TapeDeactivateDisplayOff(TRUE);
4998 SetMouseCursor(CURSOR_DEFAULT);
5000 // pause network game while waiting for request to answer
5001 if (network.enabled &&
5002 game_status == GAME_MODE_PLAYING &&
5003 !game.all_players_gone &&
5004 req_state & REQUEST_WAIT_FOR_INPUT)
5005 SendToServer_PausePlaying();
5007 // simulate releasing mouse button over last gadget, if still pressed
5009 HandleGadgets(-1, -1, 0);
5013 // (replace with setting corresponding request background)
5014 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5015 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5017 // clear door drawing field
5018 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5020 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5022 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5024 if (game_status == GAME_MODE_PLAYING)
5026 SetPanelBackground();
5027 SetDrawBackgroundMask(REDRAW_DOOR_1);
5031 SetDrawBackgroundMask(REDRAW_FIELD);
5037 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5039 // ---------- handle request buttons ----------
5040 result = RequestHandleEvents(req_state, draw_buffer_last);
5044 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5048 if (game_status == GAME_MODE_PLAYING)
5050 SetPanelBackground();
5051 SetDrawBackgroundMask(REDRAW_DOOR_1);
5055 SetDrawBackgroundMask(REDRAW_FIELD);
5058 // continue network game after request
5059 if (network.enabled &&
5060 game_status == GAME_MODE_PLAYING &&
5061 !game.all_players_gone &&
5062 req_state & REQUEST_WAIT_FOR_INPUT)
5063 SendToServer_ContinuePlaying();
5065 // restore deactivated drawing when quick-loading level tape recording
5066 if (tape.playing && tape.deactivate_display)
5067 TapeDeactivateDisplayOn();
5072 boolean Request(char *text, unsigned int req_state)
5074 boolean overlay_enabled = GetOverlayEnabled();
5077 game.request_active_or_moving = TRUE;
5079 SetOverlayEnabled(FALSE);
5081 if (global.use_envelope_request)
5082 result = RequestEnvelope(text, req_state);
5084 result = RequestDoor(text, req_state);
5086 SetOverlayEnabled(overlay_enabled);
5088 game.request_active_or_moving = FALSE;
5093 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5095 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5096 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5099 if (dpo1->sort_priority != dpo2->sort_priority)
5100 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5102 compare_result = dpo1->nr - dpo2->nr;
5104 return compare_result;
5107 void InitGraphicCompatibilityInfo_Doors(void)
5113 struct DoorInfo *door;
5117 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5118 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5120 { -1, -1, -1, NULL }
5122 struct Rect door_rect_list[] =
5124 { DX, DY, DXSIZE, DYSIZE },
5125 { VX, VY, VXSIZE, VYSIZE }
5129 for (i = 0; doors[i].door_token != -1; i++)
5131 int door_token = doors[i].door_token;
5132 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5133 int part_1 = doors[i].part_1;
5134 int part_8 = doors[i].part_8;
5135 int part_2 = part_1 + 1;
5136 int part_3 = part_1 + 2;
5137 struct DoorInfo *door = doors[i].door;
5138 struct Rect *door_rect = &door_rect_list[door_index];
5139 boolean door_gfx_redefined = FALSE;
5141 // check if any door part graphic definitions have been redefined
5143 for (j = 0; door_part_controls[j].door_token != -1; j++)
5145 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5146 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5148 if (dpc->door_token == door_token && fi->redefined)
5149 door_gfx_redefined = TRUE;
5152 // check for old-style door graphic/animation modifications
5154 if (!door_gfx_redefined)
5156 if (door->anim_mode & ANIM_STATIC_PANEL)
5158 door->panel.step_xoffset = 0;
5159 door->panel.step_yoffset = 0;
5162 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5164 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5165 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5166 int num_door_steps, num_panel_steps;
5168 // remove door part graphics other than the two default wings
5170 for (j = 0; door_part_controls[j].door_token != -1; j++)
5172 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5173 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5175 if (dpc->graphic >= part_3 &&
5176 dpc->graphic <= part_8)
5180 // set graphics and screen positions of the default wings
5182 g_part_1->width = door_rect->width;
5183 g_part_1->height = door_rect->height;
5184 g_part_2->width = door_rect->width;
5185 g_part_2->height = door_rect->height;
5186 g_part_2->src_x = door_rect->width;
5187 g_part_2->src_y = g_part_1->src_y;
5189 door->part_2.x = door->part_1.x;
5190 door->part_2.y = door->part_1.y;
5192 if (door->width != -1)
5194 g_part_1->width = door->width;
5195 g_part_2->width = door->width;
5197 // special treatment for graphics and screen position of right wing
5198 g_part_2->src_x += door_rect->width - door->width;
5199 door->part_2.x += door_rect->width - door->width;
5202 if (door->height != -1)
5204 g_part_1->height = door->height;
5205 g_part_2->height = door->height;
5207 // special treatment for graphics and screen position of bottom wing
5208 g_part_2->src_y += door_rect->height - door->height;
5209 door->part_2.y += door_rect->height - door->height;
5212 // set animation delays for the default wings and panels
5214 door->part_1.step_delay = door->step_delay;
5215 door->part_2.step_delay = door->step_delay;
5216 door->panel.step_delay = door->step_delay;
5218 // set animation draw order for the default wings
5220 door->part_1.sort_priority = 2; // draw left wing over ...
5221 door->part_2.sort_priority = 1; // ... right wing
5223 // set animation draw offset for the default wings
5225 if (door->anim_mode & ANIM_HORIZONTAL)
5227 door->part_1.step_xoffset = door->step_offset;
5228 door->part_1.step_yoffset = 0;
5229 door->part_2.step_xoffset = door->step_offset * -1;
5230 door->part_2.step_yoffset = 0;
5232 num_door_steps = g_part_1->width / door->step_offset;
5234 else // ANIM_VERTICAL
5236 door->part_1.step_xoffset = 0;
5237 door->part_1.step_yoffset = door->step_offset;
5238 door->part_2.step_xoffset = 0;
5239 door->part_2.step_yoffset = door->step_offset * -1;
5241 num_door_steps = g_part_1->height / door->step_offset;
5244 // set animation draw offset for the default panels
5246 if (door->step_offset > 1)
5248 num_panel_steps = 2 * door_rect->height / door->step_offset;
5249 door->panel.start_step = num_panel_steps - num_door_steps;
5250 door->panel.start_step_closing = door->panel.start_step;
5254 num_panel_steps = door_rect->height / door->step_offset;
5255 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5256 door->panel.start_step_closing = door->panel.start_step;
5257 door->panel.step_delay *= 2;
5264 void InitDoors(void)
5268 for (i = 0; door_part_controls[i].door_token != -1; i++)
5270 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5271 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5273 // initialize "start_step_opening" and "start_step_closing", if needed
5274 if (dpc->pos->start_step_opening == 0 &&
5275 dpc->pos->start_step_closing == 0)
5277 // dpc->pos->start_step_opening = dpc->pos->start_step;
5278 dpc->pos->start_step_closing = dpc->pos->start_step;
5281 // fill structure for door part draw order (sorted below)
5283 dpo->sort_priority = dpc->pos->sort_priority;
5286 // sort door part controls according to sort_priority and graphic number
5287 qsort(door_part_order, MAX_DOOR_PARTS,
5288 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5291 unsigned int OpenDoor(unsigned int door_state)
5293 if (door_state & DOOR_COPY_BACK)
5295 if (door_state & DOOR_OPEN_1)
5296 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5297 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5299 if (door_state & DOOR_OPEN_2)
5300 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5301 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5303 door_state &= ~DOOR_COPY_BACK;
5306 return MoveDoor(door_state);
5309 unsigned int CloseDoor(unsigned int door_state)
5311 unsigned int old_door_state = GetDoorState();
5313 if (!(door_state & DOOR_NO_COPY_BACK))
5315 if (old_door_state & DOOR_OPEN_1)
5316 BlitBitmap(backbuffer, bitmap_db_door_1,
5317 DX, DY, DXSIZE, DYSIZE, 0, 0);
5319 if (old_door_state & DOOR_OPEN_2)
5320 BlitBitmap(backbuffer, bitmap_db_door_2,
5321 VX, VY, VXSIZE, VYSIZE, 0, 0);
5323 door_state &= ~DOOR_NO_COPY_BACK;
5326 return MoveDoor(door_state);
5329 unsigned int GetDoorState(void)
5331 return MoveDoor(DOOR_GET_STATE);
5334 unsigned int SetDoorState(unsigned int door_state)
5336 return MoveDoor(door_state | DOOR_SET_STATE);
5339 static int euclid(int a, int b)
5341 return (b ? euclid(b, a % b) : a);
5344 unsigned int MoveDoor(unsigned int door_state)
5346 struct Rect door_rect_list[] =
5348 { DX, DY, DXSIZE, DYSIZE },
5349 { VX, VY, VXSIZE, VYSIZE }
5351 static int door1 = DOOR_CLOSE_1;
5352 static int door2 = DOOR_CLOSE_2;
5353 DelayCounter door_delay = { 0 };
5356 if (door_state == DOOR_GET_STATE)
5357 return (door1 | door2);
5359 if (door_state & DOOR_SET_STATE)
5361 if (door_state & DOOR_ACTION_1)
5362 door1 = door_state & DOOR_ACTION_1;
5363 if (door_state & DOOR_ACTION_2)
5364 door2 = door_state & DOOR_ACTION_2;
5366 return (door1 | door2);
5369 if (!(door_state & DOOR_FORCE_REDRAW))
5371 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5372 door_state &= ~DOOR_OPEN_1;
5373 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5374 door_state &= ~DOOR_CLOSE_1;
5375 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5376 door_state &= ~DOOR_OPEN_2;
5377 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5378 door_state &= ~DOOR_CLOSE_2;
5381 if (global.autoplay_leveldir)
5383 door_state |= DOOR_NO_DELAY;
5384 door_state &= ~DOOR_CLOSE_ALL;
5387 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5388 door_state |= DOOR_NO_DELAY;
5390 if (door_state & DOOR_ACTION)
5392 boolean door_panel_drawn[NUM_DOORS];
5393 boolean panel_has_doors[NUM_DOORS];
5394 boolean door_part_skip[MAX_DOOR_PARTS];
5395 boolean door_part_done[MAX_DOOR_PARTS];
5396 boolean door_part_done_all;
5397 int num_steps[MAX_DOOR_PARTS];
5398 int max_move_delay = 0; // delay for complete animations of all doors
5399 int max_step_delay = 0; // delay (ms) between two animation frames
5400 int num_move_steps = 0; // number of animation steps for all doors
5401 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5402 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5406 for (i = 0; i < NUM_DOORS; i++)
5407 panel_has_doors[i] = FALSE;
5409 for (i = 0; i < MAX_DOOR_PARTS; i++)
5411 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5412 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5413 int door_token = dpc->door_token;
5415 door_part_done[i] = FALSE;
5416 door_part_skip[i] = (!(door_state & door_token) ||
5420 for (i = 0; i < MAX_DOOR_PARTS; i++)
5422 int nr = door_part_order[i].nr;
5423 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5424 struct DoorPartPosInfo *pos = dpc->pos;
5425 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5426 int door_token = dpc->door_token;
5427 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5428 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5429 int step_xoffset = ABS(pos->step_xoffset);
5430 int step_yoffset = ABS(pos->step_yoffset);
5431 int step_delay = pos->step_delay;
5432 int current_door_state = door_state & door_token;
5433 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5434 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5435 boolean part_opening = (is_panel ? door_closing : door_opening);
5436 int start_step = (part_opening ? pos->start_step_opening :
5437 pos->start_step_closing);
5438 float move_xsize = (step_xoffset ? g->width : 0);
5439 float move_ysize = (step_yoffset ? g->height : 0);
5440 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5441 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5442 int move_steps = (move_xsteps && move_ysteps ?
5443 MIN(move_xsteps, move_ysteps) :
5444 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5445 int move_delay = move_steps * step_delay;
5447 if (door_part_skip[nr])
5450 max_move_delay = MAX(max_move_delay, move_delay);
5451 max_step_delay = (max_step_delay == 0 ? step_delay :
5452 euclid(max_step_delay, step_delay));
5453 num_steps[nr] = move_steps;
5457 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5459 panel_has_doors[door_index] = TRUE;
5463 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5465 num_move_steps = max_move_delay / max_step_delay;
5466 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5468 door_delay.value = max_step_delay;
5470 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5472 start = num_move_steps - 1;
5476 // opening door sound has priority over simultaneously closing door
5477 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5479 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5481 if (door_state & DOOR_OPEN_1)
5482 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5483 if (door_state & DOOR_OPEN_2)
5484 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5486 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5488 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5490 if (door_state & DOOR_CLOSE_1)
5491 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5492 if (door_state & DOOR_CLOSE_2)
5493 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5497 for (k = start; k < num_move_steps; k++)
5499 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5501 door_part_done_all = TRUE;
5503 for (i = 0; i < NUM_DOORS; i++)
5504 door_panel_drawn[i] = FALSE;
5506 for (i = 0; i < MAX_DOOR_PARTS; i++)
5508 int nr = door_part_order[i].nr;
5509 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5510 struct DoorPartPosInfo *pos = dpc->pos;
5511 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5512 int door_token = dpc->door_token;
5513 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5514 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5515 boolean is_panel_and_door_has_closed = FALSE;
5516 struct Rect *door_rect = &door_rect_list[door_index];
5517 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5519 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5520 int current_door_state = door_state & door_token;
5521 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5522 boolean door_closing = !door_opening;
5523 boolean part_opening = (is_panel ? door_closing : door_opening);
5524 boolean part_closing = !part_opening;
5525 int start_step = (part_opening ? pos->start_step_opening :
5526 pos->start_step_closing);
5527 int step_delay = pos->step_delay;
5528 int step_factor = step_delay / max_step_delay;
5529 int k1 = (step_factor ? k / step_factor + 1 : k);
5530 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5531 int kk = MAX(0, k2);
5534 int src_x, src_y, src_xx, src_yy;
5535 int dst_x, dst_y, dst_xx, dst_yy;
5538 if (door_part_skip[nr])
5541 if (!(door_state & door_token))
5549 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5550 int kk_door = MAX(0, k2_door);
5551 int sync_frame = kk_door * door_delay.value;
5552 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5554 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5555 &g_src_x, &g_src_y);
5560 if (!door_panel_drawn[door_index])
5562 ClearRectangle(drawto, door_rect->x, door_rect->y,
5563 door_rect->width, door_rect->height);
5565 door_panel_drawn[door_index] = TRUE;
5568 // draw opening or closing door parts
5570 if (pos->step_xoffset < 0) // door part on right side
5573 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5576 if (dst_xx + width > door_rect->width)
5577 width = door_rect->width - dst_xx;
5579 else // door part on left side
5582 dst_xx = pos->x - kk * pos->step_xoffset;
5586 src_xx = ABS(dst_xx);
5590 width = g->width - src_xx;
5592 if (width > door_rect->width)
5593 width = door_rect->width;
5595 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5598 if (pos->step_yoffset < 0) // door part on bottom side
5601 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5604 if (dst_yy + height > door_rect->height)
5605 height = door_rect->height - dst_yy;
5607 else // door part on top side
5610 dst_yy = pos->y - kk * pos->step_yoffset;
5614 src_yy = ABS(dst_yy);
5618 height = g->height - src_yy;
5621 src_x = g_src_x + src_xx;
5622 src_y = g_src_y + src_yy;
5624 dst_x = door_rect->x + dst_xx;
5625 dst_y = door_rect->y + dst_yy;
5627 is_panel_and_door_has_closed =
5630 panel_has_doors[door_index] &&
5631 k >= num_move_steps_doors_only - 1);
5633 if (width >= 0 && width <= g->width &&
5634 height >= 0 && height <= g->height &&
5635 !is_panel_and_door_has_closed)
5637 if (is_panel || !pos->draw_masked)
5638 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5641 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5645 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5647 if ((part_opening && (width < 0 || height < 0)) ||
5648 (part_closing && (width >= g->width && height >= g->height)))
5649 door_part_done[nr] = TRUE;
5651 // continue door part animations, but not panel after door has closed
5652 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5653 door_part_done_all = FALSE;
5656 if (!(door_state & DOOR_NO_DELAY))
5660 SkipUntilDelayReached(&door_delay, &k, last_frame);
5662 // prevent OS (Windows) from complaining about program not responding
5666 if (door_part_done_all)
5670 if (!(door_state & DOOR_NO_DELAY))
5672 // wait for specified door action post delay
5673 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5674 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5675 else if (door_state & DOOR_ACTION_1)
5676 door_delay.value = door_1.post_delay;
5677 else if (door_state & DOOR_ACTION_2)
5678 door_delay.value = door_2.post_delay;
5680 while (!DelayReached(&door_delay))
5685 if (door_state & DOOR_ACTION_1)
5686 door1 = door_state & DOOR_ACTION_1;
5687 if (door_state & DOOR_ACTION_2)
5688 door2 = door_state & DOOR_ACTION_2;
5690 // draw masked border over door area
5691 DrawMaskedBorder(REDRAW_DOOR_1);
5692 DrawMaskedBorder(REDRAW_DOOR_2);
5694 ClearAutoRepeatKeyEvents();
5696 return (door1 | door2);
5699 static boolean useSpecialEditorDoor(void)
5701 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5702 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5704 // do not draw special editor door if editor border defined or redefined
5705 if (graphic_info[graphic].bitmap != NULL || redefined)
5708 // do not draw special editor door if global border defined to be empty
5709 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5712 // do not draw special editor door if viewport definitions do not match
5716 EY + EYSIZE != VY + VYSIZE)
5722 void DrawSpecialEditorDoor(void)
5724 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5725 int top_border_width = gfx1->width;
5726 int top_border_height = gfx1->height;
5727 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5728 int ex = EX - outer_border;
5729 int ey = EY - outer_border;
5730 int vy = VY - outer_border;
5731 int exsize = EXSIZE + 2 * outer_border;
5733 if (!useSpecialEditorDoor())
5736 // draw bigger level editor toolbox window
5737 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5738 top_border_width, top_border_height, ex, ey - top_border_height);
5739 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5740 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5742 redraw_mask |= REDRAW_ALL;
5745 void UndrawSpecialEditorDoor(void)
5747 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5748 int top_border_width = gfx1->width;
5749 int top_border_height = gfx1->height;
5750 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5751 int ex = EX - outer_border;
5752 int ey = EY - outer_border;
5753 int ey_top = ey - top_border_height;
5754 int exsize = EXSIZE + 2 * outer_border;
5755 int eysize = EYSIZE + 2 * outer_border;
5757 if (!useSpecialEditorDoor())
5760 // draw normal tape recorder window
5761 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5763 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5764 ex, ey_top, top_border_width, top_border_height,
5766 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5767 ex, ey, exsize, eysize, ex, ey);
5771 // if screen background is set to "[NONE]", clear editor toolbox window
5772 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5773 ClearRectangle(drawto, ex, ey, exsize, eysize);
5776 redraw_mask |= REDRAW_ALL;
5780 // ---------- new tool button stuff -------------------------------------------
5785 struct TextPosInfo *pos;
5787 boolean is_touch_button;
5789 } toolbutton_info[NUM_TOOL_BUTTONS] =
5792 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5793 TOOL_CTRL_ID_YES, FALSE, "yes"
5796 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5797 TOOL_CTRL_ID_NO, FALSE, "no"
5800 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5801 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5804 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5805 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5808 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5809 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5812 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5813 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5816 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5817 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5820 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5821 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5824 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5825 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5828 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5829 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5833 void CreateToolButtons(void)
5837 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5839 int graphic = toolbutton_info[i].graphic;
5840 struct GraphicInfo *gfx = &graphic_info[graphic];
5841 struct TextPosInfo *pos = toolbutton_info[i].pos;
5842 struct GadgetInfo *gi;
5843 Bitmap *deco_bitmap = None;
5844 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5845 unsigned int event_mask = GD_EVENT_RELEASED;
5846 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5847 int base_x = (is_touch_button ? 0 : DX);
5848 int base_y = (is_touch_button ? 0 : DY);
5849 int gd_x = gfx->src_x;
5850 int gd_y = gfx->src_y;
5851 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5852 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5857 // do not use touch buttons if overlay touch buttons are disabled
5858 if (is_touch_button && !setup.touch.overlay_buttons)
5861 if (global.use_envelope_request && !is_touch_button)
5863 setRequestPosition(&base_x, &base_y, TRUE);
5865 // check if request buttons are outside of envelope and fix, if needed
5866 if (x < 0 || x + gfx->width > request.width ||
5867 y < 0 || y + gfx->height > request.height)
5869 if (id == TOOL_CTRL_ID_YES)
5872 y = request.height - 2 * request.border_size - gfx->height;
5874 else if (id == TOOL_CTRL_ID_NO)
5876 x = request.width - 2 * request.border_size - gfx->width;
5877 y = request.height - 2 * request.border_size - gfx->height;
5879 else if (id == TOOL_CTRL_ID_CONFIRM)
5881 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5882 y = request.height - 2 * request.border_size - gfx->height;
5884 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5886 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5888 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5889 y = request.height - 2 * request.border_size - gfx->height * 2;
5891 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5892 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5897 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5899 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5901 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5902 pos->size, &deco_bitmap, &deco_x, &deco_y);
5903 deco_xpos = (gfx->width - pos->size) / 2;
5904 deco_ypos = (gfx->height - pos->size) / 2;
5907 gi = CreateGadget(GDI_CUSTOM_ID, id,
5908 GDI_IMAGE_ID, graphic,
5909 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5912 GDI_WIDTH, gfx->width,
5913 GDI_HEIGHT, gfx->height,
5914 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5915 GDI_STATE, GD_BUTTON_UNPRESSED,
5916 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5917 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5918 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5919 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5920 GDI_DECORATION_SIZE, pos->size, pos->size,
5921 GDI_DECORATION_SHIFTING, 1, 1,
5922 GDI_DIRECT_DRAW, FALSE,
5923 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5924 GDI_EVENT_MASK, event_mask,
5925 GDI_CALLBACK_ACTION, HandleToolButtons,
5929 Fail("cannot create gadget");
5931 tool_gadget[id] = gi;
5935 void FreeToolButtons(void)
5939 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5940 FreeGadget(tool_gadget[i]);
5943 static void UnmapToolButtons(void)
5947 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5948 UnmapGadget(tool_gadget[i]);
5951 static void HandleToolButtons(struct GadgetInfo *gi)
5953 request_gadget_id = gi->custom_id;
5956 static struct Mapping_EM_to_RND_object
5959 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5960 boolean is_backside; // backside of moving element
5966 em_object_mapping_list[GAME_TILE_MAX + 1] =
5969 Zborder, FALSE, FALSE,
5973 Zplayer, FALSE, FALSE,
5982 Ztank, FALSE, FALSE,
5986 Zeater, FALSE, FALSE,
5990 Zdynamite, FALSE, FALSE,
5994 Zboom, FALSE, FALSE,
5999 Xchain, FALSE, FALSE,
6000 EL_DEFAULT, ACTION_EXPLODING, -1
6003 Xboom_bug, FALSE, FALSE,
6004 EL_BUG, ACTION_EXPLODING, -1
6007 Xboom_tank, FALSE, FALSE,
6008 EL_SPACESHIP, ACTION_EXPLODING, -1
6011 Xboom_android, FALSE, FALSE,
6012 EL_EMC_ANDROID, ACTION_OTHER, -1
6015 Xboom_1, FALSE, FALSE,
6016 EL_DEFAULT, ACTION_EXPLODING, -1
6019 Xboom_2, FALSE, FALSE,
6020 EL_DEFAULT, ACTION_EXPLODING, -1
6024 Xblank, TRUE, FALSE,
6029 Xsplash_e, FALSE, FALSE,
6030 EL_ACID_SPLASH_RIGHT, -1, -1
6033 Xsplash_w, FALSE, FALSE,
6034 EL_ACID_SPLASH_LEFT, -1, -1
6038 Xplant, TRUE, FALSE,
6039 EL_EMC_PLANT, -1, -1
6042 Yplant, FALSE, FALSE,
6043 EL_EMC_PLANT, -1, -1
6047 Xacid_1, TRUE, FALSE,
6051 Xacid_2, FALSE, FALSE,
6055 Xacid_3, FALSE, FALSE,
6059 Xacid_4, FALSE, FALSE,
6063 Xacid_5, FALSE, FALSE,
6067 Xacid_6, FALSE, FALSE,
6071 Xacid_7, FALSE, FALSE,
6075 Xacid_8, FALSE, FALSE,
6080 Xfake_acid_1, TRUE, FALSE,
6081 EL_EMC_FAKE_ACID, -1, -1
6084 Xfake_acid_2, FALSE, FALSE,
6085 EL_EMC_FAKE_ACID, -1, -1
6088 Xfake_acid_3, FALSE, FALSE,
6089 EL_EMC_FAKE_ACID, -1, -1
6092 Xfake_acid_4, FALSE, FALSE,
6093 EL_EMC_FAKE_ACID, -1, -1
6096 Xfake_acid_5, FALSE, FALSE,
6097 EL_EMC_FAKE_ACID, -1, -1
6100 Xfake_acid_6, FALSE, FALSE,
6101 EL_EMC_FAKE_ACID, -1, -1
6104 Xfake_acid_7, FALSE, FALSE,
6105 EL_EMC_FAKE_ACID, -1, -1
6108 Xfake_acid_8, FALSE, FALSE,
6109 EL_EMC_FAKE_ACID, -1, -1
6113 Xfake_acid_1_player, FALSE, FALSE,
6114 EL_EMC_FAKE_ACID, -1, -1
6117 Xfake_acid_2_player, FALSE, FALSE,
6118 EL_EMC_FAKE_ACID, -1, -1
6121 Xfake_acid_3_player, FALSE, FALSE,
6122 EL_EMC_FAKE_ACID, -1, -1
6125 Xfake_acid_4_player, FALSE, FALSE,
6126 EL_EMC_FAKE_ACID, -1, -1
6129 Xfake_acid_5_player, FALSE, FALSE,
6130 EL_EMC_FAKE_ACID, -1, -1
6133 Xfake_acid_6_player, FALSE, FALSE,
6134 EL_EMC_FAKE_ACID, -1, -1
6137 Xfake_acid_7_player, FALSE, FALSE,
6138 EL_EMC_FAKE_ACID, -1, -1
6141 Xfake_acid_8_player, FALSE, FALSE,
6142 EL_EMC_FAKE_ACID, -1, -1
6146 Xgrass, TRUE, FALSE,
6147 EL_EMC_GRASS, -1, -1
6150 Ygrass_nB, FALSE, FALSE,
6151 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6154 Ygrass_eB, FALSE, FALSE,
6155 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6158 Ygrass_sB, FALSE, FALSE,
6159 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6162 Ygrass_wB, FALSE, FALSE,
6163 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6171 Ydirt_nB, FALSE, FALSE,
6172 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6175 Ydirt_eB, FALSE, FALSE,
6176 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6179 Ydirt_sB, FALSE, FALSE,
6180 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6183 Ydirt_wB, FALSE, FALSE,
6184 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6188 Xandroid, TRUE, FALSE,
6189 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6192 Xandroid_1_n, FALSE, FALSE,
6193 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6196 Xandroid_2_n, FALSE, FALSE,
6197 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6200 Xandroid_1_e, FALSE, FALSE,
6201 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6204 Xandroid_2_e, FALSE, FALSE,
6205 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6208 Xandroid_1_w, FALSE, FALSE,
6209 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6212 Xandroid_2_w, FALSE, FALSE,
6213 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6216 Xandroid_1_s, FALSE, FALSE,
6217 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6220 Xandroid_2_s, FALSE, FALSE,
6221 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6224 Yandroid_n, FALSE, FALSE,
6225 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6228 Yandroid_nB, FALSE, TRUE,
6229 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6232 Yandroid_ne, FALSE, FALSE,
6233 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6236 Yandroid_neB, FALSE, TRUE,
6237 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6240 Yandroid_e, FALSE, FALSE,
6241 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6244 Yandroid_eB, FALSE, TRUE,
6245 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6248 Yandroid_se, FALSE, FALSE,
6249 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6252 Yandroid_seB, FALSE, TRUE,
6253 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6256 Yandroid_s, FALSE, FALSE,
6257 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6260 Yandroid_sB, FALSE, TRUE,
6261 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6264 Yandroid_sw, FALSE, FALSE,
6265 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6268 Yandroid_swB, FALSE, TRUE,
6269 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6272 Yandroid_w, FALSE, FALSE,
6273 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6276 Yandroid_wB, FALSE, TRUE,
6277 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6280 Yandroid_nw, FALSE, FALSE,
6281 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6284 Yandroid_nwB, FALSE, TRUE,
6285 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6289 Xeater_n, TRUE, FALSE,
6290 EL_YAMYAM_UP, -1, -1
6293 Xeater_e, TRUE, FALSE,
6294 EL_YAMYAM_RIGHT, -1, -1
6297 Xeater_w, TRUE, FALSE,
6298 EL_YAMYAM_LEFT, -1, -1
6301 Xeater_s, TRUE, FALSE,
6302 EL_YAMYAM_DOWN, -1, -1
6305 Yeater_n, FALSE, FALSE,
6306 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6309 Yeater_nB, FALSE, TRUE,
6310 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6313 Yeater_e, FALSE, FALSE,
6314 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6317 Yeater_eB, FALSE, TRUE,
6318 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6321 Yeater_s, FALSE, FALSE,
6322 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6325 Yeater_sB, FALSE, TRUE,
6326 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6329 Yeater_w, FALSE, FALSE,
6330 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6333 Yeater_wB, FALSE, TRUE,
6334 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6337 Yeater_stone, FALSE, FALSE,
6338 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6341 Yeater_spring, FALSE, FALSE,
6342 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6346 Xalien, TRUE, FALSE,
6350 Xalien_pause, FALSE, FALSE,
6354 Yalien_n, FALSE, FALSE,
6355 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6358 Yalien_nB, FALSE, TRUE,
6359 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6362 Yalien_e, FALSE, FALSE,
6363 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6366 Yalien_eB, FALSE, TRUE,
6367 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6370 Yalien_s, FALSE, FALSE,
6371 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6374 Yalien_sB, FALSE, TRUE,
6375 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6378 Yalien_w, FALSE, FALSE,
6379 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6382 Yalien_wB, FALSE, TRUE,
6383 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6386 Yalien_stone, FALSE, FALSE,
6387 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6390 Yalien_spring, FALSE, FALSE,
6391 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6395 Xbug_1_n, TRUE, FALSE,
6399 Xbug_1_e, TRUE, FALSE,
6400 EL_BUG_RIGHT, -1, -1
6403 Xbug_1_s, TRUE, FALSE,
6407 Xbug_1_w, TRUE, FALSE,
6411 Xbug_2_n, FALSE, FALSE,
6415 Xbug_2_e, FALSE, FALSE,
6416 EL_BUG_RIGHT, -1, -1
6419 Xbug_2_s, FALSE, FALSE,
6423 Xbug_2_w, FALSE, FALSE,
6427 Ybug_n, FALSE, FALSE,
6428 EL_BUG, ACTION_MOVING, MV_BIT_UP
6431 Ybug_nB, FALSE, TRUE,
6432 EL_BUG, ACTION_MOVING, MV_BIT_UP
6435 Ybug_e, FALSE, FALSE,
6436 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6439 Ybug_eB, FALSE, TRUE,
6440 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6443 Ybug_s, FALSE, FALSE,
6444 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6447 Ybug_sB, FALSE, TRUE,
6448 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6451 Ybug_w, FALSE, FALSE,
6452 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6455 Ybug_wB, FALSE, TRUE,
6456 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6459 Ybug_w_n, FALSE, FALSE,
6460 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6463 Ybug_n_e, FALSE, FALSE,
6464 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6467 Ybug_e_s, FALSE, FALSE,
6468 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6471 Ybug_s_w, FALSE, FALSE,
6472 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6475 Ybug_e_n, FALSE, FALSE,
6476 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6479 Ybug_s_e, FALSE, FALSE,
6480 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6483 Ybug_w_s, FALSE, FALSE,
6484 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6487 Ybug_n_w, FALSE, FALSE,
6488 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6491 Ybug_stone, FALSE, FALSE,
6492 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6495 Ybug_spring, FALSE, FALSE,
6496 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6500 Xtank_1_n, TRUE, FALSE,
6501 EL_SPACESHIP_UP, -1, -1
6504 Xtank_1_e, TRUE, FALSE,
6505 EL_SPACESHIP_RIGHT, -1, -1
6508 Xtank_1_s, TRUE, FALSE,
6509 EL_SPACESHIP_DOWN, -1, -1
6512 Xtank_1_w, TRUE, FALSE,
6513 EL_SPACESHIP_LEFT, -1, -1
6516 Xtank_2_n, FALSE, FALSE,
6517 EL_SPACESHIP_UP, -1, -1
6520 Xtank_2_e, FALSE, FALSE,
6521 EL_SPACESHIP_RIGHT, -1, -1
6524 Xtank_2_s, FALSE, FALSE,
6525 EL_SPACESHIP_DOWN, -1, -1
6528 Xtank_2_w, FALSE, FALSE,
6529 EL_SPACESHIP_LEFT, -1, -1
6532 Ytank_n, FALSE, FALSE,
6533 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6536 Ytank_nB, FALSE, TRUE,
6537 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6540 Ytank_e, FALSE, FALSE,
6541 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6544 Ytank_eB, FALSE, TRUE,
6545 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6548 Ytank_s, FALSE, FALSE,
6549 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6552 Ytank_sB, FALSE, TRUE,
6553 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6556 Ytank_w, FALSE, FALSE,
6557 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6560 Ytank_wB, FALSE, TRUE,
6561 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6564 Ytank_w_n, FALSE, FALSE,
6565 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6568 Ytank_n_e, FALSE, FALSE,
6569 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6572 Ytank_e_s, FALSE, FALSE,
6573 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6576 Ytank_s_w, FALSE, FALSE,
6577 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6580 Ytank_e_n, FALSE, FALSE,
6581 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6584 Ytank_s_e, FALSE, FALSE,
6585 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6588 Ytank_w_s, FALSE, FALSE,
6589 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6592 Ytank_n_w, FALSE, FALSE,
6593 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6596 Ytank_stone, FALSE, FALSE,
6597 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6600 Ytank_spring, FALSE, FALSE,
6601 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6605 Xemerald, TRUE, FALSE,
6609 Xemerald_pause, FALSE, FALSE,
6613 Xemerald_fall, FALSE, FALSE,
6617 Xemerald_shine, FALSE, FALSE,
6618 EL_EMERALD, ACTION_TWINKLING, -1
6621 Yemerald_s, FALSE, FALSE,
6622 EL_EMERALD, ACTION_FALLING, -1
6625 Yemerald_sB, FALSE, TRUE,
6626 EL_EMERALD, ACTION_FALLING, -1
6629 Yemerald_e, FALSE, FALSE,
6630 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6633 Yemerald_eB, FALSE, TRUE,
6634 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6637 Yemerald_w, FALSE, FALSE,
6638 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6641 Yemerald_wB, FALSE, TRUE,
6642 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6645 Yemerald_blank, FALSE, FALSE,
6646 EL_EMERALD, ACTION_COLLECTING, -1
6650 Xdiamond, TRUE, FALSE,
6654 Xdiamond_pause, FALSE, FALSE,
6658 Xdiamond_fall, FALSE, FALSE,
6662 Xdiamond_shine, FALSE, FALSE,
6663 EL_DIAMOND, ACTION_TWINKLING, -1
6666 Ydiamond_s, FALSE, FALSE,
6667 EL_DIAMOND, ACTION_FALLING, -1
6670 Ydiamond_sB, FALSE, TRUE,
6671 EL_DIAMOND, ACTION_FALLING, -1
6674 Ydiamond_e, FALSE, FALSE,
6675 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6678 Ydiamond_eB, FALSE, TRUE,
6679 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6682 Ydiamond_w, FALSE, FALSE,
6683 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6686 Ydiamond_wB, FALSE, TRUE,
6687 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6690 Ydiamond_blank, FALSE, FALSE,
6691 EL_DIAMOND, ACTION_COLLECTING, -1
6694 Ydiamond_stone, FALSE, FALSE,
6695 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6699 Xstone, TRUE, FALSE,
6703 Xstone_pause, FALSE, FALSE,
6707 Xstone_fall, FALSE, FALSE,
6711 Ystone_s, FALSE, FALSE,
6712 EL_ROCK, ACTION_FALLING, -1
6715 Ystone_sB, FALSE, TRUE,
6716 EL_ROCK, ACTION_FALLING, -1
6719 Ystone_e, FALSE, FALSE,
6720 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6723 Ystone_eB, FALSE, TRUE,
6724 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6727 Ystone_w, FALSE, FALSE,
6728 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6731 Ystone_wB, FALSE, TRUE,
6732 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6740 Xbomb_pause, FALSE, FALSE,
6744 Xbomb_fall, FALSE, FALSE,
6748 Ybomb_s, FALSE, FALSE,
6749 EL_BOMB, ACTION_FALLING, -1
6752 Ybomb_sB, FALSE, TRUE,
6753 EL_BOMB, ACTION_FALLING, -1
6756 Ybomb_e, FALSE, FALSE,
6757 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6760 Ybomb_eB, FALSE, TRUE,
6761 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6764 Ybomb_w, FALSE, FALSE,
6765 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6768 Ybomb_wB, FALSE, TRUE,
6769 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6772 Ybomb_blank, FALSE, FALSE,
6773 EL_BOMB, ACTION_ACTIVATING, -1
6781 Xnut_pause, FALSE, FALSE,
6785 Xnut_fall, FALSE, FALSE,
6789 Ynut_s, FALSE, FALSE,
6790 EL_NUT, ACTION_FALLING, -1
6793 Ynut_sB, FALSE, TRUE,
6794 EL_NUT, ACTION_FALLING, -1
6797 Ynut_e, FALSE, FALSE,
6798 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6801 Ynut_eB, FALSE, TRUE,
6802 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6805 Ynut_w, FALSE, FALSE,
6806 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6809 Ynut_wB, FALSE, TRUE,
6810 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6813 Ynut_stone, FALSE, FALSE,
6814 EL_NUT, ACTION_BREAKING, -1
6818 Xspring, TRUE, FALSE,
6822 Xspring_pause, FALSE, FALSE,
6826 Xspring_e, TRUE, FALSE,
6827 EL_SPRING_RIGHT, -1, -1
6830 Xspring_w, TRUE, FALSE,
6831 EL_SPRING_LEFT, -1, -1
6834 Xspring_fall, FALSE, FALSE,
6838 Yspring_s, FALSE, FALSE,
6839 EL_SPRING, ACTION_FALLING, -1
6842 Yspring_sB, FALSE, TRUE,
6843 EL_SPRING, ACTION_FALLING, -1
6846 Yspring_e, FALSE, FALSE,
6847 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6850 Yspring_eB, FALSE, TRUE,
6851 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6854 Yspring_w, FALSE, FALSE,
6855 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6858 Yspring_wB, FALSE, TRUE,
6859 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6862 Yspring_alien_e, FALSE, FALSE,
6863 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6866 Yspring_alien_eB, FALSE, TRUE,
6867 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6870 Yspring_alien_w, FALSE, FALSE,
6871 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6874 Yspring_alien_wB, FALSE, TRUE,
6875 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6879 Xpush_emerald_e, FALSE, FALSE,
6880 EL_EMERALD, -1, MV_BIT_RIGHT
6883 Xpush_emerald_w, FALSE, FALSE,
6884 EL_EMERALD, -1, MV_BIT_LEFT
6887 Xpush_diamond_e, FALSE, FALSE,
6888 EL_DIAMOND, -1, MV_BIT_RIGHT
6891 Xpush_diamond_w, FALSE, FALSE,
6892 EL_DIAMOND, -1, MV_BIT_LEFT
6895 Xpush_stone_e, FALSE, FALSE,
6896 EL_ROCK, -1, MV_BIT_RIGHT
6899 Xpush_stone_w, FALSE, FALSE,
6900 EL_ROCK, -1, MV_BIT_LEFT
6903 Xpush_bomb_e, FALSE, FALSE,
6904 EL_BOMB, -1, MV_BIT_RIGHT
6907 Xpush_bomb_w, FALSE, FALSE,
6908 EL_BOMB, -1, MV_BIT_LEFT
6911 Xpush_nut_e, FALSE, FALSE,
6912 EL_NUT, -1, MV_BIT_RIGHT
6915 Xpush_nut_w, FALSE, FALSE,
6916 EL_NUT, -1, MV_BIT_LEFT
6919 Xpush_spring_e, FALSE, FALSE,
6920 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6923 Xpush_spring_w, FALSE, FALSE,
6924 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6928 Xdynamite, TRUE, FALSE,
6929 EL_EM_DYNAMITE, -1, -1
6932 Ydynamite_blank, FALSE, FALSE,
6933 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6936 Xdynamite_1, TRUE, FALSE,
6937 EL_EM_DYNAMITE_ACTIVE, -1, -1
6940 Xdynamite_2, FALSE, FALSE,
6941 EL_EM_DYNAMITE_ACTIVE, -1, -1
6944 Xdynamite_3, FALSE, FALSE,
6945 EL_EM_DYNAMITE_ACTIVE, -1, -1
6948 Xdynamite_4, FALSE, FALSE,
6949 EL_EM_DYNAMITE_ACTIVE, -1, -1
6953 Xkey_1, TRUE, FALSE,
6957 Xkey_2, TRUE, FALSE,
6961 Xkey_3, TRUE, FALSE,
6965 Xkey_4, TRUE, FALSE,
6969 Xkey_5, TRUE, FALSE,
6970 EL_EMC_KEY_5, -1, -1
6973 Xkey_6, TRUE, FALSE,
6974 EL_EMC_KEY_6, -1, -1
6977 Xkey_7, TRUE, FALSE,
6978 EL_EMC_KEY_7, -1, -1
6981 Xkey_8, TRUE, FALSE,
6982 EL_EMC_KEY_8, -1, -1
6986 Xdoor_1, TRUE, FALSE,
6987 EL_EM_GATE_1, -1, -1
6990 Xdoor_2, TRUE, FALSE,
6991 EL_EM_GATE_2, -1, -1
6994 Xdoor_3, TRUE, FALSE,
6995 EL_EM_GATE_3, -1, -1
6998 Xdoor_4, TRUE, FALSE,
6999 EL_EM_GATE_4, -1, -1
7002 Xdoor_5, TRUE, FALSE,
7003 EL_EMC_GATE_5, -1, -1
7006 Xdoor_6, TRUE, FALSE,
7007 EL_EMC_GATE_6, -1, -1
7010 Xdoor_7, TRUE, FALSE,
7011 EL_EMC_GATE_7, -1, -1
7014 Xdoor_8, TRUE, FALSE,
7015 EL_EMC_GATE_8, -1, -1
7019 Xfake_door_1, TRUE, FALSE,
7020 EL_EM_GATE_1_GRAY, -1, -1
7023 Xfake_door_2, TRUE, FALSE,
7024 EL_EM_GATE_2_GRAY, -1, -1
7027 Xfake_door_3, TRUE, FALSE,
7028 EL_EM_GATE_3_GRAY, -1, -1
7031 Xfake_door_4, TRUE, FALSE,
7032 EL_EM_GATE_4_GRAY, -1, -1
7035 Xfake_door_5, TRUE, FALSE,
7036 EL_EMC_GATE_5_GRAY, -1, -1
7039 Xfake_door_6, TRUE, FALSE,
7040 EL_EMC_GATE_6_GRAY, -1, -1
7043 Xfake_door_7, TRUE, FALSE,
7044 EL_EMC_GATE_7_GRAY, -1, -1
7047 Xfake_door_8, TRUE, FALSE,
7048 EL_EMC_GATE_8_GRAY, -1, -1
7052 Xballoon, TRUE, FALSE,
7056 Yballoon_n, FALSE, FALSE,
7057 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7060 Yballoon_nB, FALSE, TRUE,
7061 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7064 Yballoon_e, FALSE, FALSE,
7065 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7068 Yballoon_eB, FALSE, TRUE,
7069 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7072 Yballoon_s, FALSE, FALSE,
7073 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7076 Yballoon_sB, FALSE, TRUE,
7077 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7080 Yballoon_w, FALSE, FALSE,
7081 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7084 Yballoon_wB, FALSE, TRUE,
7085 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7089 Xball_1, TRUE, FALSE,
7090 EL_EMC_MAGIC_BALL, -1, -1
7093 Yball_1, FALSE, FALSE,
7094 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7097 Xball_2, FALSE, FALSE,
7098 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7101 Yball_2, FALSE, FALSE,
7102 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7105 Yball_blank, FALSE, FALSE,
7106 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7110 Xamoeba_1, TRUE, FALSE,
7111 EL_AMOEBA_DRY, ACTION_OTHER, -1
7114 Xamoeba_2, FALSE, FALSE,
7115 EL_AMOEBA_DRY, ACTION_OTHER, -1
7118 Xamoeba_3, FALSE, FALSE,
7119 EL_AMOEBA_DRY, ACTION_OTHER, -1
7122 Xamoeba_4, FALSE, FALSE,
7123 EL_AMOEBA_DRY, ACTION_OTHER, -1
7126 Xamoeba_5, TRUE, FALSE,
7127 EL_AMOEBA_WET, ACTION_OTHER, -1
7130 Xamoeba_6, FALSE, FALSE,
7131 EL_AMOEBA_WET, ACTION_OTHER, -1
7134 Xamoeba_7, FALSE, FALSE,
7135 EL_AMOEBA_WET, ACTION_OTHER, -1
7138 Xamoeba_8, FALSE, FALSE,
7139 EL_AMOEBA_WET, ACTION_OTHER, -1
7144 EL_AMOEBA_DROP, ACTION_GROWING, -1
7147 Xdrip_fall, FALSE, FALSE,
7148 EL_AMOEBA_DROP, -1, -1
7151 Xdrip_stretch, FALSE, FALSE,
7152 EL_AMOEBA_DROP, ACTION_FALLING, -1
7155 Xdrip_stretchB, FALSE, TRUE,
7156 EL_AMOEBA_DROP, ACTION_FALLING, -1
7159 Ydrip_1_s, FALSE, FALSE,
7160 EL_AMOEBA_DROP, ACTION_FALLING, -1
7163 Ydrip_1_sB, FALSE, TRUE,
7164 EL_AMOEBA_DROP, ACTION_FALLING, -1
7167 Ydrip_2_s, FALSE, FALSE,
7168 EL_AMOEBA_DROP, ACTION_FALLING, -1
7171 Ydrip_2_sB, FALSE, TRUE,
7172 EL_AMOEBA_DROP, ACTION_FALLING, -1
7176 Xwonderwall, TRUE, FALSE,
7177 EL_MAGIC_WALL, -1, -1
7180 Ywonderwall, FALSE, FALSE,
7181 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7185 Xwheel, TRUE, FALSE,
7186 EL_ROBOT_WHEEL, -1, -1
7189 Ywheel, FALSE, FALSE,
7190 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7194 Xswitch, TRUE, FALSE,
7195 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7198 Yswitch, FALSE, FALSE,
7199 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7203 Xbumper, TRUE, FALSE,
7204 EL_EMC_SPRING_BUMPER, -1, -1
7207 Ybumper, FALSE, FALSE,
7208 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7212 Xacid_nw, TRUE, FALSE,
7213 EL_ACID_POOL_TOPLEFT, -1, -1
7216 Xacid_ne, TRUE, FALSE,
7217 EL_ACID_POOL_TOPRIGHT, -1, -1
7220 Xacid_sw, TRUE, FALSE,
7221 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7224 Xacid_s, TRUE, FALSE,
7225 EL_ACID_POOL_BOTTOM, -1, -1
7228 Xacid_se, TRUE, FALSE,
7229 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7233 Xfake_blank, TRUE, FALSE,
7234 EL_INVISIBLE_WALL, -1, -1
7237 Yfake_blank, FALSE, FALSE,
7238 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7242 Xfake_grass, TRUE, FALSE,
7243 EL_EMC_FAKE_GRASS, -1, -1
7246 Yfake_grass, FALSE, FALSE,
7247 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7251 Xfake_amoeba, TRUE, FALSE,
7252 EL_EMC_DRIPPER, -1, -1
7255 Yfake_amoeba, FALSE, FALSE,
7256 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7260 Xlenses, TRUE, FALSE,
7261 EL_EMC_LENSES, -1, -1
7265 Xmagnify, TRUE, FALSE,
7266 EL_EMC_MAGNIFIER, -1, -1
7271 EL_QUICKSAND_EMPTY, -1, -1
7274 Xsand_stone, TRUE, FALSE,
7275 EL_QUICKSAND_FULL, -1, -1
7278 Xsand_stonein_1, FALSE, TRUE,
7279 EL_ROCK, ACTION_FILLING, -1
7282 Xsand_stonein_2, FALSE, TRUE,
7283 EL_ROCK, ACTION_FILLING, -1
7286 Xsand_stonein_3, FALSE, TRUE,
7287 EL_ROCK, ACTION_FILLING, -1
7290 Xsand_stonein_4, FALSE, TRUE,
7291 EL_ROCK, ACTION_FILLING, -1
7294 Xsand_sandstone_1, FALSE, FALSE,
7295 EL_QUICKSAND_FILLING, -1, -1
7298 Xsand_sandstone_2, FALSE, FALSE,
7299 EL_QUICKSAND_FILLING, -1, -1
7302 Xsand_sandstone_3, FALSE, FALSE,
7303 EL_QUICKSAND_FILLING, -1, -1
7306 Xsand_sandstone_4, FALSE, FALSE,
7307 EL_QUICKSAND_FILLING, -1, -1
7310 Xsand_stonesand_1, FALSE, FALSE,
7311 EL_QUICKSAND_EMPTYING, -1, -1
7314 Xsand_stonesand_2, FALSE, FALSE,
7315 EL_QUICKSAND_EMPTYING, -1, -1
7318 Xsand_stonesand_3, FALSE, FALSE,
7319 EL_QUICKSAND_EMPTYING, -1, -1
7322 Xsand_stonesand_4, FALSE, FALSE,
7323 EL_QUICKSAND_EMPTYING, -1, -1
7326 Xsand_stoneout_1, FALSE, FALSE,
7327 EL_ROCK, ACTION_EMPTYING, -1
7330 Xsand_stoneout_2, FALSE, FALSE,
7331 EL_ROCK, ACTION_EMPTYING, -1
7334 Xsand_stonesand_quickout_1, FALSE, FALSE,
7335 EL_QUICKSAND_EMPTYING, -1, -1
7338 Xsand_stonesand_quickout_2, FALSE, FALSE,
7339 EL_QUICKSAND_EMPTYING, -1, -1
7343 Xslide_ns, TRUE, FALSE,
7344 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7347 Yslide_ns_blank, FALSE, FALSE,
7348 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7351 Xslide_ew, TRUE, FALSE,
7352 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7355 Yslide_ew_blank, FALSE, FALSE,
7356 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7360 Xwind_n, TRUE, FALSE,
7361 EL_BALLOON_SWITCH_UP, -1, -1
7364 Xwind_e, TRUE, FALSE,
7365 EL_BALLOON_SWITCH_RIGHT, -1, -1
7368 Xwind_s, TRUE, FALSE,
7369 EL_BALLOON_SWITCH_DOWN, -1, -1
7372 Xwind_w, TRUE, FALSE,
7373 EL_BALLOON_SWITCH_LEFT, -1, -1
7376 Xwind_any, TRUE, FALSE,
7377 EL_BALLOON_SWITCH_ANY, -1, -1
7380 Xwind_stop, TRUE, FALSE,
7381 EL_BALLOON_SWITCH_NONE, -1, -1
7386 EL_EM_EXIT_CLOSED, -1, -1
7389 Xexit_1, TRUE, FALSE,
7390 EL_EM_EXIT_OPEN, -1, -1
7393 Xexit_2, FALSE, FALSE,
7394 EL_EM_EXIT_OPEN, -1, -1
7397 Xexit_3, FALSE, FALSE,
7398 EL_EM_EXIT_OPEN, -1, -1
7402 Xpause, FALSE, FALSE,
7407 Xwall_1, TRUE, FALSE,
7411 Xwall_2, TRUE, FALSE,
7412 EL_EMC_WALL_14, -1, -1
7415 Xwall_3, TRUE, FALSE,
7416 EL_EMC_WALL_15, -1, -1
7419 Xwall_4, TRUE, FALSE,
7420 EL_EMC_WALL_16, -1, -1
7424 Xroundwall_1, TRUE, FALSE,
7425 EL_WALL_SLIPPERY, -1, -1
7428 Xroundwall_2, TRUE, FALSE,
7429 EL_EMC_WALL_SLIPPERY_2, -1, -1
7432 Xroundwall_3, TRUE, FALSE,
7433 EL_EMC_WALL_SLIPPERY_3, -1, -1
7436 Xroundwall_4, TRUE, FALSE,
7437 EL_EMC_WALL_SLIPPERY_4, -1, -1
7441 Xsteel_1, TRUE, FALSE,
7442 EL_STEELWALL, -1, -1
7445 Xsteel_2, TRUE, FALSE,
7446 EL_EMC_STEELWALL_2, -1, -1
7449 Xsteel_3, TRUE, FALSE,
7450 EL_EMC_STEELWALL_3, -1, -1
7453 Xsteel_4, TRUE, FALSE,
7454 EL_EMC_STEELWALL_4, -1, -1
7458 Xdecor_1, TRUE, FALSE,
7459 EL_EMC_WALL_8, -1, -1
7462 Xdecor_2, TRUE, FALSE,
7463 EL_EMC_WALL_6, -1, -1
7466 Xdecor_3, TRUE, FALSE,
7467 EL_EMC_WALL_4, -1, -1
7470 Xdecor_4, TRUE, FALSE,
7471 EL_EMC_WALL_7, -1, -1
7474 Xdecor_5, TRUE, FALSE,
7475 EL_EMC_WALL_5, -1, -1
7478 Xdecor_6, TRUE, FALSE,
7479 EL_EMC_WALL_9, -1, -1
7482 Xdecor_7, TRUE, FALSE,
7483 EL_EMC_WALL_10, -1, -1
7486 Xdecor_8, TRUE, FALSE,
7487 EL_EMC_WALL_1, -1, -1
7490 Xdecor_9, TRUE, FALSE,
7491 EL_EMC_WALL_2, -1, -1
7494 Xdecor_10, TRUE, FALSE,
7495 EL_EMC_WALL_3, -1, -1
7498 Xdecor_11, TRUE, FALSE,
7499 EL_EMC_WALL_11, -1, -1
7502 Xdecor_12, TRUE, FALSE,
7503 EL_EMC_WALL_12, -1, -1
7507 Xalpha_0, TRUE, FALSE,
7508 EL_CHAR('0'), -1, -1
7511 Xalpha_1, TRUE, FALSE,
7512 EL_CHAR('1'), -1, -1
7515 Xalpha_2, TRUE, FALSE,
7516 EL_CHAR('2'), -1, -1
7519 Xalpha_3, TRUE, FALSE,
7520 EL_CHAR('3'), -1, -1
7523 Xalpha_4, TRUE, FALSE,
7524 EL_CHAR('4'), -1, -1
7527 Xalpha_5, TRUE, FALSE,
7528 EL_CHAR('5'), -1, -1
7531 Xalpha_6, TRUE, FALSE,
7532 EL_CHAR('6'), -1, -1
7535 Xalpha_7, TRUE, FALSE,
7536 EL_CHAR('7'), -1, -1
7539 Xalpha_8, TRUE, FALSE,
7540 EL_CHAR('8'), -1, -1
7543 Xalpha_9, TRUE, FALSE,
7544 EL_CHAR('9'), -1, -1
7547 Xalpha_excla, TRUE, FALSE,
7548 EL_CHAR('!'), -1, -1
7551 Xalpha_apost, TRUE, FALSE,
7552 EL_CHAR('\''), -1, -1
7555 Xalpha_comma, TRUE, FALSE,
7556 EL_CHAR(','), -1, -1
7559 Xalpha_minus, TRUE, FALSE,
7560 EL_CHAR('-'), -1, -1
7563 Xalpha_perio, TRUE, FALSE,
7564 EL_CHAR('.'), -1, -1
7567 Xalpha_colon, TRUE, FALSE,
7568 EL_CHAR(':'), -1, -1
7571 Xalpha_quest, TRUE, FALSE,
7572 EL_CHAR('?'), -1, -1
7575 Xalpha_a, TRUE, FALSE,
7576 EL_CHAR('A'), -1, -1
7579 Xalpha_b, TRUE, FALSE,
7580 EL_CHAR('B'), -1, -1
7583 Xalpha_c, TRUE, FALSE,
7584 EL_CHAR('C'), -1, -1
7587 Xalpha_d, TRUE, FALSE,
7588 EL_CHAR('D'), -1, -1
7591 Xalpha_e, TRUE, FALSE,
7592 EL_CHAR('E'), -1, -1
7595 Xalpha_f, TRUE, FALSE,
7596 EL_CHAR('F'), -1, -1
7599 Xalpha_g, TRUE, FALSE,
7600 EL_CHAR('G'), -1, -1
7603 Xalpha_h, TRUE, FALSE,
7604 EL_CHAR('H'), -1, -1
7607 Xalpha_i, TRUE, FALSE,
7608 EL_CHAR('I'), -1, -1
7611 Xalpha_j, TRUE, FALSE,
7612 EL_CHAR('J'), -1, -1
7615 Xalpha_k, TRUE, FALSE,
7616 EL_CHAR('K'), -1, -1
7619 Xalpha_l, TRUE, FALSE,
7620 EL_CHAR('L'), -1, -1
7623 Xalpha_m, TRUE, FALSE,
7624 EL_CHAR('M'), -1, -1
7627 Xalpha_n, TRUE, FALSE,
7628 EL_CHAR('N'), -1, -1
7631 Xalpha_o, TRUE, FALSE,
7632 EL_CHAR('O'), -1, -1
7635 Xalpha_p, TRUE, FALSE,
7636 EL_CHAR('P'), -1, -1
7639 Xalpha_q, TRUE, FALSE,
7640 EL_CHAR('Q'), -1, -1
7643 Xalpha_r, TRUE, FALSE,
7644 EL_CHAR('R'), -1, -1
7647 Xalpha_s, TRUE, FALSE,
7648 EL_CHAR('S'), -1, -1
7651 Xalpha_t, TRUE, FALSE,
7652 EL_CHAR('T'), -1, -1
7655 Xalpha_u, TRUE, FALSE,
7656 EL_CHAR('U'), -1, -1
7659 Xalpha_v, TRUE, FALSE,
7660 EL_CHAR('V'), -1, -1
7663 Xalpha_w, TRUE, FALSE,
7664 EL_CHAR('W'), -1, -1
7667 Xalpha_x, TRUE, FALSE,
7668 EL_CHAR('X'), -1, -1
7671 Xalpha_y, TRUE, FALSE,
7672 EL_CHAR('Y'), -1, -1
7675 Xalpha_z, TRUE, FALSE,
7676 EL_CHAR('Z'), -1, -1
7679 Xalpha_arrow_e, TRUE, FALSE,
7680 EL_CHAR('>'), -1, -1
7683 Xalpha_arrow_w, TRUE, FALSE,
7684 EL_CHAR('<'), -1, -1
7687 Xalpha_copyr, TRUE, FALSE,
7688 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7692 Ykey_1_blank, FALSE, FALSE,
7693 EL_EM_KEY_1, ACTION_COLLECTING, -1
7696 Ykey_2_blank, FALSE, FALSE,
7697 EL_EM_KEY_2, ACTION_COLLECTING, -1
7700 Ykey_3_blank, FALSE, FALSE,
7701 EL_EM_KEY_3, ACTION_COLLECTING, -1
7704 Ykey_4_blank, FALSE, FALSE,
7705 EL_EM_KEY_4, ACTION_COLLECTING, -1
7708 Ykey_5_blank, FALSE, FALSE,
7709 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7712 Ykey_6_blank, FALSE, FALSE,
7713 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7716 Ykey_7_blank, FALSE, FALSE,
7717 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7720 Ykey_8_blank, FALSE, FALSE,
7721 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7724 Ylenses_blank, FALSE, FALSE,
7725 EL_EMC_LENSES, ACTION_COLLECTING, -1
7728 Ymagnify_blank, FALSE, FALSE,
7729 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7732 Ygrass_blank, FALSE, FALSE,
7733 EL_EMC_GRASS, ACTION_SNAPPING, -1
7736 Ydirt_blank, FALSE, FALSE,
7737 EL_SAND, ACTION_SNAPPING, -1
7746 static struct Mapping_EM_to_RND_player
7755 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7759 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7763 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7767 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7771 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7775 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7779 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7783 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7787 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7791 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7795 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7799 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7803 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7807 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7811 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7815 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7819 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7823 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7827 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7831 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7835 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7839 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7843 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7847 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7851 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7855 EL_PLAYER_1, ACTION_DEFAULT, -1,
7859 EL_PLAYER_2, ACTION_DEFAULT, -1,
7863 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7867 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7871 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7875 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7879 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7883 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7887 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7891 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7895 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7899 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7903 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7907 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7911 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7915 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7919 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7923 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7927 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7931 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7935 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7939 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7943 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7947 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7951 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7955 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7959 EL_PLAYER_3, ACTION_DEFAULT, -1,
7963 EL_PLAYER_4, ACTION_DEFAULT, -1,
7972 int map_element_RND_to_EM_cave(int element_rnd)
7974 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7975 static boolean mapping_initialized = FALSE;
7977 if (!mapping_initialized)
7981 // return "Xalpha_quest" for all undefined elements in mapping array
7982 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7983 mapping_RND_to_EM[i] = Xalpha_quest;
7985 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7986 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7987 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7988 em_object_mapping_list[i].element_em;
7990 mapping_initialized = TRUE;
7993 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7995 Warn("invalid RND level element %d", element_rnd);
8000 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
8003 int map_element_EM_to_RND_cave(int element_em_cave)
8005 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8006 static boolean mapping_initialized = FALSE;
8008 if (!mapping_initialized)
8012 // return "EL_UNKNOWN" for all undefined elements in mapping array
8013 for (i = 0; i < GAME_TILE_MAX; i++)
8014 mapping_EM_to_RND[i] = EL_UNKNOWN;
8016 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8017 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8018 em_object_mapping_list[i].element_rnd;
8020 mapping_initialized = TRUE;
8023 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8025 Warn("invalid EM cave element %d", element_em_cave);
8030 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8033 int map_element_EM_to_RND_game(int element_em_game)
8035 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8036 static boolean mapping_initialized = FALSE;
8038 if (!mapping_initialized)
8042 // return "EL_UNKNOWN" for all undefined elements in mapping array
8043 for (i = 0; i < GAME_TILE_MAX; i++)
8044 mapping_EM_to_RND[i] = EL_UNKNOWN;
8046 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8047 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8048 em_object_mapping_list[i].element_rnd;
8050 mapping_initialized = TRUE;
8053 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8055 Warn("invalid EM game element %d", element_em_game);
8060 return mapping_EM_to_RND[element_em_game];
8063 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8065 struct LevelInfo_EM *level_em = level->native_em_level;
8066 struct CAVE *cav = level_em->cav;
8069 for (i = 0; i < GAME_TILE_MAX; i++)
8070 cav->android_array[i] = Cblank;
8072 for (i = 0; i < level->num_android_clone_elements; i++)
8074 int element_rnd = level->android_clone_element[i];
8075 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8077 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8078 if (em_object_mapping_list[j].element_rnd == element_rnd)
8079 cav->android_array[em_object_mapping_list[j].element_em] =
8084 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8086 struct LevelInfo_EM *level_em = level->native_em_level;
8087 struct CAVE *cav = level_em->cav;
8090 level->num_android_clone_elements = 0;
8092 for (i = 0; i < GAME_TILE_MAX; i++)
8094 int element_em_cave = cav->android_array[i];
8096 boolean element_found = FALSE;
8098 if (element_em_cave == Cblank)
8101 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8103 for (j = 0; j < level->num_android_clone_elements; j++)
8104 if (level->android_clone_element[j] == element_rnd)
8105 element_found = TRUE;
8109 level->android_clone_element[level->num_android_clone_elements++] =
8112 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8117 if (level->num_android_clone_elements == 0)
8119 level->num_android_clone_elements = 1;
8120 level->android_clone_element[0] = EL_EMPTY;
8124 int map_direction_RND_to_EM(int direction)
8126 return (direction == MV_UP ? 0 :
8127 direction == MV_RIGHT ? 1 :
8128 direction == MV_DOWN ? 2 :
8129 direction == MV_LEFT ? 3 :
8133 int map_direction_EM_to_RND(int direction)
8135 return (direction == 0 ? MV_UP :
8136 direction == 1 ? MV_RIGHT :
8137 direction == 2 ? MV_DOWN :
8138 direction == 3 ? MV_LEFT :
8142 int map_element_RND_to_SP(int element_rnd)
8144 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8146 if (element_rnd >= EL_SP_START &&
8147 element_rnd <= EL_SP_END)
8148 element_sp = element_rnd - EL_SP_START;
8149 else if (element_rnd == EL_EMPTY_SPACE)
8151 else if (element_rnd == EL_INVISIBLE_WALL)
8157 int map_element_SP_to_RND(int element_sp)
8159 int element_rnd = EL_UNKNOWN;
8161 if (element_sp >= 0x00 &&
8163 element_rnd = EL_SP_START + element_sp;
8164 else if (element_sp == 0x28)
8165 element_rnd = EL_INVISIBLE_WALL;
8170 int map_action_SP_to_RND(int action_sp)
8174 case actActive: return ACTION_ACTIVE;
8175 case actImpact: return ACTION_IMPACT;
8176 case actExploding: return ACTION_EXPLODING;
8177 case actDigging: return ACTION_DIGGING;
8178 case actSnapping: return ACTION_SNAPPING;
8179 case actCollecting: return ACTION_COLLECTING;
8180 case actPassing: return ACTION_PASSING;
8181 case actPushing: return ACTION_PUSHING;
8182 case actDropping: return ACTION_DROPPING;
8184 default: return ACTION_DEFAULT;
8188 int map_element_RND_to_MM(int element_rnd)
8190 return (element_rnd >= EL_MM_START_1 &&
8191 element_rnd <= EL_MM_END_1 ?
8192 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8194 element_rnd >= EL_MM_START_2 &&
8195 element_rnd <= EL_MM_END_2 ?
8196 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8198 element_rnd >= EL_CHAR_START &&
8199 element_rnd <= EL_CHAR_END ?
8200 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8202 element_rnd >= EL_MM_RUNTIME_START &&
8203 element_rnd <= EL_MM_RUNTIME_END ?
8204 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8206 element_rnd >= EL_MM_DUMMY_START &&
8207 element_rnd <= EL_MM_DUMMY_END ?
8208 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
8210 EL_MM_EMPTY_NATIVE);
8213 int map_element_MM_to_RND(int element_mm)
8215 return (element_mm == EL_MM_EMPTY_NATIVE ||
8216 element_mm == EL_DF_EMPTY_NATIVE ?
8219 element_mm >= EL_MM_START_1_NATIVE &&
8220 element_mm <= EL_MM_END_1_NATIVE ?
8221 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8223 element_mm >= EL_MM_START_2_NATIVE &&
8224 element_mm <= EL_MM_END_2_NATIVE ?
8225 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8227 element_mm >= EL_MM_CHAR_START_NATIVE &&
8228 element_mm <= EL_MM_CHAR_END_NATIVE ?
8229 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8231 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8232 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8233 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8235 element_mm >= EL_MM_DUMMY_START_NATIVE &&
8236 element_mm <= EL_MM_DUMMY_END_NATIVE ?
8237 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
8242 int map_action_MM_to_RND(int action_mm)
8244 // all MM actions are defined to exactly match their RND counterparts
8248 int map_sound_MM_to_RND(int sound_mm)
8252 case SND_MM_GAME_LEVELTIME_CHARGING:
8253 return SND_GAME_LEVELTIME_CHARGING;
8255 case SND_MM_GAME_HEALTH_CHARGING:
8256 return SND_GAME_HEALTH_CHARGING;
8259 return SND_UNDEFINED;
8263 int map_mm_wall_element(int element)
8265 return (element >= EL_MM_STEEL_WALL_START &&
8266 element <= EL_MM_STEEL_WALL_END ?
8269 element >= EL_MM_WOODEN_WALL_START &&
8270 element <= EL_MM_WOODEN_WALL_END ?
8273 element >= EL_MM_ICE_WALL_START &&
8274 element <= EL_MM_ICE_WALL_END ?
8277 element >= EL_MM_AMOEBA_WALL_START &&
8278 element <= EL_MM_AMOEBA_WALL_END ?
8281 element >= EL_DF_STEEL_WALL_START &&
8282 element <= EL_DF_STEEL_WALL_END ?
8285 element >= EL_DF_WOODEN_WALL_START &&
8286 element <= EL_DF_WOODEN_WALL_END ?
8292 int map_mm_wall_element_editor(int element)
8296 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8297 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8298 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8299 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8300 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8301 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8303 default: return element;
8307 int get_next_element(int element)
8311 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8312 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8313 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8314 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8315 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8316 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8317 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8318 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8319 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8320 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8321 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8323 default: return element;
8327 int el2img_mm(int element_mm)
8329 return el2img(map_element_MM_to_RND(element_mm));
8332 int el_act_dir2img(int element, int action, int direction)
8334 element = GFX_ELEMENT(element);
8335 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8337 // direction_graphic[][] == graphic[] for undefined direction graphics
8338 return element_info[element].direction_graphic[action][direction];
8341 static int el_act_dir2crm(int element, int action, int direction)
8343 element = GFX_ELEMENT(element);
8344 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8346 // direction_graphic[][] == graphic[] for undefined direction graphics
8347 return element_info[element].direction_crumbled[action][direction];
8350 int el_act2img(int element, int action)
8352 element = GFX_ELEMENT(element);
8354 return element_info[element].graphic[action];
8357 int el_act2crm(int element, int action)
8359 element = GFX_ELEMENT(element);
8361 return element_info[element].crumbled[action];
8364 int el_dir2img(int element, int direction)
8366 element = GFX_ELEMENT(element);
8368 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8371 int el2baseimg(int element)
8373 return element_info[element].graphic[ACTION_DEFAULT];
8376 int el2img(int element)
8378 element = GFX_ELEMENT(element);
8380 return element_info[element].graphic[ACTION_DEFAULT];
8383 int el2edimg(int element)
8385 element = GFX_ELEMENT(element);
8387 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8390 int el2preimg(int element)
8392 element = GFX_ELEMENT(element);
8394 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8397 int el2panelimg(int element)
8399 element = GFX_ELEMENT(element);
8401 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8404 int font2baseimg(int font_nr)
8406 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8409 int getBeltNrFromBeltElement(int element)
8411 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8412 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8413 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8416 int getBeltNrFromBeltActiveElement(int element)
8418 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8419 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8420 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8423 int getBeltNrFromBeltSwitchElement(int element)
8425 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8426 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8427 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8430 int getBeltDirNrFromBeltElement(int element)
8432 static int belt_base_element[4] =
8434 EL_CONVEYOR_BELT_1_LEFT,
8435 EL_CONVEYOR_BELT_2_LEFT,
8436 EL_CONVEYOR_BELT_3_LEFT,
8437 EL_CONVEYOR_BELT_4_LEFT
8440 int belt_nr = getBeltNrFromBeltElement(element);
8441 int belt_dir_nr = element - belt_base_element[belt_nr];
8443 return (belt_dir_nr % 3);
8446 int getBeltDirNrFromBeltSwitchElement(int element)
8448 static int belt_base_element[4] =
8450 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8451 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8452 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8453 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8456 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8457 int belt_dir_nr = element - belt_base_element[belt_nr];
8459 return (belt_dir_nr % 3);
8462 int getBeltDirFromBeltElement(int element)
8464 static int belt_move_dir[3] =
8471 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8473 return belt_move_dir[belt_dir_nr];
8476 int getBeltDirFromBeltSwitchElement(int element)
8478 static int belt_move_dir[3] =
8485 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8487 return belt_move_dir[belt_dir_nr];
8490 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8492 static int belt_base_element[4] =
8494 EL_CONVEYOR_BELT_1_LEFT,
8495 EL_CONVEYOR_BELT_2_LEFT,
8496 EL_CONVEYOR_BELT_3_LEFT,
8497 EL_CONVEYOR_BELT_4_LEFT
8500 return belt_base_element[belt_nr] + belt_dir_nr;
8503 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8505 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8507 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8510 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8512 static int belt_base_element[4] =
8514 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8515 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8516 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8517 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8520 return belt_base_element[belt_nr] + belt_dir_nr;
8523 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8525 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8527 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8530 boolean swapTiles_EM(boolean is_pre_emc_cave)
8532 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8535 boolean getTeamMode_EM(void)
8537 return game.team_mode || network_playing;
8540 boolean isActivePlayer_EM(int player_nr)
8542 return stored_player[player_nr].active;
8545 unsigned int InitRND(int seed)
8547 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8548 return InitEngineRandom_EM(seed);
8549 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8550 return InitEngineRandom_SP(seed);
8551 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8552 return InitEngineRandom_MM(seed);
8554 return InitEngineRandom_RND(seed);
8557 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8558 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8560 static int get_effective_element_EM(int tile, int frame_em)
8562 int element = object_mapping[tile].element_rnd;
8563 int action = object_mapping[tile].action;
8564 boolean is_backside = object_mapping[tile].is_backside;
8565 boolean action_removing = (action == ACTION_DIGGING ||
8566 action == ACTION_SNAPPING ||
8567 action == ACTION_COLLECTING);
8575 return (frame_em > 5 ? EL_EMPTY : element);
8581 else // frame_em == 7
8592 case Ydiamond_stone:
8596 case Xdrip_stretchB:
8612 case Ymagnify_blank:
8615 case Xsand_stonein_1:
8616 case Xsand_stonein_2:
8617 case Xsand_stonein_3:
8618 case Xsand_stonein_4:
8622 return (is_backside || action_removing ? EL_EMPTY : element);
8627 static boolean check_linear_animation_EM(int tile)
8631 case Xsand_stonesand_1:
8632 case Xsand_stonesand_quickout_1:
8633 case Xsand_sandstone_1:
8634 case Xsand_stonein_1:
8635 case Xsand_stoneout_1:
8663 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8664 boolean has_crumbled_graphics,
8665 int crumbled, int sync_frame)
8667 // if element can be crumbled, but certain action graphics are just empty
8668 // space (like instantly snapping sand to empty space in 1 frame), do not
8669 // treat these empty space graphics as crumbled graphics in EMC engine
8670 if (crumbled == IMG_EMPTY_SPACE)
8671 has_crumbled_graphics = FALSE;
8673 if (has_crumbled_graphics)
8675 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8676 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8677 g_crumbled->anim_delay,
8678 g_crumbled->anim_mode,
8679 g_crumbled->anim_start_frame,
8682 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8683 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8685 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8686 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8688 g_em->has_crumbled_graphics = TRUE;
8692 g_em->crumbled_bitmap = NULL;
8693 g_em->crumbled_src_x = 0;
8694 g_em->crumbled_src_y = 0;
8695 g_em->crumbled_border_size = 0;
8696 g_em->crumbled_tile_size = 0;
8698 g_em->has_crumbled_graphics = FALSE;
8703 void ResetGfxAnimation_EM(int x, int y, int tile)
8709 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8710 int tile, int frame_em, int x, int y)
8712 int action = object_mapping[tile].action;
8713 int direction = object_mapping[tile].direction;
8714 int effective_element = get_effective_element_EM(tile, frame_em);
8715 int graphic = (direction == MV_NONE ?
8716 el_act2img(effective_element, action) :
8717 el_act_dir2img(effective_element, action, direction));
8718 struct GraphicInfo *g = &graphic_info[graphic];
8720 boolean action_removing = (action == ACTION_DIGGING ||
8721 action == ACTION_SNAPPING ||
8722 action == ACTION_COLLECTING);
8723 boolean action_moving = (action == ACTION_FALLING ||
8724 action == ACTION_MOVING ||
8725 action == ACTION_PUSHING ||
8726 action == ACTION_EATING ||
8727 action == ACTION_FILLING ||
8728 action == ACTION_EMPTYING);
8729 boolean action_falling = (action == ACTION_FALLING ||
8730 action == ACTION_FILLING ||
8731 action == ACTION_EMPTYING);
8733 // special case: graphic uses "2nd movement tile" and has defined
8734 // 7 frames for movement animation (or less) => use default graphic
8735 // for last (8th) frame which ends the movement animation
8736 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8738 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8739 graphic = (direction == MV_NONE ?
8740 el_act2img(effective_element, action) :
8741 el_act_dir2img(effective_element, action, direction));
8743 g = &graphic_info[graphic];
8746 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8750 else if (action_moving)
8752 boolean is_backside = object_mapping[tile].is_backside;
8756 int direction = object_mapping[tile].direction;
8757 int move_dir = (action_falling ? MV_DOWN : direction);
8762 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8763 if (g->double_movement && frame_em == 0)
8767 if (move_dir == MV_LEFT)
8768 GfxFrame[x - 1][y] = GfxFrame[x][y];
8769 else if (move_dir == MV_RIGHT)
8770 GfxFrame[x + 1][y] = GfxFrame[x][y];
8771 else if (move_dir == MV_UP)
8772 GfxFrame[x][y - 1] = GfxFrame[x][y];
8773 else if (move_dir == MV_DOWN)
8774 GfxFrame[x][y + 1] = GfxFrame[x][y];
8781 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8782 if (tile == Xsand_stonesand_quickout_1 ||
8783 tile == Xsand_stonesand_quickout_2)
8787 if (graphic_info[graphic].anim_global_sync)
8788 sync_frame = FrameCounter;
8789 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8790 sync_frame = GfxFrame[x][y];
8792 sync_frame = 0; // playfield border (pseudo steel)
8794 SetRandomAnimationValue(x, y);
8796 int frame = getAnimationFrame(g->anim_frames,
8799 g->anim_start_frame,
8802 g_em->unique_identifier =
8803 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8806 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8807 int tile, int frame_em, int x, int y)
8809 int action = object_mapping[tile].action;
8810 int direction = object_mapping[tile].direction;
8811 boolean is_backside = object_mapping[tile].is_backside;
8812 int effective_element = get_effective_element_EM(tile, frame_em);
8813 int effective_action = action;
8814 int graphic = (direction == MV_NONE ?
8815 el_act2img(effective_element, effective_action) :
8816 el_act_dir2img(effective_element, effective_action,
8818 int crumbled = (direction == MV_NONE ?
8819 el_act2crm(effective_element, effective_action) :
8820 el_act_dir2crm(effective_element, effective_action,
8822 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8823 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8824 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8825 struct GraphicInfo *g = &graphic_info[graphic];
8828 // special case: graphic uses "2nd movement tile" and has defined
8829 // 7 frames for movement animation (or less) => use default graphic
8830 // for last (8th) frame which ends the movement animation
8831 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8833 effective_action = ACTION_DEFAULT;
8834 graphic = (direction == MV_NONE ?
8835 el_act2img(effective_element, effective_action) :
8836 el_act_dir2img(effective_element, effective_action,
8838 crumbled = (direction == MV_NONE ?
8839 el_act2crm(effective_element, effective_action) :
8840 el_act_dir2crm(effective_element, effective_action,
8843 g = &graphic_info[graphic];
8846 if (graphic_info[graphic].anim_global_sync)
8847 sync_frame = FrameCounter;
8848 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8849 sync_frame = GfxFrame[x][y];
8851 sync_frame = 0; // playfield border (pseudo steel)
8853 SetRandomAnimationValue(x, y);
8855 int frame = getAnimationFrame(g->anim_frames,
8858 g->anim_start_frame,
8861 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8862 g->double_movement && is_backside);
8864 // (updating the "crumbled" graphic definitions is probably not really needed,
8865 // as animations for crumbled graphics can't be longer than one EMC cycle)
8866 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8870 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8871 int player_nr, int anim, int frame_em)
8873 int element = player_mapping[player_nr][anim].element_rnd;
8874 int action = player_mapping[player_nr][anim].action;
8875 int direction = player_mapping[player_nr][anim].direction;
8876 int graphic = (direction == MV_NONE ?
8877 el_act2img(element, action) :
8878 el_act_dir2img(element, action, direction));
8879 struct GraphicInfo *g = &graphic_info[graphic];
8882 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8884 stored_player[player_nr].StepFrame = frame_em;
8886 sync_frame = stored_player[player_nr].Frame;
8888 int frame = getAnimationFrame(g->anim_frames,
8891 g->anim_start_frame,
8894 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8895 &g_em->src_x, &g_em->src_y, FALSE);
8898 void InitGraphicInfo_EM(void)
8902 // always start with reliable default values
8903 for (i = 0; i < GAME_TILE_MAX; i++)
8905 object_mapping[i].element_rnd = EL_UNKNOWN;
8906 object_mapping[i].is_backside = FALSE;
8907 object_mapping[i].action = ACTION_DEFAULT;
8908 object_mapping[i].direction = MV_NONE;
8911 // always start with reliable default values
8912 for (p = 0; p < MAX_PLAYERS; p++)
8914 for (i = 0; i < PLY_MAX; i++)
8916 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8917 player_mapping[p][i].action = ACTION_DEFAULT;
8918 player_mapping[p][i].direction = MV_NONE;
8922 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8924 int e = em_object_mapping_list[i].element_em;
8926 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8927 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8929 if (em_object_mapping_list[i].action != -1)
8930 object_mapping[e].action = em_object_mapping_list[i].action;
8932 if (em_object_mapping_list[i].direction != -1)
8933 object_mapping[e].direction =
8934 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8937 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8939 int a = em_player_mapping_list[i].action_em;
8940 int p = em_player_mapping_list[i].player_nr;
8942 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8944 if (em_player_mapping_list[i].action != -1)
8945 player_mapping[p][a].action = em_player_mapping_list[i].action;
8947 if (em_player_mapping_list[i].direction != -1)
8948 player_mapping[p][a].direction =
8949 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8952 for (i = 0; i < GAME_TILE_MAX; i++)
8954 int element = object_mapping[i].element_rnd;
8955 int action = object_mapping[i].action;
8956 int direction = object_mapping[i].direction;
8957 boolean is_backside = object_mapping[i].is_backside;
8958 boolean action_exploding = ((action == ACTION_EXPLODING ||
8959 action == ACTION_SMASHED_BY_ROCK ||
8960 action == ACTION_SMASHED_BY_SPRING) &&
8961 element != EL_DIAMOND);
8962 boolean action_active = (action == ACTION_ACTIVE);
8963 boolean action_other = (action == ACTION_OTHER);
8965 for (j = 0; j < 8; j++)
8967 int effective_element = get_effective_element_EM(i, j);
8968 int effective_action = (j < 7 ? action :
8969 i == Xdrip_stretch ? action :
8970 i == Xdrip_stretchB ? action :
8971 i == Ydrip_1_s ? action :
8972 i == Ydrip_1_sB ? action :
8973 i == Yball_1 ? action :
8974 i == Xball_2 ? action :
8975 i == Yball_2 ? action :
8976 i == Yball_blank ? action :
8977 i == Ykey_1_blank ? action :
8978 i == Ykey_2_blank ? action :
8979 i == Ykey_3_blank ? action :
8980 i == Ykey_4_blank ? action :
8981 i == Ykey_5_blank ? action :
8982 i == Ykey_6_blank ? action :
8983 i == Ykey_7_blank ? action :
8984 i == Ykey_8_blank ? action :
8985 i == Ylenses_blank ? action :
8986 i == Ymagnify_blank ? action :
8987 i == Ygrass_blank ? action :
8988 i == Ydirt_blank ? action :
8989 i == Xsand_stonein_1 ? action :
8990 i == Xsand_stonein_2 ? action :
8991 i == Xsand_stonein_3 ? action :
8992 i == Xsand_stonein_4 ? action :
8993 i == Xsand_stoneout_1 ? action :
8994 i == Xsand_stoneout_2 ? action :
8995 i == Xboom_android ? ACTION_EXPLODING :
8996 action_exploding ? ACTION_EXPLODING :
8997 action_active ? action :
8998 action_other ? action :
9000 int graphic = (el_act_dir2img(effective_element, effective_action,
9002 int crumbled = (el_act_dir2crm(effective_element, effective_action,
9004 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
9005 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
9006 boolean has_action_graphics = (graphic != base_graphic);
9007 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9008 struct GraphicInfo *g = &graphic_info[graphic];
9009 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9012 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9013 boolean special_animation = (action != ACTION_DEFAULT &&
9014 g->anim_frames == 3 &&
9015 g->anim_delay == 2 &&
9016 g->anim_mode & ANIM_LINEAR);
9017 int sync_frame = (i == Xdrip_stretch ? 7 :
9018 i == Xdrip_stretchB ? 7 :
9019 i == Ydrip_2_s ? j + 8 :
9020 i == Ydrip_2_sB ? j + 8 :
9029 i == Xfake_acid_1 ? 0 :
9030 i == Xfake_acid_2 ? 10 :
9031 i == Xfake_acid_3 ? 20 :
9032 i == Xfake_acid_4 ? 30 :
9033 i == Xfake_acid_5 ? 40 :
9034 i == Xfake_acid_6 ? 50 :
9035 i == Xfake_acid_7 ? 60 :
9036 i == Xfake_acid_8 ? 70 :
9037 i == Xfake_acid_1_player ? 0 :
9038 i == Xfake_acid_2_player ? 10 :
9039 i == Xfake_acid_3_player ? 20 :
9040 i == Xfake_acid_4_player ? 30 :
9041 i == Xfake_acid_5_player ? 40 :
9042 i == Xfake_acid_6_player ? 50 :
9043 i == Xfake_acid_7_player ? 60 :
9044 i == Xfake_acid_8_player ? 70 :
9046 i == Yball_2 ? j + 8 :
9047 i == Yball_blank ? j + 1 :
9048 i == Ykey_1_blank ? j + 1 :
9049 i == Ykey_2_blank ? j + 1 :
9050 i == Ykey_3_blank ? j + 1 :
9051 i == Ykey_4_blank ? j + 1 :
9052 i == Ykey_5_blank ? j + 1 :
9053 i == Ykey_6_blank ? j + 1 :
9054 i == Ykey_7_blank ? j + 1 :
9055 i == Ykey_8_blank ? j + 1 :
9056 i == Ylenses_blank ? j + 1 :
9057 i == Ymagnify_blank ? j + 1 :
9058 i == Ygrass_blank ? j + 1 :
9059 i == Ydirt_blank ? j + 1 :
9060 i == Xamoeba_1 ? 0 :
9061 i == Xamoeba_2 ? 1 :
9062 i == Xamoeba_3 ? 2 :
9063 i == Xamoeba_4 ? 3 :
9064 i == Xamoeba_5 ? 0 :
9065 i == Xamoeba_6 ? 1 :
9066 i == Xamoeba_7 ? 2 :
9067 i == Xamoeba_8 ? 3 :
9068 i == Xexit_2 ? j + 8 :
9069 i == Xexit_3 ? j + 16 :
9070 i == Xdynamite_1 ? 0 :
9071 i == Xdynamite_2 ? 8 :
9072 i == Xdynamite_3 ? 16 :
9073 i == Xdynamite_4 ? 24 :
9074 i == Xsand_stonein_1 ? j + 1 :
9075 i == Xsand_stonein_2 ? j + 9 :
9076 i == Xsand_stonein_3 ? j + 17 :
9077 i == Xsand_stonein_4 ? j + 25 :
9078 i == Xsand_stoneout_1 && j == 0 ? 0 :
9079 i == Xsand_stoneout_1 && j == 1 ? 0 :
9080 i == Xsand_stoneout_1 && j == 2 ? 1 :
9081 i == Xsand_stoneout_1 && j == 3 ? 2 :
9082 i == Xsand_stoneout_1 && j == 4 ? 2 :
9083 i == Xsand_stoneout_1 && j == 5 ? 3 :
9084 i == Xsand_stoneout_1 && j == 6 ? 4 :
9085 i == Xsand_stoneout_1 && j == 7 ? 4 :
9086 i == Xsand_stoneout_2 && j == 0 ? 5 :
9087 i == Xsand_stoneout_2 && j == 1 ? 6 :
9088 i == Xsand_stoneout_2 && j == 2 ? 7 :
9089 i == Xsand_stoneout_2 && j == 3 ? 8 :
9090 i == Xsand_stoneout_2 && j == 4 ? 9 :
9091 i == Xsand_stoneout_2 && j == 5 ? 11 :
9092 i == Xsand_stoneout_2 && j == 6 ? 13 :
9093 i == Xsand_stoneout_2 && j == 7 ? 15 :
9094 i == Xboom_bug && j == 1 ? 2 :
9095 i == Xboom_bug && j == 2 ? 2 :
9096 i == Xboom_bug && j == 3 ? 4 :
9097 i == Xboom_bug && j == 4 ? 4 :
9098 i == Xboom_bug && j == 5 ? 2 :
9099 i == Xboom_bug && j == 6 ? 2 :
9100 i == Xboom_bug && j == 7 ? 0 :
9101 i == Xboom_tank && j == 1 ? 2 :
9102 i == Xboom_tank && j == 2 ? 2 :
9103 i == Xboom_tank && j == 3 ? 4 :
9104 i == Xboom_tank && j == 4 ? 4 :
9105 i == Xboom_tank && j == 5 ? 2 :
9106 i == Xboom_tank && j == 6 ? 2 :
9107 i == Xboom_tank && j == 7 ? 0 :
9108 i == Xboom_android && j == 7 ? 6 :
9109 i == Xboom_1 && j == 1 ? 2 :
9110 i == Xboom_1 && j == 2 ? 2 :
9111 i == Xboom_1 && j == 3 ? 4 :
9112 i == Xboom_1 && j == 4 ? 4 :
9113 i == Xboom_1 && j == 5 ? 6 :
9114 i == Xboom_1 && j == 6 ? 6 :
9115 i == Xboom_1 && j == 7 ? 8 :
9116 i == Xboom_2 && j == 0 ? 8 :
9117 i == Xboom_2 && j == 1 ? 8 :
9118 i == Xboom_2 && j == 2 ? 10 :
9119 i == Xboom_2 && j == 3 ? 10 :
9120 i == Xboom_2 && j == 4 ? 10 :
9121 i == Xboom_2 && j == 5 ? 12 :
9122 i == Xboom_2 && j == 6 ? 12 :
9123 i == Xboom_2 && j == 7 ? 12 :
9124 special_animation && j == 4 ? 3 :
9125 effective_action != action ? 0 :
9127 int frame = getAnimationFrame(g->anim_frames,
9130 g->anim_start_frame,
9133 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9134 g->double_movement && is_backside);
9136 g_em->bitmap = src_bitmap;
9137 g_em->src_x = src_x;
9138 g_em->src_y = src_y;
9139 g_em->src_offset_x = 0;
9140 g_em->src_offset_y = 0;
9141 g_em->dst_offset_x = 0;
9142 g_em->dst_offset_y = 0;
9143 g_em->width = TILEX;
9144 g_em->height = TILEY;
9146 g_em->preserve_background = FALSE;
9148 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9151 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9152 effective_action == ACTION_MOVING ||
9153 effective_action == ACTION_PUSHING ||
9154 effective_action == ACTION_EATING)) ||
9155 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9156 effective_action == ACTION_EMPTYING)))
9159 (effective_action == ACTION_FALLING ||
9160 effective_action == ACTION_FILLING ||
9161 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9162 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9163 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9164 int num_steps = (i == Ydrip_1_s ? 16 :
9165 i == Ydrip_1_sB ? 16 :
9166 i == Ydrip_2_s ? 16 :
9167 i == Ydrip_2_sB ? 16 :
9168 i == Xsand_stonein_1 ? 32 :
9169 i == Xsand_stonein_2 ? 32 :
9170 i == Xsand_stonein_3 ? 32 :
9171 i == Xsand_stonein_4 ? 32 :
9172 i == Xsand_stoneout_1 ? 16 :
9173 i == Xsand_stoneout_2 ? 16 : 8);
9174 int cx = ABS(dx) * (TILEX / num_steps);
9175 int cy = ABS(dy) * (TILEY / num_steps);
9176 int step_frame = (i == Ydrip_2_s ? j + 8 :
9177 i == Ydrip_2_sB ? j + 8 :
9178 i == Xsand_stonein_2 ? j + 8 :
9179 i == Xsand_stonein_3 ? j + 16 :
9180 i == Xsand_stonein_4 ? j + 24 :
9181 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9182 int step = (is_backside ? step_frame : num_steps - step_frame);
9184 if (is_backside) // tile where movement starts
9186 if (dx < 0 || dy < 0)
9188 g_em->src_offset_x = cx * step;
9189 g_em->src_offset_y = cy * step;
9193 g_em->dst_offset_x = cx * step;
9194 g_em->dst_offset_y = cy * step;
9197 else // tile where movement ends
9199 if (dx < 0 || dy < 0)
9201 g_em->dst_offset_x = cx * step;
9202 g_em->dst_offset_y = cy * step;
9206 g_em->src_offset_x = cx * step;
9207 g_em->src_offset_y = cy * step;
9211 g_em->width = TILEX - cx * step;
9212 g_em->height = TILEY - cy * step;
9215 // create unique graphic identifier to decide if tile must be redrawn
9216 /* bit 31 - 16 (16 bit): EM style graphic
9217 bit 15 - 12 ( 4 bit): EM style frame
9218 bit 11 - 6 ( 6 bit): graphic width
9219 bit 5 - 0 ( 6 bit): graphic height */
9220 g_em->unique_identifier =
9221 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9225 for (i = 0; i < GAME_TILE_MAX; i++)
9227 for (j = 0; j < 8; j++)
9229 int element = object_mapping[i].element_rnd;
9230 int action = object_mapping[i].action;
9231 int direction = object_mapping[i].direction;
9232 boolean is_backside = object_mapping[i].is_backside;
9233 int graphic_action = el_act_dir2img(element, action, direction);
9234 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9236 if ((action == ACTION_SMASHED_BY_ROCK ||
9237 action == ACTION_SMASHED_BY_SPRING ||
9238 action == ACTION_EATING) &&
9239 graphic_action == graphic_default)
9241 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9242 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9243 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9244 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9247 // no separate animation for "smashed by rock" -- use rock instead
9248 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9249 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9251 g_em->bitmap = g_xx->bitmap;
9252 g_em->src_x = g_xx->src_x;
9253 g_em->src_y = g_xx->src_y;
9254 g_em->src_offset_x = g_xx->src_offset_x;
9255 g_em->src_offset_y = g_xx->src_offset_y;
9256 g_em->dst_offset_x = g_xx->dst_offset_x;
9257 g_em->dst_offset_y = g_xx->dst_offset_y;
9258 g_em->width = g_xx->width;
9259 g_em->height = g_xx->height;
9260 g_em->unique_identifier = g_xx->unique_identifier;
9263 g_em->preserve_background = TRUE;
9268 for (p = 0; p < MAX_PLAYERS; p++)
9270 for (i = 0; i < PLY_MAX; i++)
9272 int element = player_mapping[p][i].element_rnd;
9273 int action = player_mapping[p][i].action;
9274 int direction = player_mapping[p][i].direction;
9276 for (j = 0; j < 8; j++)
9278 int effective_element = element;
9279 int effective_action = action;
9280 int graphic = (direction == MV_NONE ?
9281 el_act2img(effective_element, effective_action) :
9282 el_act_dir2img(effective_element, effective_action,
9284 struct GraphicInfo *g = &graphic_info[graphic];
9285 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9289 int frame = getAnimationFrame(g->anim_frames,
9292 g->anim_start_frame,
9295 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9297 g_em->bitmap = src_bitmap;
9298 g_em->src_x = src_x;
9299 g_em->src_y = src_y;
9300 g_em->src_offset_x = 0;
9301 g_em->src_offset_y = 0;
9302 g_em->dst_offset_x = 0;
9303 g_em->dst_offset_y = 0;
9304 g_em->width = TILEX;
9305 g_em->height = TILEY;
9311 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9312 boolean any_player_moving,
9313 boolean any_player_snapping,
9314 boolean any_player_dropping)
9316 if (frame == 7 && !any_player_dropping)
9318 if (!local_player->was_waiting)
9320 if (!CheckSaveEngineSnapshotToList())
9323 local_player->was_waiting = TRUE;
9326 else if (any_player_moving || any_player_snapping || any_player_dropping)
9328 local_player->was_waiting = FALSE;
9332 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9333 boolean murphy_is_dropping)
9335 if (murphy_is_waiting)
9337 if (!local_player->was_waiting)
9339 if (!CheckSaveEngineSnapshotToList())
9342 local_player->was_waiting = TRUE;
9347 local_player->was_waiting = FALSE;
9351 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9352 boolean button_released)
9354 if (button_released)
9356 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9357 CheckSaveEngineSnapshotToList();
9359 else if (element_clicked)
9361 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9362 CheckSaveEngineSnapshotToList();
9364 game.snapshot.changed_action = TRUE;
9368 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9369 boolean any_player_moving,
9370 boolean any_player_snapping,
9371 boolean any_player_dropping)
9373 if (tape.single_step && tape.recording && !tape.pausing)
9374 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9375 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9377 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9378 any_player_snapping, any_player_dropping);
9380 return tape.pausing;
9383 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9384 boolean murphy_is_dropping)
9386 boolean murphy_starts_dropping = FALSE;
9389 for (i = 0; i < MAX_PLAYERS; i++)
9390 if (stored_player[i].force_dropping)
9391 murphy_starts_dropping = TRUE;
9393 if (tape.single_step && tape.recording && !tape.pausing)
9394 if (murphy_is_waiting && !murphy_starts_dropping)
9395 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9397 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9400 void CheckSingleStepMode_MM(boolean element_clicked,
9401 boolean button_released)
9403 if (tape.single_step && tape.recording && !tape.pausing)
9404 if (button_released)
9405 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9407 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9410 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9411 int graphic, int sync_frame, int x, int y)
9413 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9415 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9418 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9420 return (IS_NEXT_FRAME(sync_frame, graphic));
9423 int getGraphicInfo_Delay(int graphic)
9425 return graphic_info[graphic].anim_delay;
9428 void PlayMenuSoundExt(int sound)
9430 if (sound == SND_UNDEFINED)
9433 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9434 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9437 if (IS_LOOP_SOUND(sound))
9438 PlaySoundLoop(sound);
9443 void PlayMenuSound(void)
9445 PlayMenuSoundExt(menu.sound[game_status]);
9448 void PlayMenuSoundStereo(int sound, int stereo_position)
9450 if (sound == SND_UNDEFINED)
9453 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9454 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9457 if (IS_LOOP_SOUND(sound))
9458 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9460 PlaySoundStereo(sound, stereo_position);
9463 void PlayMenuSoundIfLoopExt(int sound)
9465 if (sound == SND_UNDEFINED)
9468 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9469 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9472 if (IS_LOOP_SOUND(sound))
9473 PlaySoundLoop(sound);
9476 void PlayMenuSoundIfLoop(void)
9478 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9481 void PlayMenuMusicExt(int music)
9483 if (music == MUS_UNDEFINED)
9486 if (!setup.sound_music)
9489 if (IS_LOOP_MUSIC(music))
9490 PlayMusicLoop(music);
9495 void PlayMenuMusic(void)
9497 char *curr_music = getCurrentlyPlayingMusicFilename();
9498 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9500 if (!strEqual(curr_music, next_music))
9501 PlayMenuMusicExt(menu.music[game_status]);
9504 void PlayMenuSoundsAndMusic(void)
9510 static void FadeMenuSounds(void)
9515 static void FadeMenuMusic(void)
9517 char *curr_music = getCurrentlyPlayingMusicFilename();
9518 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9520 if (!strEqual(curr_music, next_music))
9524 void FadeMenuSoundsAndMusic(void)
9530 void PlaySoundActivating(void)
9533 PlaySound(SND_MENU_ITEM_ACTIVATING);
9537 void PlaySoundSelecting(void)
9540 PlaySound(SND_MENU_ITEM_SELECTING);
9544 void ToggleFullscreenIfNeeded(void)
9546 // if setup and video fullscreen state are already matching, nothing do do
9547 if (setup.fullscreen == video.fullscreen_enabled ||
9548 !video.fullscreen_available)
9551 SDLSetWindowFullscreen(setup.fullscreen);
9553 // set setup value according to successfully changed fullscreen mode
9554 setup.fullscreen = video.fullscreen_enabled;
9557 void ChangeWindowScalingIfNeeded(void)
9559 // if setup and video window scaling are already matching, nothing do do
9560 if (setup.window_scaling_percent == video.window_scaling_percent ||
9561 video.fullscreen_enabled)
9564 SDLSetWindowScaling(setup.window_scaling_percent);
9566 // set setup value according to successfully changed window scaling
9567 setup.window_scaling_percent = video.window_scaling_percent;
9570 void ChangeVsyncModeIfNeeded(void)
9572 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9573 int video_vsync_mode = video.vsync_mode;
9575 // if setup and video vsync mode are already matching, nothing do do
9576 if (setup_vsync_mode == video_vsync_mode)
9579 // if renderer is using OpenGL, vsync mode can directly be changed
9580 SDLSetScreenVsyncMode(setup.vsync_mode);
9582 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9583 if (video.vsync_mode == video_vsync_mode)
9585 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9587 // save backbuffer content which gets lost when re-creating screen
9588 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9590 // force re-creating screen and renderer to set new vsync mode
9591 video.fullscreen_enabled = !setup.fullscreen;
9593 // when creating new renderer, destroy textures linked to old renderer
9594 FreeAllImageTextures(); // needs old renderer to free the textures
9596 // re-create screen and renderer (including change of vsync mode)
9597 ChangeVideoModeIfNeeded(setup.fullscreen);
9599 // set setup value according to successfully changed fullscreen mode
9600 setup.fullscreen = video.fullscreen_enabled;
9602 // restore backbuffer content from temporary backbuffer backup bitmap
9603 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9604 FreeBitmap(tmp_backbuffer);
9606 // update visible window/screen
9607 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9609 // when changing vsync mode, re-create textures for new renderer
9610 InitImageTextures();
9613 // set setup value according to successfully changed vsync mode
9614 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9617 static void JoinRectangles(int *x, int *y, int *width, int *height,
9618 int x2, int y2, int width2, int height2)
9620 // do not join with "off-screen" rectangle
9621 if (x2 == -1 || y2 == -1)
9626 *width = MAX(*width, width2);
9627 *height = MAX(*height, height2);
9630 void SetAnimStatus(int anim_status_new)
9632 if (anim_status_new == GAME_MODE_MAIN)
9633 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9634 else if (anim_status_new == GAME_MODE_NAMES)
9635 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9636 else if (anim_status_new == GAME_MODE_SCORES)
9637 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9639 global.anim_status_next = anim_status_new;
9641 // directly set screen modes that are entered without fading
9642 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9643 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9644 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9645 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9646 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9647 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9648 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9649 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9650 global.anim_status = global.anim_status_next;
9653 void SetGameStatus(int game_status_new)
9655 if (game_status_new != game_status)
9656 game_status_last_screen = game_status;
9658 game_status = game_status_new;
9660 SetAnimStatus(game_status_new);
9663 void SetFontStatus(int game_status_new)
9665 static int last_game_status = -1;
9667 if (game_status_new != -1)
9669 // set game status for font use after storing last game status
9670 last_game_status = game_status;
9671 game_status = game_status_new;
9675 // reset game status after font use from last stored game status
9676 game_status = last_game_status;
9680 void ResetFontStatus(void)
9685 void SetLevelSetInfo(char *identifier, int level_nr)
9687 setString(&levelset.identifier, identifier);
9689 levelset.level_nr = level_nr;
9692 boolean CheckIfAllViewportsHaveChanged(void)
9694 // if game status has not changed, viewports have not changed either
9695 if (game_status == game_status_last)
9698 // check if all viewports have changed with current game status
9700 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9701 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9702 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9703 int new_real_sx = vp_playfield->x;
9704 int new_real_sy = vp_playfield->y;
9705 int new_full_sxsize = vp_playfield->width;
9706 int new_full_sysize = vp_playfield->height;
9707 int new_dx = vp_door_1->x;
9708 int new_dy = vp_door_1->y;
9709 int new_dxsize = vp_door_1->width;
9710 int new_dysize = vp_door_1->height;
9711 int new_vx = vp_door_2->x;
9712 int new_vy = vp_door_2->y;
9713 int new_vxsize = vp_door_2->width;
9714 int new_vysize = vp_door_2->height;
9716 boolean playfield_viewport_has_changed =
9717 (new_real_sx != REAL_SX ||
9718 new_real_sy != REAL_SY ||
9719 new_full_sxsize != FULL_SXSIZE ||
9720 new_full_sysize != FULL_SYSIZE);
9722 boolean door_1_viewport_has_changed =
9725 new_dxsize != DXSIZE ||
9726 new_dysize != DYSIZE);
9728 boolean door_2_viewport_has_changed =
9731 new_vxsize != VXSIZE ||
9732 new_vysize != VYSIZE ||
9733 game_status_last == GAME_MODE_EDITOR);
9735 return (playfield_viewport_has_changed &&
9736 door_1_viewport_has_changed &&
9737 door_2_viewport_has_changed);
9740 boolean CheckFadeAll(void)
9742 return (CheckIfGlobalBorderHasChanged() ||
9743 CheckIfAllViewportsHaveChanged());
9746 void ChangeViewportPropertiesIfNeeded(void)
9748 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9749 FALSE : setup.small_game_graphics);
9750 int gfx_game_mode = getGlobalGameStatus(game_status);
9751 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9753 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9754 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9755 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9756 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9757 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9758 int new_win_xsize = vp_window->width;
9759 int new_win_ysize = vp_window->height;
9760 int border_left = vp_playfield->border_left;
9761 int border_right = vp_playfield->border_right;
9762 int border_top = vp_playfield->border_top;
9763 int border_bottom = vp_playfield->border_bottom;
9764 int new_sx = vp_playfield->x + border_left;
9765 int new_sy = vp_playfield->y + border_top;
9766 int new_sxsize = vp_playfield->width - border_left - border_right;
9767 int new_sysize = vp_playfield->height - border_top - border_bottom;
9768 int new_real_sx = vp_playfield->x;
9769 int new_real_sy = vp_playfield->y;
9770 int new_full_sxsize = vp_playfield->width;
9771 int new_full_sysize = vp_playfield->height;
9772 int new_dx = vp_door_1->x;
9773 int new_dy = vp_door_1->y;
9774 int new_dxsize = vp_door_1->width;
9775 int new_dysize = vp_door_1->height;
9776 int new_vx = vp_door_2->x;
9777 int new_vy = vp_door_2->y;
9778 int new_vxsize = vp_door_2->width;
9779 int new_vysize = vp_door_2->height;
9780 int new_ex = vp_door_3->x;
9781 int new_ey = vp_door_3->y;
9782 int new_exsize = vp_door_3->width;
9783 int new_eysize = vp_door_3->height;
9784 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9785 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9786 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9787 int new_scr_fieldx = new_sxsize / tilesize;
9788 int new_scr_fieldy = new_sysize / tilesize;
9789 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9790 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9791 boolean init_gfx_buffers = FALSE;
9792 boolean init_video_buffer = FALSE;
9793 boolean init_gadgets_and_anims = FALSE;
9794 boolean init_em_graphics = FALSE;
9796 if (new_win_xsize != WIN_XSIZE ||
9797 new_win_ysize != WIN_YSIZE)
9799 WIN_XSIZE = new_win_xsize;
9800 WIN_YSIZE = new_win_ysize;
9802 init_video_buffer = TRUE;
9803 init_gfx_buffers = TRUE;
9804 init_gadgets_and_anims = TRUE;
9806 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9809 if (new_scr_fieldx != SCR_FIELDX ||
9810 new_scr_fieldy != SCR_FIELDY)
9812 // this always toggles between MAIN and GAME when using small tile size
9814 SCR_FIELDX = new_scr_fieldx;
9815 SCR_FIELDY = new_scr_fieldy;
9817 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9828 new_sxsize != SXSIZE ||
9829 new_sysize != SYSIZE ||
9830 new_dxsize != DXSIZE ||
9831 new_dysize != DYSIZE ||
9832 new_vxsize != VXSIZE ||
9833 new_vysize != VYSIZE ||
9834 new_exsize != EXSIZE ||
9835 new_eysize != EYSIZE ||
9836 new_real_sx != REAL_SX ||
9837 new_real_sy != REAL_SY ||
9838 new_full_sxsize != FULL_SXSIZE ||
9839 new_full_sysize != FULL_SYSIZE ||
9840 new_tilesize_var != TILESIZE_VAR
9843 // ------------------------------------------------------------------------
9844 // determine next fading area for changed viewport definitions
9845 // ------------------------------------------------------------------------
9847 // start with current playfield area (default fading area)
9850 FADE_SXSIZE = FULL_SXSIZE;
9851 FADE_SYSIZE = FULL_SYSIZE;
9853 // add new playfield area if position or size has changed
9854 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9855 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9857 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9858 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9861 // add current and new door 1 area if position or size has changed
9862 if (new_dx != DX || new_dy != DY ||
9863 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9865 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9866 DX, DY, DXSIZE, DYSIZE);
9867 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9868 new_dx, new_dy, new_dxsize, new_dysize);
9871 // add current and new door 2 area if position or size has changed
9872 if (new_vx != VX || new_vy != VY ||
9873 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9875 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9876 VX, VY, VXSIZE, VYSIZE);
9877 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9878 new_vx, new_vy, new_vxsize, new_vysize);
9881 // ------------------------------------------------------------------------
9882 // handle changed tile size
9883 // ------------------------------------------------------------------------
9885 if (new_tilesize_var != TILESIZE_VAR)
9887 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9889 // changing tile size invalidates scroll values of engine snapshots
9890 FreeEngineSnapshotSingle();
9892 // changing tile size requires update of graphic mapping for EM engine
9893 init_em_graphics = TRUE;
9904 SXSIZE = new_sxsize;
9905 SYSIZE = new_sysize;
9906 DXSIZE = new_dxsize;
9907 DYSIZE = new_dysize;
9908 VXSIZE = new_vxsize;
9909 VYSIZE = new_vysize;
9910 EXSIZE = new_exsize;
9911 EYSIZE = new_eysize;
9912 REAL_SX = new_real_sx;
9913 REAL_SY = new_real_sy;
9914 FULL_SXSIZE = new_full_sxsize;
9915 FULL_SYSIZE = new_full_sysize;
9916 TILESIZE_VAR = new_tilesize_var;
9918 init_gfx_buffers = TRUE;
9919 init_gadgets_and_anims = TRUE;
9921 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9922 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9925 if (init_gfx_buffers)
9927 // Debug("tools:viewport", "init_gfx_buffers");
9929 SCR_FIELDX = new_scr_fieldx_buffers;
9930 SCR_FIELDY = new_scr_fieldy_buffers;
9934 SCR_FIELDX = new_scr_fieldx;
9935 SCR_FIELDY = new_scr_fieldy;
9937 SetDrawDeactivationMask(REDRAW_NONE);
9938 SetDrawBackgroundMask(REDRAW_FIELD);
9941 if (init_video_buffer)
9943 // Debug("tools:viewport", "init_video_buffer");
9945 FreeAllImageTextures(); // needs old renderer to free the textures
9947 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9948 InitImageTextures();
9951 if (init_gadgets_and_anims)
9953 // Debug("tools:viewport", "init_gadgets_and_anims");
9956 InitGlobalAnimations();
9959 if (init_em_graphics)
9961 InitGraphicInfo_EM();
9965 void OpenURL(char *url)
9967 #if SDL_VERSION_ATLEAST(2,0,14)
9970 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
9971 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
9972 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
9976 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
9978 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
9982 // ============================================================================
9984 // ============================================================================
9986 #if defined(PLATFORM_WINDOWS)
9987 /* FILETIME of Jan 1 1970 00:00:00. */
9988 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
9991 * timezone information is stored outside the kernel so tzp isn't used anymore.
9993 * Note: this function is not for Win32 high precision timing purpose. See
9996 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
9999 SYSTEMTIME system_time;
10000 ULARGE_INTEGER ularge;
10002 GetSystemTime(&system_time);
10003 SystemTimeToFileTime(&system_time, &file_time);
10004 ularge.LowPart = file_time.dwLowDateTime;
10005 ularge.HighPart = file_time.dwHighDateTime;
10007 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10008 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10014 static char *test_init_uuid_random_function_simple(void)
10016 static char seed_text[100];
10017 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10019 sprintf(seed_text, "%d", seed);
10024 static char *test_init_uuid_random_function_better(void)
10026 static char seed_text[100];
10027 struct timeval current_time;
10029 gettimeofday(¤t_time, NULL);
10031 prng_seed_bytes(¤t_time, sizeof(current_time));
10033 sprintf(seed_text, "%ld.%ld",
10034 (long)current_time.tv_sec,
10035 (long)current_time.tv_usec);
10040 #if defined(PLATFORM_WINDOWS)
10041 static char *test_init_uuid_random_function_better_windows(void)
10043 static char seed_text[100];
10044 struct timeval current_time;
10046 gettimeofday_windows(¤t_time, NULL);
10048 prng_seed_bytes(¤t_time, sizeof(current_time));
10050 sprintf(seed_text, "%ld.%ld",
10051 (long)current_time.tv_sec,
10052 (long)current_time.tv_usec);
10058 static unsigned int test_uuid_random_function_simple(int max)
10060 return GetSimpleRandom(max);
10063 static unsigned int test_uuid_random_function_better(int max)
10065 return (max > 0 ? prng_get_uint() % max : 0);
10068 #if defined(PLATFORM_WINDOWS)
10069 #define NUM_UUID_TESTS 3
10071 #define NUM_UUID_TESTS 2
10074 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10076 struct hashtable *hash_seeds =
10077 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10078 struct hashtable *hash_uuids =
10079 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10080 static char message[100];
10083 char *random_name = (nr == 0 ? "simple" : "better");
10084 char *random_type = (always_seed ? "always" : "only once");
10085 char *(*init_random_function)(void) =
10087 test_init_uuid_random_function_simple :
10088 test_init_uuid_random_function_better);
10089 unsigned int (*random_function)(int) =
10091 test_uuid_random_function_simple :
10092 test_uuid_random_function_better);
10095 #if defined(PLATFORM_WINDOWS)
10098 random_name = "windows";
10099 init_random_function = test_init_uuid_random_function_better_windows;
10105 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10106 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10108 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10109 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10110 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10112 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10116 // always initialize random number generator at least once
10117 init_random_function();
10119 unsigned int time_start = SDL_GetTicks();
10121 for (i = 0; i < num_uuids; i++)
10125 char *seed = getStringCopy(init_random_function());
10127 hashtable_remove(hash_seeds, seed);
10128 hashtable_insert(hash_seeds, seed, "1");
10131 char *uuid = getStringCopy(getUUIDExt(random_function));
10133 hashtable_remove(hash_uuids, uuid);
10134 hashtable_insert(hash_uuids, uuid, "1");
10137 int num_unique_seeds = hashtable_count(hash_seeds);
10138 int num_unique_uuids = hashtable_count(hash_uuids);
10140 unsigned int time_needed = SDL_GetTicks() - time_start;
10142 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10144 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10147 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10149 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10150 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10152 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10154 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10156 Request(message, REQ_CONFIRM);
10158 hashtable_destroy(hash_seeds, 0);
10159 hashtable_destroy(hash_uuids, 0);
10162 void TestGeneratingUUIDs(void)
10164 int num_uuids = 1000000;
10167 for (i = 0; i < NUM_UUID_TESTS; i++)
10168 for (j = 0; j < 2; j++)
10169 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10171 CloseAllAndExit(0);