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 // always use global border for PLAYING when restarting the game
638 if (global.border_status == GAME_MODE_PSEUDO_RESTARTING)
639 global.border_status = GAME_MODE_PLAYING;
641 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
643 global.border_status = last_border_status;
644 gfx.masked_border_bitmap_ptr = backbuffer;
648 void DrawTileCursor(int draw_target)
650 DrawTileCursor_MM(draw_target, game_status == GAME_MODE_PLAYING);
653 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
655 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
658 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
660 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
661 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
663 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
666 void BlitScreenToBitmap(Bitmap *target_bitmap)
668 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
669 BlitScreenToBitmap_EM(target_bitmap);
670 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
671 BlitScreenToBitmap_SP(target_bitmap);
672 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
673 BlitScreenToBitmap_MM(target_bitmap);
674 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
675 BlitScreenToBitmap_RND(target_bitmap);
677 redraw_mask |= REDRAW_FIELD;
680 static void DrawFramesPerSecond(void)
683 int font_nr = FONT_TEXT_2;
684 int font_width = getFontWidth(font_nr);
685 int draw_deactivation_mask = GetDrawDeactivationMask();
686 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
688 // draw FPS with leading space (needed if field buffer deactivated)
689 sprintf(text, " %04.1f fps", global.frames_per_second);
691 // override draw deactivation mask (required for invisible warp mode)
692 SetDrawDeactivationMask(REDRAW_NONE);
694 // draw opaque FPS if field buffer deactivated, else draw masked FPS
695 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
696 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
698 // set draw deactivation mask to previous value
699 SetDrawDeactivationMask(draw_deactivation_mask);
701 // force full-screen redraw in this frame
702 redraw_mask = REDRAW_ALL;
706 static void PrintFrameTimeDebugging(void)
708 static unsigned int last_counter = 0;
709 unsigned int counter = Counter();
710 int diff_1 = counter - last_counter;
711 int diff_2 = diff_1 - GAME_FRAME_DELAY;
713 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
714 char diff_bar[2 * diff_2_max + 5];
718 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
720 for (i = 0; i < diff_2_max; i++)
721 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
722 i >= diff_2_max - diff_2_cut ? '-' : ' ');
724 diff_bar[pos++] = '|';
726 for (i = 0; i < diff_2_max; i++)
727 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
729 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
731 diff_bar[pos++] = '\0';
733 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
736 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
739 last_counter = counter;
743 static int unifiedRedrawMask(int mask)
745 if (mask & REDRAW_ALL)
748 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
754 static boolean equalRedrawMasks(int mask_1, int mask_2)
756 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
759 void BackToFront(void)
761 static int last_redraw_mask = REDRAW_NONE;
763 // force screen redraw in every frame to continue drawing global animations
764 // (but always use the last redraw mask to prevent unwanted side effects)
765 if (redraw_mask == REDRAW_NONE)
766 redraw_mask = last_redraw_mask;
768 last_redraw_mask = redraw_mask;
771 // masked border now drawn immediately when blitting backbuffer to window
773 // draw masked border to all viewports, if defined
774 DrawMaskedBorder(redraw_mask);
777 // draw frames per second (only if debug mode is enabled)
778 if (redraw_mask & REDRAW_FPS)
779 DrawFramesPerSecond();
781 // remove playfield redraw before potentially merging with doors redraw
782 if (DrawingDeactivated(REAL_SX, REAL_SY))
783 redraw_mask &= ~REDRAW_FIELD;
785 // redraw complete window if both playfield and (some) doors need redraw
786 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
787 redraw_mask = REDRAW_ALL;
789 /* although redrawing the whole window would be fine for normal gameplay,
790 being able to only redraw the playfield is required for deactivating
791 certain drawing areas (mainly playfield) to work, which is needed for
792 warp-forward to be fast enough (by skipping redraw of most frames) */
794 if (redraw_mask & REDRAW_ALL)
796 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
798 else if (redraw_mask & REDRAW_FIELD)
800 BlitBitmap(backbuffer, window,
801 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
803 else if (redraw_mask & REDRAW_DOORS)
805 // merge door areas to prevent calling screen redraw more than once
811 if (redraw_mask & REDRAW_DOOR_1)
815 x2 = MAX(x2, DX + DXSIZE);
816 y2 = MAX(y2, DY + DYSIZE);
819 if (redraw_mask & REDRAW_DOOR_2)
823 x2 = MAX(x2, VX + VXSIZE);
824 y2 = MAX(y2, VY + VYSIZE);
827 if (redraw_mask & REDRAW_DOOR_3)
831 x2 = MAX(x2, EX + EXSIZE);
832 y2 = MAX(y2, EY + EYSIZE);
835 // make sure that at least one pixel is blitted, and inside the screen
836 // (else nothing is blitted, causing the animations not to be updated)
837 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
838 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
839 x2 = MIN(MAX(1, x2), WIN_XSIZE);
840 y2 = MIN(MAX(1, y2), WIN_YSIZE);
842 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
845 redraw_mask = REDRAW_NONE;
848 PrintFrameTimeDebugging();
852 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
854 unsigned int frame_delay_value_old = GetVideoFrameDelay();
856 SetVideoFrameDelay(frame_delay_value);
860 SetVideoFrameDelay(frame_delay_value_old);
863 static int fade_type_skip = FADE_TYPE_NONE;
865 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
867 void (*draw_border_function)(void) = NULL;
868 int x, y, width, height;
869 int fade_delay, post_delay;
871 if (fade_type == FADE_TYPE_FADE_OUT)
873 if (fade_type_skip != FADE_TYPE_NONE)
875 // skip all fade operations until specified fade operation
876 if (fade_type & fade_type_skip)
877 fade_type_skip = FADE_TYPE_NONE;
882 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
886 redraw_mask |= fade_mask;
888 if (fade_type == FADE_TYPE_SKIP)
890 fade_type_skip = fade_mode;
895 fade_delay = fading.fade_delay;
896 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
898 if (fade_type_skip != FADE_TYPE_NONE)
900 // skip all fade operations until specified fade operation
901 if (fade_type & fade_type_skip)
902 fade_type_skip = FADE_TYPE_NONE;
907 if (global.autoplay_leveldir)
912 if (fade_mask == REDRAW_FIELD)
917 height = FADE_SYSIZE;
919 if (border.draw_masked_when_fading)
920 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
922 DrawMaskedBorder_FIELD(); // draw once
932 // when switching screens without fading, set fade delay to zero
933 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
936 // do not display black frame when fading out without fade delay
937 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
940 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
941 draw_border_function);
943 redraw_mask &= ~fade_mask;
945 ClearAutoRepeatKeyEvents();
948 static void SetScreenStates_BeforeFadingIn(void)
950 // temporarily set screen mode for animations to screen after fading in
951 global.anim_status = global.anim_status_next;
953 // store backbuffer with all animations that will be started after fading in
954 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
956 // set screen mode for animations back to fading
957 global.anim_status = GAME_MODE_PSEUDO_FADING;
960 static void SetScreenStates_AfterFadingIn(void)
962 // store new source screen (to use correct masked border for fading)
963 gfx.fade_border_source_status = global.border_status;
965 global.anim_status = global.anim_status_next;
968 static void SetScreenStates_BeforeFadingOut(void)
970 // store new target screen (to use correct masked border for fading)
971 gfx.fade_border_target_status = game_status;
973 // set screen mode for animations to fading
974 global.anim_status = GAME_MODE_PSEUDO_FADING;
976 // store backbuffer with all animations that will be stopped for fading out
977 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
980 static void SetScreenStates_AfterFadingOut(void)
982 global.border_status = game_status;
984 // always use global border for PLAYING when restarting the game
985 if (global.border_status == GAME_MODE_PSEUDO_RESTARTING)
986 global.border_status = GAME_MODE_PLAYING;
989 void FadeIn(int fade_mask)
991 SetScreenStates_BeforeFadingIn();
994 DrawMaskedBorder(REDRAW_ALL);
997 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
998 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1000 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1004 FADE_SXSIZE = FULL_SXSIZE;
1005 FADE_SYSIZE = FULL_SYSIZE;
1007 // activate virtual buttons depending on upcoming game status
1008 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1009 game_status == GAME_MODE_PLAYING && !tape.playing)
1010 SetOverlayActive(TRUE);
1012 SetScreenStates_AfterFadingIn();
1014 // force update of global animation status in case of rapid screen changes
1015 redraw_mask = REDRAW_ALL;
1019 void FadeOut(int fade_mask)
1021 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1022 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1023 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1026 SetScreenStates_BeforeFadingOut();
1028 SetTileCursorActive(FALSE);
1029 SetOverlayActive(FALSE);
1032 DrawMaskedBorder(REDRAW_ALL);
1035 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1036 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1038 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1040 SetScreenStates_AfterFadingOut();
1043 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1045 static struct TitleFadingInfo fading_leave_stored;
1048 fading_leave_stored = fading_leave;
1050 fading = fading_leave_stored;
1053 void FadeSetEnterMenu(void)
1055 fading = menu.enter_menu;
1057 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1060 void FadeSetLeaveMenu(void)
1062 fading = menu.leave_menu;
1064 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1067 void FadeSetEnterScreen(void)
1069 fading = menu.enter_screen[game_status];
1071 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1074 void FadeSetNextScreen(void)
1076 fading = menu.next_screen[game_status];
1078 // (do not overwrite fade mode set by FadeSetEnterScreen)
1079 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1082 void FadeSetLeaveScreen(void)
1084 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1087 void FadeSetFromType(int type)
1089 if (type & TYPE_ENTER_SCREEN)
1090 FadeSetEnterScreen();
1091 else if (type & TYPE_ENTER)
1093 else if (type & TYPE_LEAVE)
1097 void FadeSetDisabled(void)
1099 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1101 fading = fading_none;
1104 void FadeSkipNextFadeIn(void)
1106 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1109 void FadeSkipNextFadeOut(void)
1111 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1114 static int getGlobalGameStatus(int status)
1116 return (status == GAME_MODE_PSEUDO_TYPENAME ? GAME_MODE_MAIN :
1117 status == GAME_MODE_SCOREINFO ? GAME_MODE_SCORES :
1121 int getImageFromGraphicOrDefault(int graphic, int default_graphic)
1123 if (graphic == IMG_UNDEFINED)
1124 return IMG_UNDEFINED;
1126 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1128 return (graphic_info[graphic].bitmap != NULL || redefined ?
1129 graphic : default_graphic);
1132 static int getBackgroundImage(int graphic)
1134 return getImageFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1137 static int getGlobalBorderImage(int graphic)
1139 return getImageFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1142 Bitmap *getGlobalBorderBitmapFromStatus(int status_raw)
1144 int status = getGlobalGameStatus(status_raw);
1146 (status == GAME_MODE_MAIN ? IMG_GLOBAL_BORDER_MAIN :
1147 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1148 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1149 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1151 int graphic_final = getGlobalBorderImage(graphic);
1153 return graphic_info[graphic_final].bitmap;
1156 void SetBackgroundImage(int graphic, int redraw_mask)
1158 struct GraphicInfo *g = &graphic_info[graphic];
1159 struct GraphicInfo g_undefined = { 0 };
1161 if (graphic == IMG_UNDEFINED)
1164 // always use original size bitmap for backgrounds, if existing
1165 Bitmap *bitmap = (g->bitmaps != NULL &&
1166 g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] != NULL ?
1167 g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] : g->bitmap);
1169 // remove every mask before setting mask for window, and
1170 // remove window area mask before setting mask for main or door area
1171 int remove_mask = (redraw_mask == REDRAW_ALL ? 0xffff : REDRAW_ALL);
1173 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
1174 SetBackgroundBitmap(NULL, remove_mask, 0, 0, 0, 0); // !!! FIX THIS !!!
1175 SetBackgroundBitmap(bitmap, redraw_mask,
1177 g->width, g->height);
1180 void SetWindowBackgroundImageIfDefined(int graphic)
1182 if (graphic_info[graphic].bitmap)
1183 SetBackgroundImage(graphic, REDRAW_ALL);
1186 void SetMainBackgroundImageIfDefined(int graphic)
1188 if (graphic_info[graphic].bitmap)
1189 SetBackgroundImage(graphic, REDRAW_FIELD);
1192 void SetDoorBackgroundImageIfDefined(int graphic)
1194 if (graphic_info[graphic].bitmap)
1195 SetBackgroundImage(graphic, REDRAW_DOOR_1);
1198 void SetWindowBackgroundImage(int graphic)
1200 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_ALL);
1203 void SetMainBackgroundImage(int graphic)
1205 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_FIELD);
1208 void SetDoorBackgroundImage(int graphic)
1210 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_DOOR_1);
1213 void SetPanelBackground(void)
1215 SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
1218 void DrawBackground(int x, int y, int width, int height)
1220 // "drawto" might still point to playfield buffer here (hall of fame)
1221 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1223 if (IN_GFX_FIELD_FULL(x, y))
1224 redraw_mask |= REDRAW_FIELD;
1225 else if (IN_GFX_DOOR_1(x, y))
1226 redraw_mask |= REDRAW_DOOR_1;
1227 else if (IN_GFX_DOOR_2(x, y))
1228 redraw_mask |= REDRAW_DOOR_2;
1229 else if (IN_GFX_DOOR_3(x, y))
1230 redraw_mask |= REDRAW_DOOR_3;
1233 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1235 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1237 if (font->bitmap == NULL)
1240 DrawBackground(x, y, width, height);
1243 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1245 struct GraphicInfo *g = &graphic_info[graphic];
1247 if (g->bitmap == NULL)
1250 DrawBackground(x, y, width, height);
1253 static int game_status_last = -1;
1254 static Bitmap *global_border_bitmap_last = NULL;
1255 static Bitmap *global_border_bitmap = NULL;
1256 static int real_sx_last = -1, real_sy_last = -1;
1257 static int full_sxsize_last = -1, full_sysize_last = -1;
1258 static int dx_last = -1, dy_last = -1;
1259 static int dxsize_last = -1, dysize_last = -1;
1260 static int vx_last = -1, vy_last = -1;
1261 static int vxsize_last = -1, vysize_last = -1;
1262 static int ex_last = -1, ey_last = -1;
1263 static int exsize_last = -1, eysize_last = -1;
1265 boolean CheckIfGlobalBorderHasChanged(void)
1267 // if game status has not changed, global border has not changed either
1268 if (game_status == game_status_last)
1271 // determine and store new global border bitmap for current game status
1272 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1274 return (global_border_bitmap_last != global_border_bitmap);
1277 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1279 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1280 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1282 // if game status has not changed, nothing has to be redrawn
1283 if (game_status == game_status_last)
1286 // redraw if last screen was title screen
1287 if (game_status_last == GAME_MODE_TITLE)
1290 // redraw if global screen border has changed
1291 if (CheckIfGlobalBorderHasChanged())
1294 // redraw if position or size of playfield area has changed
1295 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1296 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1299 // redraw if position or size of door area has changed
1300 if (dx_last != DX || dy_last != DY ||
1301 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1304 // redraw if position or size of tape area has changed
1305 if (vx_last != VX || vy_last != VY ||
1306 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1309 // redraw if position or size of editor area has changed
1310 if (ex_last != EX || ey_last != EY ||
1311 exsize_last != EXSIZE || eysize_last != EYSIZE)
1318 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1321 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1323 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1326 void RedrawGlobalBorder(void)
1328 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1330 RedrawGlobalBorderFromBitmap(bitmap);
1332 redraw_mask = REDRAW_ALL;
1335 static void RedrawGlobalBorderIfNeeded(void)
1337 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1338 if (game_status == game_status_last)
1342 // copy current draw buffer to later copy back areas that have not changed
1343 if (game_status_last != GAME_MODE_TITLE)
1344 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1346 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1347 if (CheckIfGlobalBorderRedrawIsNeeded())
1349 // determine and store new global border bitmap for current game status
1350 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1353 // redraw global screen border (or clear, if defined to be empty)
1354 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1356 if (game_status == GAME_MODE_EDITOR)
1357 DrawSpecialEditorDoor();
1359 // copy previous playfield and door areas, if they are defined on both
1360 // previous and current screen and if they still have the same size
1362 if (real_sx_last != -1 && real_sy_last != -1 &&
1363 REAL_SX != -1 && REAL_SY != -1 &&
1364 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1365 BlitBitmap(bitmap_db_store_1, backbuffer,
1366 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1369 if (dx_last != -1 && dy_last != -1 &&
1370 DX != -1 && DY != -1 &&
1371 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1372 BlitBitmap(bitmap_db_store_1, backbuffer,
1373 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1375 if (game_status != GAME_MODE_EDITOR)
1377 if (vx_last != -1 && vy_last != -1 &&
1378 VX != -1 && VY != -1 &&
1379 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1380 BlitBitmap(bitmap_db_store_1, backbuffer,
1381 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1385 if (ex_last != -1 && ey_last != -1 &&
1386 EX != -1 && EY != -1 &&
1387 exsize_last == EXSIZE && eysize_last == EYSIZE)
1388 BlitBitmap(bitmap_db_store_1, backbuffer,
1389 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1392 redraw_mask = REDRAW_ALL;
1395 game_status_last = game_status;
1397 global_border_bitmap_last = global_border_bitmap;
1399 real_sx_last = REAL_SX;
1400 real_sy_last = REAL_SY;
1401 full_sxsize_last = FULL_SXSIZE;
1402 full_sysize_last = FULL_SYSIZE;
1405 dxsize_last = DXSIZE;
1406 dysize_last = DYSIZE;
1409 vxsize_last = VXSIZE;
1410 vysize_last = VYSIZE;
1413 exsize_last = EXSIZE;
1414 eysize_last = EYSIZE;
1417 void ClearField(void)
1419 RedrawGlobalBorderIfNeeded();
1421 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1422 // (when entering hall of fame after playing)
1423 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1425 // !!! maybe this should be done before clearing the background !!!
1426 if (game_status == GAME_MODE_PLAYING)
1428 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1429 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1433 SetDrawtoField(DRAW_TO_BACKBUFFER);
1437 void MarkTileDirty(int x, int y)
1439 redraw_mask |= REDRAW_FIELD;
1442 void SetBorderElement(void)
1446 BorderElement = EL_EMPTY;
1448 // only the R'n'D game engine may use an additional steelwall border
1449 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1452 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1454 for (x = 0; x < lev_fieldx; x++)
1456 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1457 BorderElement = EL_STEELWALL;
1459 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1465 void FloodFillLevelExt(int start_x, int start_y, int fill_element,
1466 int max_array_fieldx, int max_array_fieldy,
1467 short field[max_array_fieldx][max_array_fieldy],
1468 int max_fieldx, int max_fieldy)
1470 static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY];
1471 struct XY *check = xy_topdown;
1472 int old_element = field[start_x][start_y];
1475 // do nothing if start field already has the desired content
1476 if (old_element == fill_element)
1479 stack_buffer[stack_pos++] = (struct XY){ start_x, start_y };
1481 while (stack_pos > 0)
1483 struct XY current = stack_buffer[--stack_pos];
1486 field[current.x][current.y] = fill_element;
1488 for (i = 0; i < 4; i++)
1490 int x = current.x + check[i].x;
1491 int y = current.y + check[i].y;
1493 // check for stack buffer overflow (should not happen)
1494 if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY)
1495 Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug.");
1497 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1498 stack_buffer[stack_pos++] = (struct XY){ x, y };
1503 void FloodFillLevel(int from_x, int from_y, int fill_element,
1504 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1505 int max_fieldx, int max_fieldy)
1507 FloodFillLevelExt(from_x, from_y, fill_element,
1508 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1509 max_fieldx, max_fieldy);
1512 void SetRandomAnimationValue(int x, int y)
1514 gfx.anim_random_frame = GfxRandom[x][y];
1517 int getGraphicAnimationFrame(int graphic, int sync_frame)
1519 // animation synchronized with global frame counter, not move position
1520 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1521 sync_frame = FrameCounter;
1522 else if (graphic_info[graphic].anim_global_anim_sync)
1523 sync_frame = getGlobalAnimSyncFrame();
1525 return getAnimationFrame(graphic_info[graphic].anim_frames,
1526 graphic_info[graphic].anim_delay,
1527 graphic_info[graphic].anim_mode,
1528 graphic_info[graphic].anim_start_frame,
1532 int getGraphicAnimationFrameXY(int graphic, int lx, int ly)
1534 if (graphic_info[graphic].anim_mode & ANIM_TILED)
1536 struct GraphicInfo *g = &graphic_info[graphic];
1537 int xsize = MAX(1, g->anim_frames_per_line);
1538 int ysize = MAX(1, g->anim_frames / xsize);
1539 int xoffset = g->anim_start_frame % xsize;
1540 int yoffset = g->anim_start_frame % ysize;
1541 // may be needed if screen field is significantly larger than playfield
1542 int x = (lx + xoffset + SCR_FIELDX * xsize) % xsize;
1543 int y = (ly + yoffset + SCR_FIELDY * ysize) % ysize;
1544 int sync_frame = y * xsize + x;
1546 return sync_frame % g->anim_frames;
1548 else if (graphic_info[graphic].anim_mode & ANIM_RANDOM_STATIC)
1550 struct GraphicInfo *g = &graphic_info[graphic];
1551 // may be needed if screen field is significantly larger than playfield
1552 int x = (lx + SCR_FIELDX * lev_fieldx) % lev_fieldx;
1553 int y = (ly + SCR_FIELDY * lev_fieldy) % lev_fieldy;
1554 int sync_frame = GfxRandomStatic[x][y];
1556 return sync_frame % g->anim_frames;
1560 int sync_frame = (IN_LEV_FIELD(lx, ly) ? GfxFrame[lx][ly] : -1);
1562 return getGraphicAnimationFrame(graphic, sync_frame);
1566 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1568 struct GraphicInfo *g = &graphic_info[graphic];
1569 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1571 if (tilesize == gfx.standard_tile_size)
1572 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1573 else if (tilesize == game.tile_size)
1574 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1576 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1579 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1580 boolean get_backside)
1582 struct GraphicInfo *g = &graphic_info[graphic];
1583 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1584 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1586 if (g->offset_y == 0) // frames are ordered horizontally
1588 int max_width = g->anim_frames_per_line * g->width;
1589 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1591 *x = pos % max_width;
1592 *y = src_y % g->height + pos / max_width * g->height;
1594 else if (g->offset_x == 0) // frames are ordered vertically
1596 int max_height = g->anim_frames_per_line * g->height;
1597 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1599 *x = src_x % g->width + pos / max_height * g->width;
1600 *y = pos % max_height;
1602 else // frames are ordered diagonally
1604 *x = src_x + frame * g->offset_x;
1605 *y = src_y + frame * g->offset_y;
1609 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1610 Bitmap **bitmap, int *x, int *y,
1611 boolean get_backside)
1613 struct GraphicInfo *g = &graphic_info[graphic];
1615 // if no graphics defined at all, use fallback graphics
1616 if (g->bitmaps == NULL)
1617 *g = graphic_info[IMG_CHAR_EXCLAM];
1619 // if no in-game graphics defined, always use standard graphic size
1620 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1621 tilesize = TILESIZE;
1623 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1624 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1626 *x = *x * tilesize / g->tile_size;
1627 *y = *y * tilesize / g->tile_size;
1630 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1631 Bitmap **bitmap, int *x, int *y)
1633 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1636 void getFixedGraphicSource(int graphic, int frame,
1637 Bitmap **bitmap, int *x, int *y)
1639 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1642 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1644 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1647 void getGlobalAnimGraphicSource(int graphic, int frame,
1648 Bitmap **bitmap, int *x, int *y)
1650 struct GraphicInfo *g = &graphic_info[graphic];
1652 // if no graphics defined at all, use fallback graphics
1653 if (g->bitmaps == NULL)
1654 *g = graphic_info[IMG_CHAR_EXCLAM];
1656 // use original size graphics, if existing, else use standard size graphics
1657 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1658 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1660 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1662 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1665 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1666 int *x, int *y, boolean get_backside)
1668 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1672 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1674 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1677 void DrawGraphic(int x, int y, int graphic, int frame)
1680 if (!IN_SCR_FIELD(x, y))
1682 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1683 Debug("draw:DrawGraphic", "This should never happen!");
1689 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1692 MarkTileDirty(x, y);
1695 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1698 if (!IN_SCR_FIELD(x, y))
1700 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1702 Debug("draw:DrawFixedGraphic", "This should never happen!");
1708 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1710 MarkTileDirty(x, y);
1713 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1719 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1721 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1724 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1730 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1731 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1734 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1737 if (!IN_SCR_FIELD(x, y))
1739 Debug("draw:DrawGraphicThruMask", "x = %d, y = %d, graphic = %d",
1741 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1747 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1750 MarkTileDirty(x, y);
1753 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1756 if (!IN_SCR_FIELD(x, y))
1758 Debug("draw:DrawFixedGraphicThruMask", "x = %d, y = %d, graphic = %d",
1760 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1766 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1768 MarkTileDirty(x, y);
1771 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1777 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1779 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1783 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1784 int graphic, int frame)
1789 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1791 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1795 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1797 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1799 MarkTileDirty(x / tilesize, y / tilesize);
1802 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1805 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1806 graphic, frame, tilesize);
1807 MarkTileDirty(x / tilesize, y / tilesize);
1810 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1816 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1817 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1820 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1821 int frame, int tilesize)
1826 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1827 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1830 void DrawMiniGraphic(int x, int y, int graphic)
1832 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX, SY + y * MINI_TILEY, graphic);
1833 MarkTileDirty(x / 2, y / 2);
1836 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1841 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1842 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1845 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1846 int graphic, int frame,
1847 int cut_mode, int mask_mode)
1852 int width = TILEX, height = TILEY;
1855 if (dx || dy) // shifted graphic
1857 if (x < BX1) // object enters playfield from the left
1864 else if (x > BX2) // object enters playfield from the right
1870 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1876 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1878 else if (dx) // general horizontal movement
1879 MarkTileDirty(x + SIGN(dx), y);
1881 if (y < BY1) // object enters playfield from the top
1883 if (cut_mode == CUT_BELOW) // object completely above top border
1891 else if (y > BY2) // object enters playfield from the bottom
1897 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1903 else if (dy > 0 && cut_mode == CUT_ABOVE)
1905 if (y == BY2) // object completely above bottom border
1911 MarkTileDirty(x, y + 1);
1912 } // object leaves playfield to the bottom
1913 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1915 else if (dy) // general vertical movement
1916 MarkTileDirty(x, y + SIGN(dy));
1920 if (!IN_SCR_FIELD(x, y))
1922 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1924 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1930 width = width * TILESIZE_VAR / TILESIZE;
1931 height = height * TILESIZE_VAR / TILESIZE;
1932 cx = cx * TILESIZE_VAR / TILESIZE;
1933 cy = cy * TILESIZE_VAR / TILESIZE;
1934 dx = dx * TILESIZE_VAR / TILESIZE;
1935 dy = dy * TILESIZE_VAR / TILESIZE;
1937 if (width > 0 && height > 0)
1939 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1944 dst_x = FX + x * TILEX_VAR + dx;
1945 dst_y = FY + y * TILEY_VAR + dy;
1947 if (mask_mode == USE_MASKING)
1948 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1951 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1954 MarkTileDirty(x, y);
1958 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1959 int graphic, int frame,
1960 int cut_mode, int mask_mode)
1965 int width = TILEX_VAR, height = TILEY_VAR;
1968 int x2 = x + SIGN(dx);
1969 int y2 = y + SIGN(dy);
1971 // movement with two-tile animations must be sync'ed with movement position,
1972 // not with current GfxFrame (which can be higher when using slow movement)
1973 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1974 int anim_frames = graphic_info[graphic].anim_frames;
1976 // (we also need anim_delay here for movement animations with less frames)
1977 int anim_delay = graphic_info[graphic].anim_delay;
1978 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1980 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1981 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1983 // re-calculate animation frame for two-tile movement animation
1984 frame = getGraphicAnimationFrame(graphic, sync_frame);
1986 // check if movement start graphic inside screen area and should be drawn
1987 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1989 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1991 dst_x = FX + x1 * TILEX_VAR;
1992 dst_y = FY + y1 * TILEY_VAR;
1994 if (mask_mode == USE_MASKING)
1995 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1998 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
2001 MarkTileDirty(x1, y1);
2004 // check if movement end graphic inside screen area and should be drawn
2005 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
2007 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
2009 dst_x = FX + x2 * TILEX_VAR;
2010 dst_y = FY + y2 * TILEY_VAR;
2012 if (mask_mode == USE_MASKING)
2013 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
2016 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
2019 MarkTileDirty(x2, y2);
2023 static void DrawGraphicShifted(int x, int y, int dx, int dy,
2024 int graphic, int frame,
2025 int cut_mode, int mask_mode)
2029 DrawGraphic(x, y, graphic, frame);
2034 if (graphic_info[graphic].double_movement) // EM style movement images
2035 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2037 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2040 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
2041 int graphic, int frame, int cut_mode)
2043 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
2046 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
2047 int cut_mode, int mask_mode)
2049 int lx = LEVELX(x), ly = LEVELY(y);
2053 if (IN_LEV_FIELD(lx, ly))
2055 if (element == EL_EMPTY)
2056 element = GfxElementEmpty[lx][ly];
2058 SetRandomAnimationValue(lx, ly);
2060 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2061 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2063 // do not use double (EM style) movement graphic when not moving
2064 if (graphic_info[graphic].double_movement && !dx && !dy)
2066 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2067 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2070 if (game.use_masked_elements && (dx || dy))
2071 mask_mode = USE_MASKING;
2073 else // border element
2075 graphic = el2img(element);
2076 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2079 if (element == EL_EXPANDABLE_WALL)
2081 boolean left_stopped = FALSE, right_stopped = FALSE;
2083 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2084 left_stopped = TRUE;
2085 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2086 right_stopped = TRUE;
2088 if (left_stopped && right_stopped)
2090 else if (left_stopped)
2092 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2093 frame = graphic_info[graphic].anim_frames - 1;
2095 else if (right_stopped)
2097 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2098 frame = graphic_info[graphic].anim_frames - 1;
2103 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2104 else if (mask_mode == USE_MASKING)
2105 DrawGraphicThruMask(x, y, graphic, frame);
2107 DrawGraphic(x, y, graphic, frame);
2110 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2111 int cut_mode, int mask_mode)
2113 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2114 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2115 cut_mode, mask_mode);
2118 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2121 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2124 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2127 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2130 void DrawLevelElementThruMask(int x, int y, int element)
2132 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2135 void DrawLevelFieldThruMask(int x, int y)
2137 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2140 // !!! implementation of quicksand is totally broken !!!
2141 #define IS_CRUMBLED_TILE(x, y, e) \
2142 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2143 !IS_MOVING(x, y) || \
2144 (e) == EL_QUICKSAND_EMPTYING || \
2145 (e) == EL_QUICKSAND_FAST_EMPTYING))
2147 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2152 int width, height, cx, cy;
2153 int sx = SCREENX(x), sy = SCREENY(y);
2154 int crumbled_border_size = graphic_info[graphic].border_size;
2155 int crumbled_tile_size = graphic_info[graphic].tile_size;
2156 int crumbled_border_size_var =
2157 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2160 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2162 for (i = 1; i < 4; i++)
2164 int dxx = (i & 1 ? dx : 0);
2165 int dyy = (i & 2 ? dy : 0);
2168 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2171 // check if neighbour field is of same crumble type
2172 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2173 graphic_info[graphic].class ==
2174 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2176 // return if check prevents inner corner
2177 if (same == (dxx == dx && dyy == dy))
2181 // if we reach this point, we have an inner corner
2183 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2185 width = crumbled_border_size_var;
2186 height = crumbled_border_size_var;
2187 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2188 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2190 if (game.use_masked_elements)
2192 int graphic0 = el2img(EL_EMPTY);
2193 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2194 Bitmap *src_bitmap0;
2197 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2199 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2201 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2203 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2205 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2208 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2210 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2213 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2218 int width, height, bx, by, cx, cy;
2219 int sx = SCREENX(x), sy = SCREENY(y);
2220 int crumbled_border_size = graphic_info[graphic].border_size;
2221 int crumbled_tile_size = graphic_info[graphic].tile_size;
2222 int crumbled_border_size_var =
2223 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2224 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2227 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2229 // only needed when using masked elements
2230 int graphic0 = el2img(EL_EMPTY);
2231 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2232 Bitmap *src_bitmap0;
2235 if (game.use_masked_elements)
2236 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2238 // draw simple, sloppy, non-corner-accurate crumbled border
2240 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2241 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2242 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2243 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2245 if (game.use_masked_elements)
2247 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2249 FX + sx * TILEX_VAR + cx,
2250 FY + sy * TILEY_VAR + cy);
2252 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2254 FX + sx * TILEX_VAR + cx,
2255 FY + sy * TILEY_VAR + cy);
2258 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2260 FX + sx * TILEX_VAR + cx,
2261 FY + sy * TILEY_VAR + cy);
2263 // (remaining middle border part must be at least as big as corner part)
2264 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2265 crumbled_border_size_var >= TILESIZE_VAR / 3)
2268 // correct corners of crumbled border, if needed
2270 for (i = -1; i <= 1; i += 2)
2272 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2273 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2274 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2277 // check if neighbour field is of same crumble type
2278 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2279 graphic_info[graphic].class ==
2280 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2282 // no crumbled corner, but continued crumbled border
2284 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2285 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2286 int b1 = (i == 1 ? crumbled_border_size_var :
2287 TILESIZE_VAR - 2 * crumbled_border_size_var);
2289 width = crumbled_border_size_var;
2290 height = crumbled_border_size_var;
2292 if (dir == 1 || dir == 2)
2307 if (game.use_masked_elements)
2309 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2311 FX + sx * TILEX_VAR + cx,
2312 FY + sy * TILEY_VAR + cy);
2314 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2316 FX + sx * TILEX_VAR + cx,
2317 FY + sy * TILEY_VAR + cy);
2320 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2322 FX + sx * TILEX_VAR + cx,
2323 FY + sy * TILEY_VAR + cy);
2328 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2330 int sx = SCREENX(x), sy = SCREENY(y);
2333 struct XY *xy = xy_topdown;
2335 if (!IN_LEV_FIELD(x, y))
2338 element = TILE_GFX_ELEMENT(x, y);
2340 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2342 if (!IN_SCR_FIELD(sx, sy))
2345 // crumble field borders towards direct neighbour fields
2346 for (i = 0; i < 4; i++)
2348 int xx = x + xy[i].x;
2349 int yy = y + xy[i].y;
2351 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2354 // check if neighbour field is of same crumble type
2355 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2356 graphic_info[graphic].class ==
2357 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2360 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2363 // crumble inner field corners towards corner neighbour fields
2364 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2365 graphic_info[graphic].anim_frames == 2)
2367 for (i = 0; i < 4; i++)
2369 int dx = (i & 1 ? +1 : -1);
2370 int dy = (i & 2 ? +1 : -1);
2372 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2376 MarkTileDirty(sx, sy);
2378 else // center field is not crumbled -- crumble neighbour fields
2380 // crumble field borders of direct neighbour fields
2381 for (i = 0; i < 4; i++)
2383 int xx = x + xy[i].x;
2384 int yy = y + xy[i].y;
2385 int sxx = sx + xy[i].x;
2386 int syy = sy + xy[i].y;
2388 if (!IN_LEV_FIELD(xx, yy) ||
2389 !IN_SCR_FIELD(sxx, syy))
2392 // do not crumble fields that are being digged or snapped
2393 if (Tile[xx][yy] == EL_EMPTY ||
2394 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 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2406 MarkTileDirty(sxx, syy);
2409 // crumble inner field corners of corner neighbour fields
2410 for (i = 0; i < 4; i++)
2412 int dx = (i & 1 ? +1 : -1);
2413 int dy = (i & 2 ? +1 : -1);
2419 if (!IN_LEV_FIELD(xx, yy) ||
2420 !IN_SCR_FIELD(sxx, syy))
2423 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2426 element = TILE_GFX_ELEMENT(xx, yy);
2428 if (!IS_CRUMBLED_TILE(xx, yy, element))
2431 graphic = el_act2crm(element, ACTION_DEFAULT);
2433 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2434 graphic_info[graphic].anim_frames == 2)
2435 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2437 MarkTileDirty(sxx, syy);
2442 void DrawLevelFieldCrumbled(int x, int y)
2446 if (!IN_LEV_FIELD(x, y))
2449 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2450 GfxElement[x][y] != EL_UNDEFINED &&
2451 GFX_CRUMBLED(GfxElement[x][y]))
2453 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2458 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2460 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2463 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2466 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2467 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2468 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2469 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2470 int sx = SCREENX(x), sy = SCREENY(y);
2472 DrawScreenGraphic(sx, sy, graphic1, frame1);
2473 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2476 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2478 int sx = SCREENX(x), sy = SCREENY(y);
2479 struct XY *xy = xy_topdown;
2482 // crumble direct neighbour fields (required for field borders)
2483 for (i = 0; i < 4; i++)
2485 int xx = x + xy[i].x;
2486 int yy = y + xy[i].y;
2487 int sxx = sx + xy[i].x;
2488 int syy = sy + xy[i].y;
2490 if (!IN_LEV_FIELD(xx, yy) ||
2491 !IN_SCR_FIELD(sxx, syy) ||
2492 !GFX_CRUMBLED(Tile[xx][yy]) ||
2496 DrawLevelField(xx, yy);
2499 // crumble corner neighbour fields (required for inner field corners)
2500 for (i = 0; i < 4; i++)
2502 int dx = (i & 1 ? +1 : -1);
2503 int dy = (i & 2 ? +1 : -1);
2509 if (!IN_LEV_FIELD(xx, yy) ||
2510 !IN_SCR_FIELD(sxx, syy) ||
2511 !GFX_CRUMBLED(Tile[xx][yy]) ||
2515 int element = TILE_GFX_ELEMENT(xx, yy);
2516 int graphic = el_act2crm(element, ACTION_DEFAULT);
2518 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2519 graphic_info[graphic].anim_frames == 2)
2520 DrawLevelField(xx, yy);
2524 static int getBorderElement(int x, int y)
2528 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2529 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2530 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2531 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2532 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2533 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2534 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2536 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2537 int steel_position = (x == -1 && y == -1 ? 0 :
2538 x == lev_fieldx && y == -1 ? 1 :
2539 x == -1 && y == lev_fieldy ? 2 :
2540 x == lev_fieldx && y == lev_fieldy ? 3 :
2541 x == -1 || x == lev_fieldx ? 4 :
2542 y == -1 || y == lev_fieldy ? 5 : 6);
2544 return border[steel_position][steel_type];
2547 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2549 if (game.use_masked_elements)
2551 if (graphic != el2img(EL_EMPTY))
2552 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2554 DrawGraphicThruMask(x, y, graphic, frame);
2558 DrawGraphic(x, y, graphic, frame);
2562 void DrawLevelGraphic(int x, int y, int graphic, int frame)
2564 DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2567 void DrawScreenElement(int x, int y, int element)
2569 int mask_mode = NO_MASKING;
2571 if (game.use_masked_elements)
2573 int lx = LEVELX(x), ly = LEVELY(y);
2575 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2577 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2579 mask_mode = USE_MASKING;
2583 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2584 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2587 void DrawLevelElement(int x, int y, int element)
2589 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2590 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2593 void DrawScreenField(int x, int y)
2595 int lx = LEVELX(x), ly = LEVELY(y);
2596 int element, content;
2598 if (!IN_LEV_FIELD(lx, ly))
2600 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2603 element = getBorderElement(lx, ly);
2605 DrawScreenElement(x, y, element);
2610 element = Tile[lx][ly];
2611 content = Store[lx][ly];
2613 if (IS_MOVING(lx, ly))
2615 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2616 boolean cut_mode = NO_CUTTING;
2618 if (element == EL_QUICKSAND_EMPTYING ||
2619 element == EL_QUICKSAND_FAST_EMPTYING ||
2620 element == EL_MAGIC_WALL_EMPTYING ||
2621 element == EL_BD_MAGIC_WALL_EMPTYING ||
2622 element == EL_DC_MAGIC_WALL_EMPTYING ||
2623 element == EL_AMOEBA_DROPPING)
2624 cut_mode = CUT_ABOVE;
2625 else if (element == EL_QUICKSAND_FILLING ||
2626 element == EL_QUICKSAND_FAST_FILLING ||
2627 element == EL_MAGIC_WALL_FILLING ||
2628 element == EL_BD_MAGIC_WALL_FILLING ||
2629 element == EL_DC_MAGIC_WALL_FILLING)
2630 cut_mode = CUT_BELOW;
2632 if (cut_mode == CUT_ABOVE)
2633 DrawScreenElement(x, y, element);
2635 DrawScreenElement(x, y, EL_EMPTY);
2637 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2639 int dir = MovDir[lx][ly];
2640 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2641 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2643 if (IN_SCR_FIELD(newx, newy))
2644 DrawScreenElement(newx, newy, EL_EMPTY);
2648 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2649 else if (cut_mode == NO_CUTTING)
2650 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2653 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2655 if (cut_mode == CUT_BELOW &&
2656 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2657 DrawLevelElement(lx, ly + 1, element);
2660 if (content == EL_ACID)
2662 int dir = MovDir[lx][ly];
2663 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2664 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2666 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2668 // prevent target field from being drawn again (but without masking)
2669 // (this would happen if target field is scanned after moving element)
2670 Stop[newlx][newly] = TRUE;
2673 else if (IS_BLOCKED(lx, ly))
2678 boolean cut_mode = NO_CUTTING;
2679 int element_old, content_old;
2681 Blocked2Moving(lx, ly, &oldx, &oldy);
2684 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2685 MovDir[oldx][oldy] == MV_RIGHT);
2687 element_old = Tile[oldx][oldy];
2688 content_old = Store[oldx][oldy];
2690 if (element_old == EL_QUICKSAND_EMPTYING ||
2691 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2692 element_old == EL_MAGIC_WALL_EMPTYING ||
2693 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2694 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2695 element_old == EL_AMOEBA_DROPPING)
2696 cut_mode = CUT_ABOVE;
2698 DrawScreenElement(x, y, EL_EMPTY);
2701 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2703 else if (cut_mode == NO_CUTTING)
2704 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2707 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2710 else if (IS_DRAWABLE(element))
2711 DrawScreenElement(x, y, element);
2713 DrawScreenElement(x, y, EL_EMPTY);
2716 void DrawLevelField(int x, int y)
2718 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2719 DrawScreenField(SCREENX(x), SCREENY(y));
2720 else if (IS_MOVING(x, y))
2724 Moving2Blocked(x, y, &newx, &newy);
2725 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2726 DrawScreenField(SCREENX(newx), SCREENY(newy));
2728 else if (IS_BLOCKED(x, y))
2732 Blocked2Moving(x, y, &oldx, &oldy);
2733 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2734 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2738 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2739 int (*el2img_function)(int), boolean masked,
2740 int element_bits_draw)
2742 int element_base = map_mm_wall_element(element);
2743 int element_bits = (IS_DF_WALL(element) ?
2744 element - EL_DF_WALL_START :
2745 IS_MM_WALL(element) ?
2746 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2747 int graphic = el2img_function(element_base);
2748 int tilesize_draw = tilesize / 2;
2753 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2755 for (i = 0; i < 4; i++)
2757 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2758 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2760 if (!(element_bits_draw & (1 << i)))
2763 if (element_bits & (1 << i))
2766 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2767 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2769 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2770 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2775 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2776 tilesize_draw, tilesize_draw);
2781 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2782 boolean masked, int element_bits_draw)
2784 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2785 element, tilesize, el2edimg, masked, element_bits_draw);
2788 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2789 int (*el2img_function)(int))
2791 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2795 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2798 if (IS_MM_WALL(element))
2800 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2801 element, tilesize, el2edimg, masked, 0x000f);
2805 int graphic = el2edimg(element);
2808 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2810 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2814 void DrawSizedElement(int x, int y, int element, int tilesize)
2816 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2819 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2821 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2824 void DrawMiniElement(int x, int y, int element)
2828 graphic = el2edimg(element);
2829 DrawMiniGraphic(x, y, graphic);
2832 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2835 int x = sx + scroll_x, y = sy + scroll_y;
2837 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2838 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2839 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2840 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2842 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2845 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2847 int x = sx + scroll_x, y = sy + scroll_y;
2849 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2850 DrawMiniElement(sx, sy, EL_EMPTY);
2851 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2852 DrawMiniElement(sx, sy, Tile[x][y]);
2854 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2857 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2858 int x, int y, int xsize, int ysize,
2859 int tile_width, int tile_height)
2863 int dst_x = startx + x * tile_width;
2864 int dst_y = starty + y * tile_height;
2865 int width = graphic_info[graphic].width;
2866 int height = graphic_info[graphic].height;
2867 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2868 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2869 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2870 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2871 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2872 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2873 boolean draw_masked = graphic_info[graphic].draw_masked;
2875 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2877 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2879 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2883 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2884 inner_sx + (x - 1) * tile_width % inner_width);
2885 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2886 inner_sy + (y - 1) * tile_height % inner_height);
2889 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2892 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2896 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2897 int x, int y, int xsize, int ysize,
2900 int font_width = getFontWidth(font_nr);
2901 int font_height = getFontHeight(font_nr);
2903 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2904 font_width, font_height);
2907 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2909 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2910 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2911 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2912 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2913 boolean no_delay = (tape.warp_forward);
2914 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2915 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2916 DelayCounter anim_delay = { anim_delay_value };
2917 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2918 int font_width = getFontWidth(font_nr);
2919 int font_height = getFontHeight(font_nr);
2920 int max_xsize = level.envelope[envelope_nr].xsize;
2921 int max_ysize = level.envelope[envelope_nr].ysize;
2922 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2923 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2924 int xend = max_xsize;
2925 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2926 int xstep = (xstart < xend ? 1 : 0);
2927 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2929 int end = MAX(xend - xstart, yend - ystart);
2932 for (i = start; i <= end; i++)
2934 int last_frame = end; // last frame of this "for" loop
2935 int x = xstart + i * xstep;
2936 int y = ystart + i * ystep;
2937 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2938 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2939 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2940 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2943 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2945 BlitScreenToBitmap(backbuffer);
2947 SetDrawtoField(DRAW_TO_BACKBUFFER);
2949 for (yy = 0; yy < ysize; yy++)
2950 for (xx = 0; xx < xsize; xx++)
2951 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2953 DrawTextArea(sx + font_width, sy + font_height,
2954 level.envelope[envelope_nr].text, font_nr, max_xsize,
2955 xsize - 2, ysize - 2, 0, mask_mode,
2956 level.envelope[envelope_nr].autowrap,
2957 level.envelope[envelope_nr].centered, FALSE);
2959 redraw_mask |= REDRAW_FIELD;
2962 SkipUntilDelayReached(&anim_delay, &i, last_frame);
2965 ClearAutoRepeatKeyEvents();
2968 void ShowEnvelope(int envelope_nr)
2970 int element = EL_ENVELOPE_1 + envelope_nr;
2971 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2972 int sound_opening = element_info[element].sound[ACTION_OPENING];
2973 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2974 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2975 boolean no_delay = (tape.warp_forward);
2976 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2977 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2978 int anim_mode = graphic_info[graphic].anim_mode;
2979 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2980 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2981 boolean overlay_enabled = GetOverlayEnabled();
2983 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2985 SetOverlayEnabled(FALSE);
2988 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2990 if (anim_mode == ANIM_DEFAULT)
2991 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2993 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2996 Delay_WithScreenUpdates(wait_delay_value);
2998 WaitForEventToContinue();
3001 SetOverlayEnabled(overlay_enabled);
3003 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3005 if (anim_mode != ANIM_NONE)
3006 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
3008 if (anim_mode == ANIM_DEFAULT)
3009 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
3011 game.envelope_active = FALSE;
3013 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3015 redraw_mask |= REDRAW_FIELD;
3019 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
3020 int xsize, int ysize)
3022 if (!global.use_envelope_request)
3025 if (request.bitmap == NULL ||
3026 xsize > request.xsize ||
3027 ysize > request.ysize)
3029 if (request.bitmap != NULL)
3030 FreeBitmap(request.bitmap);
3032 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
3034 SDL_Surface *surface = request.bitmap->surface;
3036 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
3037 Fail("SDLGetNativeSurface() failed");
3040 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3042 // create masked surface for request bitmap, if needed
3043 if (graphic_info[IMG_BACKGROUND_REQUEST].draw_masked)
3045 SDL_Surface *surface = request.bitmap->surface;
3046 SDL_Surface *surface_masked = request.bitmap->surface_masked;
3048 SDLBlitSurface(surface, surface_masked, 0, 0, xsize, ysize, 0, 0);
3049 SDL_SetColorKey(surface_masked, SET_TRANSPARENT_PIXEL,
3050 SDL_MapRGB(surface_masked->format, 0x00, 0x00, 0x00));
3053 SDLFreeBitmapTextures(request.bitmap);
3054 SDLCreateBitmapTextures(request.bitmap);
3056 // set envelope request run-time values
3059 request.xsize = xsize;
3060 request.ysize = ysize;
3063 void DrawEnvelopeRequestToScreen(int drawing_target)
3065 if (global.use_envelope_request &&
3066 game.request_active_or_moving &&
3067 drawing_target == DRAW_TO_SCREEN)
3069 if (graphic_info[IMG_BACKGROUND_REQUEST].draw_masked)
3070 BlitToScreenMasked(request.bitmap, 0, 0, request.xsize, request.ysize,
3071 request.sx, request.sy);
3073 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3074 request.sx, request.sy);
3078 static void setRequestBasePosition(int *x, int *y)
3080 int sx_base, sy_base;
3082 if (request.x != -1)
3083 sx_base = request.x;
3084 else if (request.align == ALIGN_LEFT)
3086 else if (request.align == ALIGN_RIGHT)
3087 sx_base = SX + SXSIZE;
3089 sx_base = SX + SXSIZE / 2;
3091 if (request.y != -1)
3092 sy_base = request.y;
3093 else if (request.valign == VALIGN_TOP)
3095 else if (request.valign == VALIGN_BOTTOM)
3096 sy_base = SY + SYSIZE;
3098 sy_base = SY + SYSIZE / 2;
3104 static void setRequestPositionExt(int *x, int *y, int width, int height,
3105 boolean add_border_size)
3107 int border_size = request.border_size;
3108 int sx_base, sy_base;
3111 setRequestBasePosition(&sx_base, &sy_base);
3113 if (request.align == ALIGN_LEFT)
3115 else if (request.align == ALIGN_RIGHT)
3116 sx = sx_base - width;
3118 sx = sx_base - width / 2;
3120 if (request.valign == VALIGN_TOP)
3122 else if (request.valign == VALIGN_BOTTOM)
3123 sy = sy_base - height;
3125 sy = sy_base - height / 2;
3127 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3128 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3130 if (add_border_size)
3140 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3142 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3145 static void DrawEnvelopeRequest(char *text, unsigned int req_state)
3147 DrawBuffer *drawto_last = drawto;
3148 char *text_final = text;
3149 char *text_door_style = NULL;
3150 int graphic = IMG_BACKGROUND_REQUEST;
3151 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3152 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3153 int font_nr = FONT_REQUEST;
3154 int font_width = getFontWidth(font_nr);
3155 int font_height = getFontHeight(font_nr);
3156 int border_size = request.border_size;
3157 int line_spacing = request.line_spacing;
3158 int line_height = font_height + line_spacing;
3159 int max_text_width = request.width - 2 * border_size;
3160 int max_text_height = request.height - 2 * border_size;
3161 int line_length = max_text_width / font_width;
3162 int max_lines = max_text_height / line_height;
3163 int text_width = line_length * font_width;
3164 int width = request.width;
3165 int height = request.height;
3166 int tile_size = MAX(request.step_offset, 1);
3167 int x_steps = width / tile_size;
3168 int y_steps = height / tile_size;
3169 int sx_offset = border_size;
3170 int sy_offset = border_size;
3174 if (request.centered)
3175 sx_offset = (request.width - text_width) / 2;
3177 if (request.wrap_single_words && !request.autowrap)
3179 char *src_text_ptr, *dst_text_ptr;
3181 text_door_style = checked_malloc(2 * strlen(text) + 1);
3183 src_text_ptr = text;
3184 dst_text_ptr = text_door_style;
3186 while (*src_text_ptr)
3188 if (*src_text_ptr == ' ' ||
3189 *src_text_ptr == '?' ||
3190 *src_text_ptr == '!')
3191 *dst_text_ptr++ = '\n';
3193 if (*src_text_ptr != ' ')
3194 *dst_text_ptr++ = *src_text_ptr;
3199 *dst_text_ptr = '\0';
3201 text_final = text_door_style;
3204 setRequestPosition(&sx, &sy, FALSE);
3206 // draw complete envelope request to temporary bitmap
3207 drawto = bitmap_db_store_1;
3209 ClearRectangle(drawto, sx, sy, width, height);
3211 for (y = 0; y < y_steps; y++)
3212 for (x = 0; x < x_steps; x++)
3213 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3214 x, y, x_steps, y_steps,
3215 tile_size, tile_size);
3217 // force DOOR font inside door area
3218 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3220 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3221 line_length, -1, max_lines, line_spacing, mask_mode,
3222 request.autowrap, request.centered, FALSE);
3226 if (req_state & REQ_ASK)
3228 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3229 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3230 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3231 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3233 else if (req_state & REQ_CONFIRM)
3235 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3236 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3238 else if (req_state & REQ_PLAYER)
3240 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3241 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3242 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3243 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3246 // restore pointer to drawing buffer
3247 drawto = drawto_last;
3249 // prepare complete envelope request from temporary bitmap
3250 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy, width, height);
3252 if (text_door_style)
3253 free(text_door_style);
3256 static void AnimateEnvelopeRequest(int anim_mode, int action)
3258 int delay_value_normal = request.step_delay;
3259 int delay_value_fast = delay_value_normal / 2;
3260 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3261 boolean no_delay = (tape.warp_forward);
3262 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3263 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3264 DelayCounter anim_delay = { anim_delay_value };
3266 int tile_size = MAX(request.step_offset, 1);
3267 int max_xsize = request.width / tile_size;
3268 int max_ysize = request.height / tile_size;
3269 int max_xsize_inner = max_xsize - 2;
3270 int max_ysize_inner = max_ysize - 2;
3272 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3273 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3274 int xend = max_xsize_inner;
3275 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3276 int xstep = (xstart < xend ? 1 : 0);
3277 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3279 int end = MAX(xend - xstart, yend - ystart);
3282 if (setup.quick_doors)
3289 for (i = start; i <= end; i++)
3291 int last_frame = end; // last frame of this "for" loop
3292 int x = xstart + i * xstep;
3293 int y = ystart + i * ystep;
3294 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3295 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3296 int xsize_size_left = (xsize - 1) * tile_size;
3297 int ysize_size_top = (ysize - 1) * tile_size;
3298 int max_xsize_pos = (max_xsize - 1) * tile_size;
3299 int max_ysize_pos = (max_ysize - 1) * tile_size;
3300 int width = xsize * tile_size;
3301 int height = ysize * tile_size;
3306 setRequestPosition(&src_x, &src_y, FALSE);
3307 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3309 for (yy = 0; yy < 2; yy++)
3311 for (xx = 0; xx < 2; xx++)
3313 int src_xx = src_x + xx * max_xsize_pos;
3314 int src_yy = src_y + yy * max_ysize_pos;
3315 int dst_xx = dst_x + xx * xsize_size_left;
3316 int dst_yy = dst_y + yy * ysize_size_top;
3317 int xx_size = (xx ? tile_size : xsize_size_left);
3318 int yy_size = (yy ? tile_size : ysize_size_top);
3320 // draw partial (animated) envelope request to temporary bitmap
3321 BlitBitmap(bitmap_db_store_1, bitmap_db_store_2,
3322 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3326 // prepare partial (animated) envelope request from temporary bitmap
3327 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, dst_x, dst_y,
3330 redraw_mask |= REDRAW_FIELD;
3334 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3337 ClearAutoRepeatKeyEvents();
3340 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3342 int graphic = IMG_BACKGROUND_REQUEST;
3343 int sound_opening = SND_REQUEST_OPENING;
3344 int sound_closing = SND_REQUEST_CLOSING;
3345 int anim_mode_1 = request.anim_mode; // (higher priority)
3346 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3347 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3348 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3349 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3351 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3353 if (action == ACTION_OPENING)
3355 DrawEnvelopeRequest(text, req_state);
3357 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3359 if (anim_mode == ANIM_DEFAULT)
3360 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3362 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3366 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3368 if (anim_mode != ANIM_NONE)
3369 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3371 if (anim_mode == ANIM_DEFAULT)
3372 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3375 game.envelope_active = FALSE;
3378 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3380 if (IS_MM_WALL(element))
3382 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3388 int graphic = el2preimg(element);
3390 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3391 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3396 void DrawLevel(int draw_background_mask)
3400 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3401 SetDrawBackgroundMask(draw_background_mask);
3405 for (x = BX1; x <= BX2; x++)
3406 for (y = BY1; y <= BY2; y++)
3407 DrawScreenField(x, y);
3409 redraw_mask |= REDRAW_FIELD;
3412 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3417 for (x = 0; x < size_x; x++)
3418 for (y = 0; y < size_y; y++)
3419 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3421 redraw_mask |= REDRAW_FIELD;
3424 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3428 for (x = 0; x < size_x; x++)
3429 for (y = 0; y < size_y; y++)
3430 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3432 redraw_mask |= REDRAW_FIELD;
3435 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3437 boolean show_level_border = (BorderElement != EL_EMPTY);
3438 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3439 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3440 int tile_size = preview.tile_size;
3441 int preview_width = preview.xsize * tile_size;
3442 int preview_height = preview.ysize * tile_size;
3443 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3444 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3445 int real_preview_width = real_preview_xsize * tile_size;
3446 int real_preview_height = real_preview_ysize * tile_size;
3447 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3448 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3451 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3454 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3456 dst_x += (preview_width - real_preview_width) / 2;
3457 dst_y += (preview_height - real_preview_height) / 2;
3459 for (x = 0; x < real_preview_xsize; x++)
3461 for (y = 0; y < real_preview_ysize; y++)
3463 int lx = from_x + x + (show_level_border ? -1 : 0);
3464 int ly = from_y + y + (show_level_border ? -1 : 0);
3465 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3466 getBorderElement(lx, ly));
3468 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3469 element, tile_size);
3473 redraw_mask |= REDRAW_FIELD;
3476 #define MICROLABEL_EMPTY 0
3477 #define MICROLABEL_LEVEL_NAME 1
3478 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3479 #define MICROLABEL_LEVEL_AUTHOR 3
3480 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3481 #define MICROLABEL_IMPORTED_FROM 5
3482 #define MICROLABEL_IMPORTED_BY_HEAD 6
3483 #define MICROLABEL_IMPORTED_BY 7
3485 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3487 int max_text_width = SXSIZE;
3488 int font_width = getFontWidth(font_nr);
3490 if (pos->align == ALIGN_CENTER)
3491 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3492 else if (pos->align == ALIGN_RIGHT)
3493 max_text_width = pos->x;
3495 max_text_width = SXSIZE - pos->x;
3497 return max_text_width / font_width;
3500 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3502 char label_text[MAX_OUTPUT_LINESIZE + 1];
3503 int max_len_label_text;
3504 int font_nr = pos->font;
3507 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3510 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3511 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3512 mode == MICROLABEL_IMPORTED_BY_HEAD)
3513 font_nr = pos->font_alt;
3515 max_len_label_text = getMaxTextLength(pos, font_nr);
3517 if (pos->size != -1)
3518 max_len_label_text = pos->size;
3520 for (i = 0; i < max_len_label_text; i++)
3521 label_text[i] = ' ';
3522 label_text[max_len_label_text] = '\0';
3524 if (strlen(label_text) > 0)
3525 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3528 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3529 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3530 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3531 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3532 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3533 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3534 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3535 max_len_label_text);
3536 label_text[max_len_label_text] = '\0';
3538 if (strlen(label_text) > 0)
3539 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3541 redraw_mask |= REDRAW_FIELD;
3544 static void DrawPreviewLevelLabel(int mode)
3546 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3549 static void DrawPreviewLevelInfo(int mode)
3551 if (mode == MICROLABEL_LEVEL_NAME)
3552 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3553 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3554 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3557 static void DrawPreviewLevelExt(boolean restart)
3559 static DelayCounter scroll_delay = { 0 };
3560 static DelayCounter label_delay = { 0 };
3561 static int from_x, from_y, scroll_direction;
3562 static int label_state, label_counter;
3563 boolean show_level_border = (BorderElement != EL_EMPTY);
3564 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3565 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3567 scroll_delay.value = preview.step_delay;
3568 label_delay.value = MICROLEVEL_LABEL_DELAY;
3575 if (preview.anim_mode == ANIM_CENTERED)
3577 if (level_xsize > preview.xsize)
3578 from_x = (level_xsize - preview.xsize) / 2;
3579 if (level_ysize > preview.ysize)
3580 from_y = (level_ysize - preview.ysize) / 2;
3583 from_x += preview.xoffset;
3584 from_y += preview.yoffset;
3586 scroll_direction = MV_RIGHT;
3590 DrawPreviewLevelPlayfield(from_x, from_y);
3591 DrawPreviewLevelLabel(label_state);
3593 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3594 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3596 // initialize delay counters
3597 ResetDelayCounter(&scroll_delay);
3598 ResetDelayCounter(&label_delay);
3600 if (leveldir_current->name)
3602 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3603 char label_text[MAX_OUTPUT_LINESIZE + 1];
3604 int font_nr = pos->font;
3605 int max_len_label_text = getMaxTextLength(pos, font_nr);
3607 if (pos->size != -1)
3608 max_len_label_text = pos->size;
3610 strncpy(label_text, leveldir_current->name, max_len_label_text);
3611 label_text[max_len_label_text] = '\0';
3613 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3614 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3620 // scroll preview level, if needed
3621 if (preview.anim_mode != ANIM_NONE &&
3622 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3623 DelayReached(&scroll_delay))
3625 switch (scroll_direction)
3630 from_x -= preview.step_offset;
3631 from_x = (from_x < 0 ? 0 : from_x);
3634 scroll_direction = MV_UP;
3638 if (from_x < level_xsize - preview.xsize)
3640 from_x += preview.step_offset;
3641 from_x = (from_x > level_xsize - preview.xsize ?
3642 level_xsize - preview.xsize : from_x);
3645 scroll_direction = MV_DOWN;
3651 from_y -= preview.step_offset;
3652 from_y = (from_y < 0 ? 0 : from_y);
3655 scroll_direction = MV_RIGHT;
3659 if (from_y < level_ysize - preview.ysize)
3661 from_y += preview.step_offset;
3662 from_y = (from_y > level_ysize - preview.ysize ?
3663 level_ysize - preview.ysize : from_y);
3666 scroll_direction = MV_LEFT;
3673 DrawPreviewLevelPlayfield(from_x, from_y);
3676 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3677 // redraw micro level label, if needed
3678 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3679 !strEqual(level.author, ANONYMOUS_NAME) &&
3680 !strEqual(level.author, leveldir_current->name) &&
3681 DelayReached(&label_delay))
3683 int max_label_counter = 23;
3685 if (leveldir_current->imported_from != NULL &&
3686 strlen(leveldir_current->imported_from) > 0)
3687 max_label_counter += 14;
3688 if (leveldir_current->imported_by != NULL &&
3689 strlen(leveldir_current->imported_by) > 0)
3690 max_label_counter += 14;
3692 label_counter = (label_counter + 1) % max_label_counter;
3693 label_state = (label_counter >= 0 && label_counter <= 7 ?
3694 MICROLABEL_LEVEL_NAME :
3695 label_counter >= 9 && label_counter <= 12 ?
3696 MICROLABEL_LEVEL_AUTHOR_HEAD :
3697 label_counter >= 14 && label_counter <= 21 ?
3698 MICROLABEL_LEVEL_AUTHOR :
3699 label_counter >= 23 && label_counter <= 26 ?
3700 MICROLABEL_IMPORTED_FROM_HEAD :
3701 label_counter >= 28 && label_counter <= 35 ?
3702 MICROLABEL_IMPORTED_FROM :
3703 label_counter >= 37 && label_counter <= 40 ?
3704 MICROLABEL_IMPORTED_BY_HEAD :
3705 label_counter >= 42 && label_counter <= 49 ?
3706 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3708 if (leveldir_current->imported_from == NULL &&
3709 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3710 label_state == MICROLABEL_IMPORTED_FROM))
3711 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3712 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3714 DrawPreviewLevelLabel(label_state);
3718 void DrawPreviewPlayers(void)
3720 if (game_status != GAME_MODE_MAIN)
3723 // do not draw preview players if level preview redefined, but players aren't
3724 if (preview.redefined && !menu.main.preview_players.redefined)
3727 boolean player_found[MAX_PLAYERS];
3728 int num_players = 0;
3731 for (i = 0; i < MAX_PLAYERS; i++)
3732 player_found[i] = FALSE;
3734 // check which players can be found in the level (simple approach)
3735 for (x = 0; x < lev_fieldx; x++)
3737 for (y = 0; y < lev_fieldy; y++)
3739 int element = level.field[x][y];
3741 if (IS_PLAYER_ELEMENT(element))
3743 int player_nr = GET_PLAYER_NR(element);
3745 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3747 if (!player_found[player_nr])
3750 player_found[player_nr] = TRUE;
3755 struct TextPosInfo *pos = &menu.main.preview_players;
3756 int tile_size = pos->tile_size;
3757 int border_size = pos->border_size;
3758 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3759 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3760 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3761 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3762 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3763 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3764 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3765 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3766 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3767 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3768 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3769 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3771 // clear area in which the players will be drawn
3772 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3773 max_players_width, max_players_height);
3775 if (!network.enabled && !setup.team_mode)
3778 // only draw players if level is suited for team mode
3779 if (num_players < 2)
3782 // draw all players that were found in the level
3783 for (i = 0; i < MAX_PLAYERS; i++)
3785 if (player_found[i])
3787 int graphic = el2img(EL_PLAYER_1 + i);
3789 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3791 xpos += player_xoffset;
3792 ypos += player_yoffset;
3797 void DrawPreviewLevelInitial(void)
3799 DrawPreviewLevelExt(TRUE);
3800 DrawPreviewPlayers();
3803 void DrawPreviewLevelAnimation(void)
3805 DrawPreviewLevelExt(FALSE);
3808 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3809 int border_size, int font_nr)
3811 int graphic = el2img(EL_PLAYER_1 + player_nr);
3812 int font_height = getFontHeight(font_nr);
3813 int player_height = MAX(tile_size, font_height);
3814 int xoffset_text = tile_size + border_size;
3815 int yoffset_text = (player_height - font_height) / 2;
3816 int yoffset_graphic = (player_height - tile_size) / 2;
3817 char *player_name = getNetworkPlayerName(player_nr + 1);
3819 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3821 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3824 static void DrawNetworkPlayersExt(boolean force)
3826 if (game_status != GAME_MODE_MAIN)
3829 if (!network.connected && !force)
3832 // do not draw network players if level preview redefined, but players aren't
3833 if (preview.redefined && !menu.main.network_players.redefined)
3836 int num_players = 0;
3839 for (i = 0; i < MAX_PLAYERS; i++)
3840 if (stored_player[i].connected_network)
3843 struct TextPosInfo *pos = &menu.main.network_players;
3844 int tile_size = pos->tile_size;
3845 int border_size = pos->border_size;
3846 int xoffset_text = tile_size + border_size;
3847 int font_nr = pos->font;
3848 int font_width = getFontWidth(font_nr);
3849 int font_height = getFontHeight(font_nr);
3850 int player_height = MAX(tile_size, font_height);
3851 int player_yoffset = player_height + border_size;
3852 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3853 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3854 int all_players_height = num_players * player_yoffset - border_size;
3855 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3856 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3857 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3859 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3860 max_players_width, max_players_height);
3862 // first draw local network player ...
3863 for (i = 0; i < MAX_PLAYERS; i++)
3865 if (stored_player[i].connected_network &&
3866 stored_player[i].connected_locally)
3868 char *player_name = getNetworkPlayerName(i + 1);
3869 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3870 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3872 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3874 ypos += player_yoffset;
3878 // ... then draw all other network players
3879 for (i = 0; i < MAX_PLAYERS; i++)
3881 if (stored_player[i].connected_network &&
3882 !stored_player[i].connected_locally)
3884 char *player_name = getNetworkPlayerName(i + 1);
3885 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3886 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3888 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3890 ypos += player_yoffset;
3895 void DrawNetworkPlayers(void)
3897 DrawNetworkPlayersExt(FALSE);
3900 void ClearNetworkPlayers(void)
3902 DrawNetworkPlayersExt(TRUE);
3905 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3906 int graphic, int lx, int ly,
3909 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3911 if (mask_mode == USE_MASKING)
3912 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3914 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3917 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3918 int graphic, int sync_frame, int mask_mode)
3920 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3922 if (mask_mode == USE_MASKING)
3923 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3925 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3928 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3929 int graphic, int sync_frame, int tilesize,
3932 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3934 if (mask_mode == USE_MASKING)
3935 DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
3937 DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
3940 static void DrawGraphicAnimation(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_VAR, FY + y * TILEY_VAR,
3959 graphic, lx, ly, mask_mode);
3961 MarkTileDirty(x, y);
3964 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3966 int lx = LEVELX(x), ly = LEVELY(y);
3967 int mask_mode = NO_MASKING;
3969 if (!IN_SCR_FIELD(x, y))
3972 if (game.use_masked_elements)
3974 if (Tile[lx][ly] != EL_EMPTY)
3976 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3978 mask_mode = USE_MASKING;
3982 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3983 graphic, lx, ly, mask_mode);
3985 MarkTileDirty(x, y);
3988 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3990 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3993 void DrawLevelElementAnimation(int x, int y, int element)
3995 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3997 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4000 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
4002 int sx = SCREENX(x), sy = SCREENY(y);
4004 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4007 if (Tile[x][y] == EL_EMPTY)
4008 graphic = el2img(GfxElementEmpty[x][y]);
4010 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4013 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4016 DrawGraphicAnimation(sx, sy, graphic);
4019 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4020 DrawLevelFieldCrumbled(x, y);
4022 if (GFX_CRUMBLED(Tile[x][y]))
4023 DrawLevelFieldCrumbled(x, y);
4027 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4029 int sx = SCREENX(x), sy = SCREENY(y);
4032 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4035 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4037 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4040 DrawGraphicAnimation(sx, sy, graphic);
4042 if (GFX_CRUMBLED(element))
4043 DrawLevelFieldCrumbled(x, y);
4046 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4048 if (player->use_murphy)
4050 // this works only because currently only one player can be "murphy" ...
4051 static int last_horizontal_dir = MV_LEFT;
4052 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4054 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4055 last_horizontal_dir = move_dir;
4057 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4059 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4061 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4067 return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4070 static boolean equalGraphics(int graphic1, int graphic2)
4072 struct GraphicInfo *g1 = &graphic_info[graphic1];
4073 struct GraphicInfo *g2 = &graphic_info[graphic2];
4075 return (g1->bitmap == g2->bitmap &&
4076 g1->src_x == g2->src_x &&
4077 g1->src_y == g2->src_y &&
4078 g1->anim_frames == g2->anim_frames &&
4079 g1->anim_delay == g2->anim_delay &&
4080 g1->anim_mode == g2->anim_mode);
4083 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4087 DRAW_PLAYER_STAGE_INIT = 0,
4088 DRAW_PLAYER_STAGE_LAST_FIELD,
4089 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4090 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4091 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4092 DRAW_PLAYER_STAGE_PLAYER,
4094 DRAW_PLAYER_STAGE_PLAYER,
4095 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4097 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4098 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4100 NUM_DRAW_PLAYER_STAGES
4103 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4105 static int static_last_player_graphic[MAX_PLAYERS];
4106 static int static_last_player_frame[MAX_PLAYERS];
4107 static boolean static_player_is_opaque[MAX_PLAYERS];
4108 static boolean draw_player[MAX_PLAYERS];
4109 int pnr = player->index_nr;
4111 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4113 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4114 static_last_player_frame[pnr] = player->Frame;
4115 static_player_is_opaque[pnr] = FALSE;
4117 draw_player[pnr] = TRUE;
4120 if (!draw_player[pnr])
4124 if (!IN_LEV_FIELD(player->jx, player->jy))
4126 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4127 Debug("draw:DrawPlayerExt", "This should never happen!");
4129 draw_player[pnr] = FALSE;
4135 int last_player_graphic = static_last_player_graphic[pnr];
4136 int last_player_frame = static_last_player_frame[pnr];
4137 boolean player_is_opaque = static_player_is_opaque[pnr];
4139 int jx = player->jx;
4140 int jy = player->jy;
4141 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4142 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4143 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4144 int last_jx = (player->is_moving ? jx - dx : jx);
4145 int last_jy = (player->is_moving ? jy - dy : jy);
4146 int next_jx = jx + dx;
4147 int next_jy = jy + dy;
4148 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4149 int sx = SCREENX(jx);
4150 int sy = SCREENY(jy);
4151 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4152 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4153 int element = Tile[jx][jy];
4154 int last_element = Tile[last_jx][last_jy];
4155 int action = (player->is_pushing ? ACTION_PUSHING :
4156 player->is_digging ? ACTION_DIGGING :
4157 player->is_collecting ? ACTION_COLLECTING :
4158 player->is_moving ? ACTION_MOVING :
4159 player->is_snapping ? ACTION_SNAPPING :
4160 player->is_dropping ? ACTION_DROPPING :
4161 player->is_waiting ? player->action_waiting :
4164 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4166 // ------------------------------------------------------------------------
4167 // initialize drawing the player
4168 // ------------------------------------------------------------------------
4170 draw_player[pnr] = FALSE;
4172 // GfxElement[][] is set to the element the player is digging or collecting;
4173 // remove also for off-screen player if the player is not moving anymore
4174 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4175 GfxElement[jx][jy] = EL_UNDEFINED;
4177 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4180 if (element == EL_EXPLOSION)
4183 InitPlayerGfxAnimation(player, action, move_dir);
4185 draw_player[pnr] = TRUE;
4187 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4189 // ------------------------------------------------------------------------
4190 // draw things in the field the player is leaving, if needed
4191 // ------------------------------------------------------------------------
4193 if (!IN_SCR_FIELD(sx, sy))
4194 draw_player[pnr] = FALSE;
4196 if (!player->is_moving)
4199 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4201 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4203 if (last_element == EL_DYNAMITE_ACTIVE ||
4204 last_element == EL_EM_DYNAMITE_ACTIVE ||
4205 last_element == EL_SP_DISK_RED_ACTIVE)
4206 DrawDynamite(last_jx, last_jy);
4208 DrawLevelFieldThruMask(last_jx, last_jy);
4210 else if (last_element == EL_DYNAMITE_ACTIVE ||
4211 last_element == EL_EM_DYNAMITE_ACTIVE ||
4212 last_element == EL_SP_DISK_RED_ACTIVE)
4213 DrawDynamite(last_jx, last_jy);
4215 DrawLevelField(last_jx, last_jy);
4217 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4219 // ------------------------------------------------------------------------
4220 // draw things behind the player, if needed
4221 // ------------------------------------------------------------------------
4225 DrawLevelElement(jx, jy, Back[jx][jy]);
4230 if (IS_ACTIVE_BOMB(element))
4232 DrawLevelElement(jx, jy, EL_EMPTY);
4237 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4239 int old_element = GfxElement[jx][jy];
4240 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4241 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4243 if (GFX_CRUMBLED(old_element))
4244 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4246 DrawScreenGraphic(sx, sy, old_graphic, frame);
4248 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4249 static_player_is_opaque[pnr] = TRUE;
4253 GfxElement[jx][jy] = EL_UNDEFINED;
4255 // make sure that pushed elements are drawn with correct frame rate
4256 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4258 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4259 GfxFrame[jx][jy] = player->StepFrame;
4261 DrawLevelField(jx, jy);
4264 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4266 // ------------------------------------------------------------------------
4267 // draw things the player is pushing, if needed
4268 // ------------------------------------------------------------------------
4270 if (!player->is_pushing || !player->is_moving)
4273 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4276 int gfx_frame = GfxFrame[jx][jy];
4278 if (!IS_MOVING(jx, jy)) // push movement already finished
4280 element = Tile[next_jx][next_jy];
4281 gfx_frame = GfxFrame[next_jx][next_jy];
4284 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4285 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4286 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4288 // draw background element under pushed element (like the Sokoban field)
4289 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4291 // this allows transparent pushing animation over non-black background
4294 DrawLevelElement(jx, jy, Back[jx][jy]);
4296 DrawLevelElement(jx, jy, EL_EMPTY);
4299 if (Back[next_jx][next_jy])
4300 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4302 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4304 int px = SCREENX(jx), py = SCREENY(jy);
4305 int pxx = (TILEX - ABS(sxx)) * dx;
4306 int pyy = (TILEY - ABS(syy)) * dy;
4309 // do not draw (EM style) pushing animation when pushing is finished
4310 // (two-tile animations usually do not contain start and end frame)
4311 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4312 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4314 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4316 // masked drawing is needed for EMC style (double) movement graphics
4317 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4318 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4321 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4323 // ------------------------------------------------------------------------
4324 // draw player himself
4325 // ------------------------------------------------------------------------
4327 int graphic = getPlayerGraphic(player, move_dir);
4329 // in the case of changed player action or direction, prevent the current
4330 // animation frame from being restarted for identical animations
4331 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4332 player->Frame = last_player_frame;
4334 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4336 if (player_is_opaque)
4337 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4339 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4341 if (SHIELD_ON(player))
4343 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4344 IMG_SHIELD_NORMAL_ACTIVE);
4345 frame = getGraphicAnimationFrame(graphic, -1);
4347 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4350 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4352 // ------------------------------------------------------------------------
4353 // draw things in front of player (active dynamite or dynabombs)
4354 // ------------------------------------------------------------------------
4356 if (IS_ACTIVE_BOMB(element))
4358 int graphic = el2img(element);
4359 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4361 if (game.emulation == EMU_SUPAPLEX)
4362 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4364 DrawGraphicThruMask(sx, sy, graphic, frame);
4367 if (player_is_moving && last_element == EL_EXPLOSION)
4369 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4370 GfxElement[last_jx][last_jy] : EL_EMPTY);
4371 int graphic = el_act2img(element, ACTION_EXPLODING);
4372 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4373 int phase = ExplodePhase[last_jx][last_jy] - 1;
4374 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4377 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4380 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4382 // ------------------------------------------------------------------------
4383 // draw elements the player is just walking/passing through/under
4384 // ------------------------------------------------------------------------
4386 if (player_is_moving)
4388 // handle the field the player is leaving ...
4389 if (IS_ACCESSIBLE_INSIDE(last_element))
4390 DrawLevelField(last_jx, last_jy);
4391 else if (IS_ACCESSIBLE_UNDER(last_element))
4392 DrawLevelFieldThruMask(last_jx, last_jy);
4395 // do not redraw accessible elements if the player is just pushing them
4396 if (!player_is_moving || !player->is_pushing)
4398 // ... and the field the player is entering
4399 if (IS_ACCESSIBLE_INSIDE(element))
4400 DrawLevelField(jx, jy);
4401 else if (IS_ACCESSIBLE_UNDER(element))
4402 DrawLevelFieldThruMask(jx, jy);
4405 MarkTileDirty(sx, sy);
4409 void DrawPlayer(struct PlayerInfo *player)
4413 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4414 DrawPlayerExt(player, i);
4417 void DrawAllPlayers(void)
4421 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4422 for (j = 0; j < MAX_PLAYERS; j++)
4423 if (stored_player[j].active)
4424 DrawPlayerExt(&stored_player[j], i);
4427 void DrawPlayerField(int x, int y)
4429 if (!IS_PLAYER(x, y))
4432 DrawPlayer(PLAYERINFO(x, y));
4435 // ----------------------------------------------------------------------------
4437 void WaitForEventToContinue(void)
4439 boolean first_wait = TRUE;
4440 boolean still_wait = TRUE;
4442 if (program.headless)
4445 // simulate releasing mouse button over last gadget, if still pressed
4447 HandleGadgets(-1, -1, 0);
4449 button_status = MB_RELEASED;
4452 ClearPlayerAction();
4458 if (NextValidEvent(&event))
4462 case EVENT_BUTTONPRESS:
4463 case EVENT_FINGERPRESS:
4467 case EVENT_BUTTONRELEASE:
4468 case EVENT_FINGERRELEASE:
4469 still_wait = first_wait;
4472 case EVENT_KEYPRESS:
4473 case SDL_CONTROLLERBUTTONDOWN:
4474 case SDL_JOYBUTTONDOWN:
4479 HandleOtherEvents(&event);
4483 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4488 if (!PendingEvent())
4493 #define MAX_REQUEST_LINES 13
4494 #define MAX_REQUEST_LINE_FONT1_LEN 7
4495 #define MAX_REQUEST_LINE_FONT2_LEN 10
4497 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4499 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4501 int draw_buffer_last = GetDrawtoField();
4502 int width = request.width;
4503 int height = request.height;
4507 // when showing request dialog after game ended, deactivate game panel
4508 if (game_just_ended)
4509 game.panel.active = FALSE;
4511 game.request_active = TRUE;
4513 setRequestPosition(&sx, &sy, FALSE);
4515 button_status = MB_RELEASED;
4517 request_gadget_id = -1;
4522 if (game_just_ended)
4524 SetDrawtoField(draw_buffer_game);
4526 HandleGameActions();
4528 SetDrawtoField(DRAW_TO_BACKBUFFER);
4535 while (NextValidEvent(&event))
4539 case EVENT_BUTTONPRESS:
4540 case EVENT_BUTTONRELEASE:
4541 case EVENT_MOTIONNOTIFY:
4543 DrawBuffer *drawto_last = drawto;
4546 if (event.type == EVENT_MOTIONNOTIFY)
4551 motion_status = TRUE;
4552 mx = ((MotionEvent *) &event)->x;
4553 my = ((MotionEvent *) &event)->y;
4557 motion_status = FALSE;
4558 mx = ((ButtonEvent *) &event)->x;
4559 my = ((ButtonEvent *) &event)->y;
4560 if (event.type == EVENT_BUTTONPRESS)
4561 button_status = ((ButtonEvent *) &event)->button;
4563 button_status = MB_RELEASED;
4566 if (global.use_envelope_request)
4568 // draw changed button states to temporary bitmap
4569 drawto = bitmap_db_store_1;
4572 // this sets 'request_gadget_id'
4573 HandleGadgets(mx, my, button_status);
4575 if (global.use_envelope_request)
4577 // restore pointer to drawing buffer
4578 drawto = drawto_last;
4580 // prepare complete envelope request from temporary bitmap
4581 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy,
4585 switch (request_gadget_id)
4587 case TOOL_CTRL_ID_YES:
4588 case TOOL_CTRL_ID_TOUCH_YES:
4591 case TOOL_CTRL_ID_NO:
4592 case TOOL_CTRL_ID_TOUCH_NO:
4595 case TOOL_CTRL_ID_CONFIRM:
4596 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4597 result = TRUE | FALSE;
4600 case TOOL_CTRL_ID_PLAYER_1:
4603 case TOOL_CTRL_ID_PLAYER_2:
4606 case TOOL_CTRL_ID_PLAYER_3:
4609 case TOOL_CTRL_ID_PLAYER_4:
4614 // only check clickable animations if no request gadget clicked
4615 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4622 case SDL_WINDOWEVENT:
4623 HandleWindowEvent((WindowEvent *) &event);
4626 case SDL_APP_WILLENTERBACKGROUND:
4627 case SDL_APP_DIDENTERBACKGROUND:
4628 case SDL_APP_WILLENTERFOREGROUND:
4629 case SDL_APP_DIDENTERFOREGROUND:
4630 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4633 case EVENT_KEYPRESS:
4635 Key key = GetEventKey((KeyEvent *)&event);
4640 if (req_state & REQ_CONFIRM)
4649 #if defined(KSYM_Rewind)
4650 case KSYM_Rewind: // for Amazon Fire TV remote
4659 #if defined(KSYM_FastForward)
4660 case KSYM_FastForward: // for Amazon Fire TV remote
4666 HandleKeysDebug(key, KEY_PRESSED);
4670 if (req_state & REQ_PLAYER)
4672 int old_player_nr = setup.network_player_nr;
4675 result = old_player_nr + 1;
4680 result = old_player_nr + 1;
4711 case EVENT_FINGERRELEASE:
4712 case EVENT_KEYRELEASE:
4713 ClearPlayerAction();
4716 case SDL_CONTROLLERBUTTONDOWN:
4717 switch (event.cbutton.button)
4719 case SDL_CONTROLLER_BUTTON_A:
4720 case SDL_CONTROLLER_BUTTON_X:
4721 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4722 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4726 case SDL_CONTROLLER_BUTTON_B:
4727 case SDL_CONTROLLER_BUTTON_Y:
4728 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4729 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4730 case SDL_CONTROLLER_BUTTON_BACK:
4735 if (req_state & REQ_PLAYER)
4737 int old_player_nr = setup.network_player_nr;
4740 result = old_player_nr + 1;
4742 switch (event.cbutton.button)
4744 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4745 case SDL_CONTROLLER_BUTTON_Y:
4749 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4750 case SDL_CONTROLLER_BUTTON_B:
4754 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4755 case SDL_CONTROLLER_BUTTON_A:
4759 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4760 case SDL_CONTROLLER_BUTTON_X:
4771 case SDL_CONTROLLERBUTTONUP:
4772 HandleJoystickEvent(&event);
4773 ClearPlayerAction();
4777 HandleOtherEvents(&event);
4782 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4784 int joy = AnyJoystick();
4786 if (joy & JOY_BUTTON_1)
4788 else if (joy & JOY_BUTTON_2)
4791 else if (AnyJoystick())
4793 int joy = AnyJoystick();
4795 if (req_state & REQ_PLAYER)
4799 else if (joy & JOY_RIGHT)
4801 else if (joy & JOY_DOWN)
4803 else if (joy & JOY_LEFT)
4811 SetDrawtoField(draw_buffer_last);
4813 game.request_active = FALSE;
4818 static boolean RequestDoor(char *text, unsigned int req_state)
4820 int draw_buffer_last = GetDrawtoField();
4821 unsigned int old_door_state;
4822 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4823 int font_nr = FONT_TEXT_2;
4828 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4830 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4831 font_nr = FONT_TEXT_1;
4834 if (game_status == GAME_MODE_PLAYING)
4835 BlitScreenToBitmap(backbuffer);
4837 // disable deactivated drawing when quick-loading level tape recording
4838 if (tape.playing && tape.deactivate_display)
4839 TapeDeactivateDisplayOff(TRUE);
4841 SetMouseCursor(CURSOR_DEFAULT);
4843 // pause network game while waiting for request to answer
4844 if (network.enabled &&
4845 game_status == GAME_MODE_PLAYING &&
4846 !game.all_players_gone &&
4847 req_state & REQUEST_WAIT_FOR_INPUT)
4848 SendToServer_PausePlaying();
4850 old_door_state = GetDoorState();
4852 // simulate releasing mouse button over last gadget, if still pressed
4854 HandleGadgets(-1, -1, 0);
4858 // draw released gadget before proceeding
4861 if (old_door_state & DOOR_OPEN_1)
4863 CloseDoor(DOOR_CLOSE_1);
4865 // save old door content
4866 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4867 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4870 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4871 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4873 // clear door drawing field
4874 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4876 // force DOOR font inside door area
4877 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4879 // write text for request
4880 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4882 char text_line[max_request_line_len + 1];
4888 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4890 tc = *(text_ptr + tx);
4891 // if (!tc || tc == ' ')
4892 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4896 if ((tc == '?' || tc == '!') && tl == 0)
4906 strncpy(text_line, text_ptr, tl);
4909 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4910 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4911 text_line, font_nr);
4913 text_ptr += tl + (tc == ' ' ? 1 : 0);
4914 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4919 if (req_state & REQ_ASK)
4921 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4922 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4923 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4924 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4926 else if (req_state & REQ_CONFIRM)
4928 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4929 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4931 else if (req_state & REQ_PLAYER)
4933 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4934 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4935 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4936 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4939 // copy request gadgets to door backbuffer
4940 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4942 OpenDoor(DOOR_OPEN_1);
4944 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4946 if (game_status == GAME_MODE_PLAYING)
4948 SetPanelBackground();
4949 SetDrawBackgroundMask(REDRAW_DOOR_1);
4953 SetDrawBackgroundMask(REDRAW_FIELD);
4959 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4961 // ---------- handle request buttons ----------
4962 result = RequestHandleEvents(req_state, draw_buffer_last);
4966 if (!(req_state & REQ_STAY_OPEN))
4968 CloseDoor(DOOR_CLOSE_1);
4970 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4971 (req_state & REQ_REOPEN))
4972 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4977 if (game_status == GAME_MODE_PLAYING)
4979 SetPanelBackground();
4980 SetDrawBackgroundMask(REDRAW_DOOR_1);
4984 SetDrawBackgroundMask(REDRAW_FIELD);
4987 // continue network game after request
4988 if (network.enabled &&
4989 game_status == GAME_MODE_PLAYING &&
4990 !game.all_players_gone &&
4991 req_state & REQUEST_WAIT_FOR_INPUT)
4992 SendToServer_ContinuePlaying();
4994 // restore deactivated drawing when quick-loading level tape recording
4995 if (tape.playing && tape.deactivate_display)
4996 TapeDeactivateDisplayOn();
5001 static boolean RequestEnvelope(char *text, unsigned int req_state)
5003 int draw_buffer_last = GetDrawtoField();
5006 if (game_status == GAME_MODE_PLAYING)
5007 BlitScreenToBitmap(backbuffer);
5009 // disable deactivated drawing when quick-loading level tape recording
5010 if (tape.playing && tape.deactivate_display)
5011 TapeDeactivateDisplayOff(TRUE);
5013 SetMouseCursor(CURSOR_DEFAULT);
5015 // pause network game while waiting for request to answer
5016 if (network.enabled &&
5017 game_status == GAME_MODE_PLAYING &&
5018 !game.all_players_gone &&
5019 req_state & REQUEST_WAIT_FOR_INPUT)
5020 SendToServer_PausePlaying();
5022 // simulate releasing mouse button over last gadget, if still pressed
5024 HandleGadgets(-1, -1, 0);
5028 // (replace with setting corresponding request background)
5029 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5030 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5032 // clear door drawing field
5033 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5035 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5037 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5039 if (game_status == GAME_MODE_PLAYING)
5041 SetPanelBackground();
5042 SetDrawBackgroundMask(REDRAW_DOOR_1);
5046 SetDrawBackgroundMask(REDRAW_FIELD);
5052 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5054 // ---------- handle request buttons ----------
5055 result = RequestHandleEvents(req_state, draw_buffer_last);
5059 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5063 if (game_status == GAME_MODE_PLAYING)
5065 SetPanelBackground();
5066 SetDrawBackgroundMask(REDRAW_DOOR_1);
5070 SetDrawBackgroundMask(REDRAW_FIELD);
5073 // continue network game after request
5074 if (network.enabled &&
5075 game_status == GAME_MODE_PLAYING &&
5076 !game.all_players_gone &&
5077 req_state & REQUEST_WAIT_FOR_INPUT)
5078 SendToServer_ContinuePlaying();
5080 // restore deactivated drawing when quick-loading level tape recording
5081 if (tape.playing && tape.deactivate_display)
5082 TapeDeactivateDisplayOn();
5087 boolean Request(char *text, unsigned int req_state)
5089 boolean overlay_enabled = GetOverlayEnabled();
5092 game.request_active_or_moving = TRUE;
5094 SetOverlayEnabled(FALSE);
5096 if (global.use_envelope_request)
5097 result = RequestEnvelope(text, req_state);
5099 result = RequestDoor(text, req_state);
5101 SetOverlayEnabled(overlay_enabled);
5103 game.request_active_or_moving = FALSE;
5108 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5110 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5111 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5114 if (dpo1->sort_priority != dpo2->sort_priority)
5115 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5117 compare_result = dpo1->nr - dpo2->nr;
5119 return compare_result;
5122 void InitGraphicCompatibilityInfo_Doors(void)
5128 struct DoorInfo *door;
5132 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5133 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5135 { -1, -1, -1, NULL }
5137 struct Rect door_rect_list[] =
5139 { DX, DY, DXSIZE, DYSIZE },
5140 { VX, VY, VXSIZE, VYSIZE }
5144 for (i = 0; doors[i].door_token != -1; i++)
5146 int door_token = doors[i].door_token;
5147 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5148 int part_1 = doors[i].part_1;
5149 int part_8 = doors[i].part_8;
5150 int part_2 = part_1 + 1;
5151 int part_3 = part_1 + 2;
5152 struct DoorInfo *door = doors[i].door;
5153 struct Rect *door_rect = &door_rect_list[door_index];
5154 boolean door_gfx_redefined = FALSE;
5156 // check if any door part graphic definitions have been redefined
5158 for (j = 0; door_part_controls[j].door_token != -1; j++)
5160 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5161 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5163 if (dpc->door_token == door_token && fi->redefined)
5164 door_gfx_redefined = TRUE;
5167 // check for old-style door graphic/animation modifications
5169 if (!door_gfx_redefined)
5171 if (door->anim_mode & ANIM_STATIC_PANEL)
5173 door->panel.step_xoffset = 0;
5174 door->panel.step_yoffset = 0;
5177 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5179 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5180 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5181 int num_door_steps, num_panel_steps;
5183 // remove door part graphics other than the two default wings
5185 for (j = 0; door_part_controls[j].door_token != -1; j++)
5187 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5188 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5190 if (dpc->graphic >= part_3 &&
5191 dpc->graphic <= part_8)
5195 // set graphics and screen positions of the default wings
5197 g_part_1->width = door_rect->width;
5198 g_part_1->height = door_rect->height;
5199 g_part_2->width = door_rect->width;
5200 g_part_2->height = door_rect->height;
5201 g_part_2->src_x = door_rect->width;
5202 g_part_2->src_y = g_part_1->src_y;
5204 door->part_2.x = door->part_1.x;
5205 door->part_2.y = door->part_1.y;
5207 if (door->width != -1)
5209 g_part_1->width = door->width;
5210 g_part_2->width = door->width;
5212 // special treatment for graphics and screen position of right wing
5213 g_part_2->src_x += door_rect->width - door->width;
5214 door->part_2.x += door_rect->width - door->width;
5217 if (door->height != -1)
5219 g_part_1->height = door->height;
5220 g_part_2->height = door->height;
5222 // special treatment for graphics and screen position of bottom wing
5223 g_part_2->src_y += door_rect->height - door->height;
5224 door->part_2.y += door_rect->height - door->height;
5227 // set animation delays for the default wings and panels
5229 door->part_1.step_delay = door->step_delay;
5230 door->part_2.step_delay = door->step_delay;
5231 door->panel.step_delay = door->step_delay;
5233 // set animation draw order for the default wings
5235 door->part_1.sort_priority = 2; // draw left wing over ...
5236 door->part_2.sort_priority = 1; // ... right wing
5238 // set animation draw offset for the default wings
5240 if (door->anim_mode & ANIM_HORIZONTAL)
5242 door->part_1.step_xoffset = door->step_offset;
5243 door->part_1.step_yoffset = 0;
5244 door->part_2.step_xoffset = door->step_offset * -1;
5245 door->part_2.step_yoffset = 0;
5247 num_door_steps = g_part_1->width / door->step_offset;
5249 else // ANIM_VERTICAL
5251 door->part_1.step_xoffset = 0;
5252 door->part_1.step_yoffset = door->step_offset;
5253 door->part_2.step_xoffset = 0;
5254 door->part_2.step_yoffset = door->step_offset * -1;
5256 num_door_steps = g_part_1->height / door->step_offset;
5259 // set animation draw offset for the default panels
5261 if (door->step_offset > 1)
5263 num_panel_steps = 2 * door_rect->height / door->step_offset;
5264 door->panel.start_step = num_panel_steps - num_door_steps;
5265 door->panel.start_step_closing = door->panel.start_step;
5269 num_panel_steps = door_rect->height / door->step_offset;
5270 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5271 door->panel.start_step_closing = door->panel.start_step;
5272 door->panel.step_delay *= 2;
5279 void InitDoors(void)
5283 for (i = 0; door_part_controls[i].door_token != -1; i++)
5285 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5286 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5288 // initialize "start_step_opening" and "start_step_closing", if needed
5289 if (dpc->pos->start_step_opening == 0 &&
5290 dpc->pos->start_step_closing == 0)
5292 // dpc->pos->start_step_opening = dpc->pos->start_step;
5293 dpc->pos->start_step_closing = dpc->pos->start_step;
5296 // fill structure for door part draw order (sorted below)
5298 dpo->sort_priority = dpc->pos->sort_priority;
5301 // sort door part controls according to sort_priority and graphic number
5302 qsort(door_part_order, MAX_DOOR_PARTS,
5303 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5306 unsigned int OpenDoor(unsigned int door_state)
5308 if (door_state & DOOR_COPY_BACK)
5310 if (door_state & DOOR_OPEN_1)
5311 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5312 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5314 if (door_state & DOOR_OPEN_2)
5315 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5316 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5318 door_state &= ~DOOR_COPY_BACK;
5321 return MoveDoor(door_state);
5324 unsigned int CloseDoor(unsigned int door_state)
5326 unsigned int old_door_state = GetDoorState();
5328 if (!(door_state & DOOR_NO_COPY_BACK))
5330 if (old_door_state & DOOR_OPEN_1)
5331 BlitBitmap(backbuffer, bitmap_db_door_1,
5332 DX, DY, DXSIZE, DYSIZE, 0, 0);
5334 if (old_door_state & DOOR_OPEN_2)
5335 BlitBitmap(backbuffer, bitmap_db_door_2,
5336 VX, VY, VXSIZE, VYSIZE, 0, 0);
5338 door_state &= ~DOOR_NO_COPY_BACK;
5341 return MoveDoor(door_state);
5344 unsigned int GetDoorState(void)
5346 return MoveDoor(DOOR_GET_STATE);
5349 unsigned int SetDoorState(unsigned int door_state)
5351 return MoveDoor(door_state | DOOR_SET_STATE);
5354 static int euclid(int a, int b)
5356 return (b ? euclid(b, a % b) : a);
5359 unsigned int MoveDoor(unsigned int door_state)
5361 struct Rect door_rect_list[] =
5363 { DX, DY, DXSIZE, DYSIZE },
5364 { VX, VY, VXSIZE, VYSIZE }
5366 static int door1 = DOOR_CLOSE_1;
5367 static int door2 = DOOR_CLOSE_2;
5368 DelayCounter door_delay = { 0 };
5371 if (door_state == DOOR_GET_STATE)
5372 return (door1 | door2);
5374 if (door_state & DOOR_SET_STATE)
5376 if (door_state & DOOR_ACTION_1)
5377 door1 = door_state & DOOR_ACTION_1;
5378 if (door_state & DOOR_ACTION_2)
5379 door2 = door_state & DOOR_ACTION_2;
5381 return (door1 | door2);
5384 if (!(door_state & DOOR_FORCE_REDRAW))
5386 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5387 door_state &= ~DOOR_OPEN_1;
5388 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5389 door_state &= ~DOOR_CLOSE_1;
5390 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5391 door_state &= ~DOOR_OPEN_2;
5392 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5393 door_state &= ~DOOR_CLOSE_2;
5396 if (global.autoplay_leveldir)
5398 door_state |= DOOR_NO_DELAY;
5399 door_state &= ~DOOR_CLOSE_ALL;
5402 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5403 door_state |= DOOR_NO_DELAY;
5405 if (door_state & DOOR_ACTION)
5407 boolean door_panel_drawn[NUM_DOORS];
5408 boolean panel_has_doors[NUM_DOORS];
5409 boolean door_part_skip[MAX_DOOR_PARTS];
5410 boolean door_part_done[MAX_DOOR_PARTS];
5411 boolean door_part_done_all;
5412 int num_steps[MAX_DOOR_PARTS];
5413 int max_move_delay = 0; // delay for complete animations of all doors
5414 int max_step_delay = 0; // delay (ms) between two animation frames
5415 int num_move_steps = 0; // number of animation steps for all doors
5416 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5417 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5421 for (i = 0; i < NUM_DOORS; i++)
5422 panel_has_doors[i] = FALSE;
5424 for (i = 0; i < MAX_DOOR_PARTS; i++)
5426 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5427 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5428 int door_token = dpc->door_token;
5430 door_part_done[i] = FALSE;
5431 door_part_skip[i] = (!(door_state & door_token) ||
5435 for (i = 0; i < MAX_DOOR_PARTS; i++)
5437 int nr = door_part_order[i].nr;
5438 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5439 struct DoorPartPosInfo *pos = dpc->pos;
5440 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5441 int door_token = dpc->door_token;
5442 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5443 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5444 int step_xoffset = ABS(pos->step_xoffset);
5445 int step_yoffset = ABS(pos->step_yoffset);
5446 int step_delay = pos->step_delay;
5447 int current_door_state = door_state & door_token;
5448 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5449 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5450 boolean part_opening = (is_panel ? door_closing : door_opening);
5451 int start_step = (part_opening ? pos->start_step_opening :
5452 pos->start_step_closing);
5453 float move_xsize = (step_xoffset ? g->width : 0);
5454 float move_ysize = (step_yoffset ? g->height : 0);
5455 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5456 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5457 int move_steps = (move_xsteps && move_ysteps ?
5458 MIN(move_xsteps, move_ysteps) :
5459 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5460 int move_delay = move_steps * step_delay;
5462 if (door_part_skip[nr])
5465 max_move_delay = MAX(max_move_delay, move_delay);
5466 max_step_delay = (max_step_delay == 0 ? step_delay :
5467 euclid(max_step_delay, step_delay));
5468 num_steps[nr] = move_steps;
5472 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5474 panel_has_doors[door_index] = TRUE;
5478 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5480 num_move_steps = max_move_delay / max_step_delay;
5481 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5483 door_delay.value = max_step_delay;
5485 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5487 start = num_move_steps - 1;
5491 // opening door sound has priority over simultaneously closing door
5492 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5494 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5496 if (door_state & DOOR_OPEN_1)
5497 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5498 if (door_state & DOOR_OPEN_2)
5499 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5501 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5503 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5505 if (door_state & DOOR_CLOSE_1)
5506 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5507 if (door_state & DOOR_CLOSE_2)
5508 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5512 for (k = start; k < num_move_steps; k++)
5514 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5516 door_part_done_all = TRUE;
5518 for (i = 0; i < NUM_DOORS; i++)
5519 door_panel_drawn[i] = FALSE;
5521 for (i = 0; i < MAX_DOOR_PARTS; i++)
5523 int nr = door_part_order[i].nr;
5524 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5525 struct DoorPartPosInfo *pos = dpc->pos;
5526 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5527 int door_token = dpc->door_token;
5528 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5529 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5530 boolean is_panel_and_door_has_closed = FALSE;
5531 struct Rect *door_rect = &door_rect_list[door_index];
5532 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5534 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5535 int current_door_state = door_state & door_token;
5536 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5537 boolean door_closing = !door_opening;
5538 boolean part_opening = (is_panel ? door_closing : door_opening);
5539 boolean part_closing = !part_opening;
5540 int start_step = (part_opening ? pos->start_step_opening :
5541 pos->start_step_closing);
5542 int step_delay = pos->step_delay;
5543 int step_factor = step_delay / max_step_delay;
5544 int k1 = (step_factor ? k / step_factor + 1 : k);
5545 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5546 int kk = MAX(0, k2);
5549 int src_x, src_y, src_xx, src_yy;
5550 int dst_x, dst_y, dst_xx, dst_yy;
5553 if (door_part_skip[nr])
5556 if (!(door_state & door_token))
5564 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5565 int kk_door = MAX(0, k2_door);
5566 int sync_frame = kk_door * door_delay.value;
5567 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5569 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5570 &g_src_x, &g_src_y);
5575 if (!door_panel_drawn[door_index])
5577 ClearRectangle(drawto, door_rect->x, door_rect->y,
5578 door_rect->width, door_rect->height);
5580 door_panel_drawn[door_index] = TRUE;
5583 // draw opening or closing door parts
5585 if (pos->step_xoffset < 0) // door part on right side
5588 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5591 if (dst_xx + width > door_rect->width)
5592 width = door_rect->width - dst_xx;
5594 else // door part on left side
5597 dst_xx = pos->x - kk * pos->step_xoffset;
5601 src_xx = ABS(dst_xx);
5605 width = g->width - src_xx;
5607 if (width > door_rect->width)
5608 width = door_rect->width;
5610 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5613 if (pos->step_yoffset < 0) // door part on bottom side
5616 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5619 if (dst_yy + height > door_rect->height)
5620 height = door_rect->height - dst_yy;
5622 else // door part on top side
5625 dst_yy = pos->y - kk * pos->step_yoffset;
5629 src_yy = ABS(dst_yy);
5633 height = g->height - src_yy;
5636 src_x = g_src_x + src_xx;
5637 src_y = g_src_y + src_yy;
5639 dst_x = door_rect->x + dst_xx;
5640 dst_y = door_rect->y + dst_yy;
5642 is_panel_and_door_has_closed =
5645 panel_has_doors[door_index] &&
5646 k >= num_move_steps_doors_only - 1);
5648 if (width >= 0 && width <= g->width &&
5649 height >= 0 && height <= g->height &&
5650 !is_panel_and_door_has_closed)
5652 if (is_panel || !pos->draw_masked)
5653 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5656 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5660 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5662 if ((part_opening && (width < 0 || height < 0)) ||
5663 (part_closing && (width >= g->width && height >= g->height)))
5664 door_part_done[nr] = TRUE;
5666 // continue door part animations, but not panel after door has closed
5667 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5668 door_part_done_all = FALSE;
5671 if (!(door_state & DOOR_NO_DELAY))
5675 SkipUntilDelayReached(&door_delay, &k, last_frame);
5677 // prevent OS (Windows) from complaining about program not responding
5681 if (door_part_done_all)
5685 if (!(door_state & DOOR_NO_DELAY))
5687 // wait for specified door action post delay
5688 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5689 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5690 else if (door_state & DOOR_ACTION_1)
5691 door_delay.value = door_1.post_delay;
5692 else if (door_state & DOOR_ACTION_2)
5693 door_delay.value = door_2.post_delay;
5695 while (!DelayReached(&door_delay))
5700 if (door_state & DOOR_ACTION_1)
5701 door1 = door_state & DOOR_ACTION_1;
5702 if (door_state & DOOR_ACTION_2)
5703 door2 = door_state & DOOR_ACTION_2;
5705 // draw masked border over door area
5706 DrawMaskedBorder(REDRAW_DOOR_1);
5707 DrawMaskedBorder(REDRAW_DOOR_2);
5709 ClearAutoRepeatKeyEvents();
5711 return (door1 | door2);
5714 static boolean useSpecialEditorDoor(void)
5716 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5717 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5719 // do not draw special editor door if editor border defined or redefined
5720 if (graphic_info[graphic].bitmap != NULL || redefined)
5723 // do not draw special editor door if global border defined to be empty
5724 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5727 // do not draw special editor door if viewport definitions do not match
5731 EY + EYSIZE != VY + VYSIZE)
5737 void DrawSpecialEditorDoor(void)
5739 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5740 int top_border_width = gfx1->width;
5741 int top_border_height = gfx1->height;
5742 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5743 int ex = EX - outer_border;
5744 int ey = EY - outer_border;
5745 int vy = VY - outer_border;
5746 int exsize = EXSIZE + 2 * outer_border;
5748 if (!useSpecialEditorDoor())
5751 // draw bigger level editor toolbox window
5752 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5753 top_border_width, top_border_height, ex, ey - top_border_height);
5754 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5755 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5757 redraw_mask |= REDRAW_ALL;
5760 void UndrawSpecialEditorDoor(void)
5762 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5763 int top_border_width = gfx1->width;
5764 int top_border_height = gfx1->height;
5765 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5766 int ex = EX - outer_border;
5767 int ey = EY - outer_border;
5768 int ey_top = ey - top_border_height;
5769 int exsize = EXSIZE + 2 * outer_border;
5770 int eysize = EYSIZE + 2 * outer_border;
5772 if (!useSpecialEditorDoor())
5775 // draw normal tape recorder window
5776 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5778 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5779 ex, ey_top, top_border_width, top_border_height,
5781 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5782 ex, ey, exsize, eysize, ex, ey);
5786 // if screen background is set to "[NONE]", clear editor toolbox window
5787 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5788 ClearRectangle(drawto, ex, ey, exsize, eysize);
5791 redraw_mask |= REDRAW_ALL;
5795 // ---------- new tool button stuff -------------------------------------------
5800 struct TextPosInfo *pos;
5802 boolean is_touch_button;
5804 } toolbutton_info[NUM_TOOL_BUTTONS] =
5807 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5808 TOOL_CTRL_ID_YES, FALSE, "yes"
5811 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5812 TOOL_CTRL_ID_NO, FALSE, "no"
5815 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5816 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5819 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5820 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5823 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5824 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5827 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5828 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5831 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5832 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5835 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5836 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5839 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5840 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5843 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5844 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5848 void CreateToolButtons(void)
5852 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5854 int graphic = toolbutton_info[i].graphic;
5855 struct GraphicInfo *gfx = &graphic_info[graphic];
5856 struct TextPosInfo *pos = toolbutton_info[i].pos;
5857 struct GadgetInfo *gi;
5858 Bitmap *deco_bitmap = None;
5859 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5860 unsigned int event_mask = GD_EVENT_RELEASED;
5861 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5862 int base_x = (is_touch_button ? 0 : DX);
5863 int base_y = (is_touch_button ? 0 : DY);
5864 int gd_x = gfx->src_x;
5865 int gd_y = gfx->src_y;
5866 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5867 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5872 // do not use touch buttons if overlay touch buttons are disabled
5873 if (is_touch_button && !setup.touch.overlay_buttons)
5876 if (global.use_envelope_request && !is_touch_button)
5878 setRequestPosition(&base_x, &base_y, TRUE);
5880 // check if request buttons are outside of envelope and fix, if needed
5881 if (x < 0 || x + gfx->width > request.width ||
5882 y < 0 || y + gfx->height > request.height)
5884 if (id == TOOL_CTRL_ID_YES)
5887 y = request.height - 2 * request.border_size - gfx->height;
5889 else if (id == TOOL_CTRL_ID_NO)
5891 x = request.width - 2 * request.border_size - gfx->width;
5892 y = request.height - 2 * request.border_size - gfx->height;
5894 else if (id == TOOL_CTRL_ID_CONFIRM)
5896 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5897 y = request.height - 2 * request.border_size - gfx->height;
5899 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5901 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5903 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5904 y = request.height - 2 * request.border_size - gfx->height * 2;
5906 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5907 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5912 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5915 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5917 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5918 pos->size, &deco_bitmap, &deco_x, &deco_y);
5919 deco_xpos = (gfx->width - pos->size) / 2;
5920 deco_ypos = (gfx->height - pos->size) / 2;
5923 gi = CreateGadget(GDI_CUSTOM_ID, id,
5924 GDI_IMAGE_ID, graphic,
5925 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5928 GDI_WIDTH, gfx->width,
5929 GDI_HEIGHT, gfx->height,
5930 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5931 GDI_STATE, GD_BUTTON_UNPRESSED,
5932 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5933 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5934 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5935 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5936 GDI_DECORATION_SIZE, pos->size, pos->size,
5937 GDI_DECORATION_SHIFTING, 1, 1,
5938 GDI_DIRECT_DRAW, FALSE,
5939 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5940 GDI_EVENT_MASK, event_mask,
5941 GDI_CALLBACK_ACTION, HandleToolButtons,
5945 Fail("cannot create gadget");
5947 tool_gadget[id] = gi;
5951 void FreeToolButtons(void)
5955 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5956 FreeGadget(tool_gadget[i]);
5959 static void UnmapToolButtons(void)
5963 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5964 UnmapGadget(tool_gadget[i]);
5967 static void HandleToolButtons(struct GadgetInfo *gi)
5969 request_gadget_id = gi->custom_id;
5972 static struct Mapping_EM_to_RND_object
5975 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5976 boolean is_backside; // backside of moving element
5982 em_object_mapping_list[GAME_TILE_MAX + 1] =
5985 Zborder, FALSE, FALSE,
5989 Zplayer, FALSE, FALSE,
5998 Ztank, FALSE, FALSE,
6002 Zeater, FALSE, FALSE,
6006 Zdynamite, FALSE, FALSE,
6010 Zboom, FALSE, FALSE,
6015 Xchain, FALSE, FALSE,
6016 EL_DEFAULT, ACTION_EXPLODING, -1
6019 Xboom_bug, FALSE, FALSE,
6020 EL_BUG, ACTION_EXPLODING, -1
6023 Xboom_tank, FALSE, FALSE,
6024 EL_SPACESHIP, ACTION_EXPLODING, -1
6027 Xboom_android, FALSE, FALSE,
6028 EL_EMC_ANDROID, ACTION_OTHER, -1
6031 Xboom_1, FALSE, FALSE,
6032 EL_DEFAULT, ACTION_EXPLODING, -1
6035 Xboom_2, FALSE, FALSE,
6036 EL_DEFAULT, ACTION_EXPLODING, -1
6040 Xblank, TRUE, FALSE,
6045 Xsplash_e, FALSE, FALSE,
6046 EL_ACID_SPLASH_RIGHT, -1, -1
6049 Xsplash_w, FALSE, FALSE,
6050 EL_ACID_SPLASH_LEFT, -1, -1
6054 Xplant, TRUE, FALSE,
6055 EL_EMC_PLANT, -1, -1
6058 Yplant, FALSE, FALSE,
6059 EL_EMC_PLANT, -1, -1
6063 Xacid_1, TRUE, FALSE,
6067 Xacid_2, FALSE, FALSE,
6071 Xacid_3, FALSE, FALSE,
6075 Xacid_4, FALSE, FALSE,
6079 Xacid_5, FALSE, FALSE,
6083 Xacid_6, FALSE, FALSE,
6087 Xacid_7, FALSE, FALSE,
6091 Xacid_8, FALSE, FALSE,
6096 Xfake_acid_1, TRUE, FALSE,
6097 EL_EMC_FAKE_ACID, -1, -1
6100 Xfake_acid_2, FALSE, FALSE,
6101 EL_EMC_FAKE_ACID, -1, -1
6104 Xfake_acid_3, FALSE, FALSE,
6105 EL_EMC_FAKE_ACID, -1, -1
6108 Xfake_acid_4, FALSE, FALSE,
6109 EL_EMC_FAKE_ACID, -1, -1
6112 Xfake_acid_5, FALSE, FALSE,
6113 EL_EMC_FAKE_ACID, -1, -1
6116 Xfake_acid_6, FALSE, FALSE,
6117 EL_EMC_FAKE_ACID, -1, -1
6120 Xfake_acid_7, FALSE, FALSE,
6121 EL_EMC_FAKE_ACID, -1, -1
6124 Xfake_acid_8, FALSE, FALSE,
6125 EL_EMC_FAKE_ACID, -1, -1
6129 Xfake_acid_1_player, FALSE, FALSE,
6130 EL_EMC_FAKE_ACID, -1, -1
6133 Xfake_acid_2_player, FALSE, FALSE,
6134 EL_EMC_FAKE_ACID, -1, -1
6137 Xfake_acid_3_player, FALSE, FALSE,
6138 EL_EMC_FAKE_ACID, -1, -1
6141 Xfake_acid_4_player, FALSE, FALSE,
6142 EL_EMC_FAKE_ACID, -1, -1
6145 Xfake_acid_5_player, FALSE, FALSE,
6146 EL_EMC_FAKE_ACID, -1, -1
6149 Xfake_acid_6_player, FALSE, FALSE,
6150 EL_EMC_FAKE_ACID, -1, -1
6153 Xfake_acid_7_player, FALSE, FALSE,
6154 EL_EMC_FAKE_ACID, -1, -1
6157 Xfake_acid_8_player, FALSE, FALSE,
6158 EL_EMC_FAKE_ACID, -1, -1
6162 Xgrass, TRUE, FALSE,
6163 EL_EMC_GRASS, -1, -1
6166 Ygrass_nB, FALSE, FALSE,
6167 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6170 Ygrass_eB, FALSE, FALSE,
6171 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6174 Ygrass_sB, FALSE, FALSE,
6175 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6178 Ygrass_wB, FALSE, FALSE,
6179 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6187 Ydirt_nB, FALSE, FALSE,
6188 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6191 Ydirt_eB, FALSE, FALSE,
6192 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6195 Ydirt_sB, FALSE, FALSE,
6196 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6199 Ydirt_wB, FALSE, FALSE,
6200 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6204 Xandroid, TRUE, FALSE,
6205 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6208 Xandroid_1_n, FALSE, FALSE,
6209 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6212 Xandroid_2_n, FALSE, FALSE,
6213 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6216 Xandroid_1_e, FALSE, FALSE,
6217 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6220 Xandroid_2_e, FALSE, FALSE,
6221 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6224 Xandroid_1_w, FALSE, FALSE,
6225 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6228 Xandroid_2_w, FALSE, FALSE,
6229 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6232 Xandroid_1_s, FALSE, FALSE,
6233 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6236 Xandroid_2_s, FALSE, FALSE,
6237 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6240 Yandroid_n, FALSE, FALSE,
6241 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6244 Yandroid_nB, FALSE, TRUE,
6245 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6248 Yandroid_ne, FALSE, FALSE,
6249 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6252 Yandroid_neB, FALSE, TRUE,
6253 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6256 Yandroid_e, FALSE, FALSE,
6257 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6260 Yandroid_eB, FALSE, TRUE,
6261 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6264 Yandroid_se, FALSE, FALSE,
6265 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6268 Yandroid_seB, FALSE, TRUE,
6269 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6272 Yandroid_s, FALSE, FALSE,
6273 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6276 Yandroid_sB, FALSE, TRUE,
6277 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6280 Yandroid_sw, FALSE, FALSE,
6281 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6284 Yandroid_swB, FALSE, TRUE,
6285 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6288 Yandroid_w, FALSE, FALSE,
6289 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6292 Yandroid_wB, FALSE, TRUE,
6293 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6296 Yandroid_nw, FALSE, FALSE,
6297 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6300 Yandroid_nwB, FALSE, TRUE,
6301 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6305 Xeater_n, TRUE, FALSE,
6306 EL_YAMYAM_UP, -1, -1
6309 Xeater_e, TRUE, FALSE,
6310 EL_YAMYAM_RIGHT, -1, -1
6313 Xeater_w, TRUE, FALSE,
6314 EL_YAMYAM_LEFT, -1, -1
6317 Xeater_s, TRUE, FALSE,
6318 EL_YAMYAM_DOWN, -1, -1
6321 Yeater_n, FALSE, FALSE,
6322 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6325 Yeater_nB, FALSE, TRUE,
6326 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6329 Yeater_e, FALSE, FALSE,
6330 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6333 Yeater_eB, FALSE, TRUE,
6334 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6337 Yeater_s, FALSE, FALSE,
6338 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6341 Yeater_sB, FALSE, TRUE,
6342 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6345 Yeater_w, FALSE, FALSE,
6346 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6349 Yeater_wB, FALSE, TRUE,
6350 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6353 Yeater_stone, FALSE, FALSE,
6354 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6357 Yeater_spring, FALSE, FALSE,
6358 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6362 Xalien, TRUE, FALSE,
6366 Xalien_pause, FALSE, FALSE,
6370 Yalien_n, FALSE, FALSE,
6371 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6374 Yalien_nB, FALSE, TRUE,
6375 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6378 Yalien_e, FALSE, FALSE,
6379 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6382 Yalien_eB, FALSE, TRUE,
6383 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6386 Yalien_s, FALSE, FALSE,
6387 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6390 Yalien_sB, FALSE, TRUE,
6391 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6394 Yalien_w, FALSE, FALSE,
6395 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6398 Yalien_wB, FALSE, TRUE,
6399 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6402 Yalien_stone, FALSE, FALSE,
6403 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6406 Yalien_spring, FALSE, FALSE,
6407 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6411 Xbug_1_n, TRUE, FALSE,
6415 Xbug_1_e, TRUE, FALSE,
6416 EL_BUG_RIGHT, -1, -1
6419 Xbug_1_s, TRUE, FALSE,
6423 Xbug_1_w, TRUE, FALSE,
6427 Xbug_2_n, FALSE, FALSE,
6431 Xbug_2_e, FALSE, FALSE,
6432 EL_BUG_RIGHT, -1, -1
6435 Xbug_2_s, FALSE, FALSE,
6439 Xbug_2_w, FALSE, FALSE,
6443 Ybug_n, FALSE, FALSE,
6444 EL_BUG, ACTION_MOVING, MV_BIT_UP
6447 Ybug_nB, FALSE, TRUE,
6448 EL_BUG, ACTION_MOVING, MV_BIT_UP
6451 Ybug_e, FALSE, FALSE,
6452 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6455 Ybug_eB, FALSE, TRUE,
6456 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6459 Ybug_s, FALSE, FALSE,
6460 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6463 Ybug_sB, FALSE, TRUE,
6464 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6467 Ybug_w, FALSE, FALSE,
6468 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6471 Ybug_wB, FALSE, TRUE,
6472 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6475 Ybug_w_n, FALSE, FALSE,
6476 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6479 Ybug_n_e, FALSE, FALSE,
6480 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6483 Ybug_e_s, FALSE, FALSE,
6484 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6487 Ybug_s_w, FALSE, FALSE,
6488 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6491 Ybug_e_n, FALSE, FALSE,
6492 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6495 Ybug_s_e, FALSE, FALSE,
6496 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6499 Ybug_w_s, FALSE, FALSE,
6500 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6503 Ybug_n_w, FALSE, FALSE,
6504 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6507 Ybug_stone, FALSE, FALSE,
6508 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6511 Ybug_spring, FALSE, FALSE,
6512 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6516 Xtank_1_n, TRUE, FALSE,
6517 EL_SPACESHIP_UP, -1, -1
6520 Xtank_1_e, TRUE, FALSE,
6521 EL_SPACESHIP_RIGHT, -1, -1
6524 Xtank_1_s, TRUE, FALSE,
6525 EL_SPACESHIP_DOWN, -1, -1
6528 Xtank_1_w, TRUE, FALSE,
6529 EL_SPACESHIP_LEFT, -1, -1
6532 Xtank_2_n, FALSE, FALSE,
6533 EL_SPACESHIP_UP, -1, -1
6536 Xtank_2_e, FALSE, FALSE,
6537 EL_SPACESHIP_RIGHT, -1, -1
6540 Xtank_2_s, FALSE, FALSE,
6541 EL_SPACESHIP_DOWN, -1, -1
6544 Xtank_2_w, FALSE, FALSE,
6545 EL_SPACESHIP_LEFT, -1, -1
6548 Ytank_n, FALSE, FALSE,
6549 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6552 Ytank_nB, FALSE, TRUE,
6553 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6556 Ytank_e, FALSE, FALSE,
6557 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6560 Ytank_eB, FALSE, TRUE,
6561 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6564 Ytank_s, FALSE, FALSE,
6565 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6568 Ytank_sB, FALSE, TRUE,
6569 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6572 Ytank_w, FALSE, FALSE,
6573 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6576 Ytank_wB, FALSE, TRUE,
6577 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6580 Ytank_w_n, FALSE, FALSE,
6581 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6584 Ytank_n_e, FALSE, FALSE,
6585 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6588 Ytank_e_s, FALSE, FALSE,
6589 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6592 Ytank_s_w, FALSE, FALSE,
6593 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6596 Ytank_e_n, FALSE, FALSE,
6597 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6600 Ytank_s_e, FALSE, FALSE,
6601 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6604 Ytank_w_s, FALSE, FALSE,
6605 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6608 Ytank_n_w, FALSE, FALSE,
6609 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6612 Ytank_stone, FALSE, FALSE,
6613 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6616 Ytank_spring, FALSE, FALSE,
6617 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6621 Xemerald, TRUE, FALSE,
6625 Xemerald_pause, FALSE, FALSE,
6629 Xemerald_fall, FALSE, FALSE,
6633 Xemerald_shine, FALSE, FALSE,
6634 EL_EMERALD, ACTION_TWINKLING, -1
6637 Yemerald_s, FALSE, FALSE,
6638 EL_EMERALD, ACTION_FALLING, -1
6641 Yemerald_sB, FALSE, TRUE,
6642 EL_EMERALD, ACTION_FALLING, -1
6645 Yemerald_e, FALSE, FALSE,
6646 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6649 Yemerald_eB, FALSE, TRUE,
6650 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6653 Yemerald_w, FALSE, FALSE,
6654 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6657 Yemerald_wB, FALSE, TRUE,
6658 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6661 Yemerald_blank, FALSE, FALSE,
6662 EL_EMERALD, ACTION_COLLECTING, -1
6666 Xdiamond, TRUE, FALSE,
6670 Xdiamond_pause, FALSE, FALSE,
6674 Xdiamond_fall, FALSE, FALSE,
6678 Xdiamond_shine, FALSE, FALSE,
6679 EL_DIAMOND, ACTION_TWINKLING, -1
6682 Ydiamond_s, FALSE, FALSE,
6683 EL_DIAMOND, ACTION_FALLING, -1
6686 Ydiamond_sB, FALSE, TRUE,
6687 EL_DIAMOND, ACTION_FALLING, -1
6690 Ydiamond_e, FALSE, FALSE,
6691 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6694 Ydiamond_eB, FALSE, TRUE,
6695 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6698 Ydiamond_w, FALSE, FALSE,
6699 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6702 Ydiamond_wB, FALSE, TRUE,
6703 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6706 Ydiamond_blank, FALSE, FALSE,
6707 EL_DIAMOND, ACTION_COLLECTING, -1
6710 Ydiamond_stone, FALSE, FALSE,
6711 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6715 Xstone, TRUE, FALSE,
6719 Xstone_pause, FALSE, FALSE,
6723 Xstone_fall, FALSE, FALSE,
6727 Ystone_s, FALSE, FALSE,
6728 EL_ROCK, ACTION_FALLING, -1
6731 Ystone_sB, FALSE, TRUE,
6732 EL_ROCK, ACTION_FALLING, -1
6735 Ystone_e, FALSE, FALSE,
6736 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6739 Ystone_eB, FALSE, TRUE,
6740 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6743 Ystone_w, FALSE, FALSE,
6744 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6747 Ystone_wB, FALSE, TRUE,
6748 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6756 Xbomb_pause, FALSE, FALSE,
6760 Xbomb_fall, FALSE, FALSE,
6764 Ybomb_s, FALSE, FALSE,
6765 EL_BOMB, ACTION_FALLING, -1
6768 Ybomb_sB, FALSE, TRUE,
6769 EL_BOMB, ACTION_FALLING, -1
6772 Ybomb_e, FALSE, FALSE,
6773 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6776 Ybomb_eB, FALSE, TRUE,
6777 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6780 Ybomb_w, FALSE, FALSE,
6781 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6784 Ybomb_wB, FALSE, TRUE,
6785 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6788 Ybomb_blank, FALSE, FALSE,
6789 EL_BOMB, ACTION_ACTIVATING, -1
6797 Xnut_pause, FALSE, FALSE,
6801 Xnut_fall, FALSE, FALSE,
6805 Ynut_s, FALSE, FALSE,
6806 EL_NUT, ACTION_FALLING, -1
6809 Ynut_sB, FALSE, TRUE,
6810 EL_NUT, ACTION_FALLING, -1
6813 Ynut_e, FALSE, FALSE,
6814 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6817 Ynut_eB, FALSE, TRUE,
6818 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6821 Ynut_w, FALSE, FALSE,
6822 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6825 Ynut_wB, FALSE, TRUE,
6826 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6829 Ynut_stone, FALSE, FALSE,
6830 EL_NUT, ACTION_BREAKING, -1
6834 Xspring, TRUE, FALSE,
6838 Xspring_pause, FALSE, FALSE,
6842 Xspring_e, TRUE, FALSE,
6843 EL_SPRING_RIGHT, -1, -1
6846 Xspring_w, TRUE, FALSE,
6847 EL_SPRING_LEFT, -1, -1
6850 Xspring_fall, FALSE, FALSE,
6854 Yspring_s, FALSE, FALSE,
6855 EL_SPRING, ACTION_FALLING, -1
6858 Yspring_sB, FALSE, TRUE,
6859 EL_SPRING, ACTION_FALLING, -1
6862 Yspring_e, FALSE, FALSE,
6863 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6866 Yspring_eB, FALSE, TRUE,
6867 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6870 Yspring_w, FALSE, FALSE,
6871 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6874 Yspring_wB, FALSE, TRUE,
6875 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6878 Yspring_alien_e, FALSE, FALSE,
6879 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6882 Yspring_alien_eB, FALSE, TRUE,
6883 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6886 Yspring_alien_w, FALSE, FALSE,
6887 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6890 Yspring_alien_wB, FALSE, TRUE,
6891 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6895 Xpush_emerald_e, FALSE, FALSE,
6896 EL_EMERALD, -1, MV_BIT_RIGHT
6899 Xpush_emerald_w, FALSE, FALSE,
6900 EL_EMERALD, -1, MV_BIT_LEFT
6903 Xpush_diamond_e, FALSE, FALSE,
6904 EL_DIAMOND, -1, MV_BIT_RIGHT
6907 Xpush_diamond_w, FALSE, FALSE,
6908 EL_DIAMOND, -1, MV_BIT_LEFT
6911 Xpush_stone_e, FALSE, FALSE,
6912 EL_ROCK, -1, MV_BIT_RIGHT
6915 Xpush_stone_w, FALSE, FALSE,
6916 EL_ROCK, -1, MV_BIT_LEFT
6919 Xpush_bomb_e, FALSE, FALSE,
6920 EL_BOMB, -1, MV_BIT_RIGHT
6923 Xpush_bomb_w, FALSE, FALSE,
6924 EL_BOMB, -1, MV_BIT_LEFT
6927 Xpush_nut_e, FALSE, FALSE,
6928 EL_NUT, -1, MV_BIT_RIGHT
6931 Xpush_nut_w, FALSE, FALSE,
6932 EL_NUT, -1, MV_BIT_LEFT
6935 Xpush_spring_e, FALSE, FALSE,
6936 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6939 Xpush_spring_w, FALSE, FALSE,
6940 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6944 Xdynamite, TRUE, FALSE,
6945 EL_EM_DYNAMITE, -1, -1
6948 Ydynamite_blank, FALSE, FALSE,
6949 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6952 Xdynamite_1, TRUE, FALSE,
6953 EL_EM_DYNAMITE_ACTIVE, -1, -1
6956 Xdynamite_2, FALSE, FALSE,
6957 EL_EM_DYNAMITE_ACTIVE, -1, -1
6960 Xdynamite_3, FALSE, FALSE,
6961 EL_EM_DYNAMITE_ACTIVE, -1, -1
6964 Xdynamite_4, FALSE, FALSE,
6965 EL_EM_DYNAMITE_ACTIVE, -1, -1
6969 Xkey_1, TRUE, FALSE,
6973 Xkey_2, TRUE, FALSE,
6977 Xkey_3, TRUE, FALSE,
6981 Xkey_4, TRUE, FALSE,
6985 Xkey_5, TRUE, FALSE,
6986 EL_EMC_KEY_5, -1, -1
6989 Xkey_6, TRUE, FALSE,
6990 EL_EMC_KEY_6, -1, -1
6993 Xkey_7, TRUE, FALSE,
6994 EL_EMC_KEY_7, -1, -1
6997 Xkey_8, TRUE, FALSE,
6998 EL_EMC_KEY_8, -1, -1
7002 Xdoor_1, TRUE, FALSE,
7003 EL_EM_GATE_1, -1, -1
7006 Xdoor_2, TRUE, FALSE,
7007 EL_EM_GATE_2, -1, -1
7010 Xdoor_3, TRUE, FALSE,
7011 EL_EM_GATE_3, -1, -1
7014 Xdoor_4, TRUE, FALSE,
7015 EL_EM_GATE_4, -1, -1
7018 Xdoor_5, TRUE, FALSE,
7019 EL_EMC_GATE_5, -1, -1
7022 Xdoor_6, TRUE, FALSE,
7023 EL_EMC_GATE_6, -1, -1
7026 Xdoor_7, TRUE, FALSE,
7027 EL_EMC_GATE_7, -1, -1
7030 Xdoor_8, TRUE, FALSE,
7031 EL_EMC_GATE_8, -1, -1
7035 Xfake_door_1, TRUE, FALSE,
7036 EL_EM_GATE_1_GRAY, -1, -1
7039 Xfake_door_2, TRUE, FALSE,
7040 EL_EM_GATE_2_GRAY, -1, -1
7043 Xfake_door_3, TRUE, FALSE,
7044 EL_EM_GATE_3_GRAY, -1, -1
7047 Xfake_door_4, TRUE, FALSE,
7048 EL_EM_GATE_4_GRAY, -1, -1
7051 Xfake_door_5, TRUE, FALSE,
7052 EL_EMC_GATE_5_GRAY, -1, -1
7055 Xfake_door_6, TRUE, FALSE,
7056 EL_EMC_GATE_6_GRAY, -1, -1
7059 Xfake_door_7, TRUE, FALSE,
7060 EL_EMC_GATE_7_GRAY, -1, -1
7063 Xfake_door_8, TRUE, FALSE,
7064 EL_EMC_GATE_8_GRAY, -1, -1
7068 Xballoon, TRUE, FALSE,
7072 Yballoon_n, FALSE, FALSE,
7073 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7076 Yballoon_nB, FALSE, TRUE,
7077 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7080 Yballoon_e, FALSE, FALSE,
7081 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7084 Yballoon_eB, FALSE, TRUE,
7085 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7088 Yballoon_s, FALSE, FALSE,
7089 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7092 Yballoon_sB, FALSE, TRUE,
7093 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7096 Yballoon_w, FALSE, FALSE,
7097 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7100 Yballoon_wB, FALSE, TRUE,
7101 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7105 Xball_1, TRUE, FALSE,
7106 EL_EMC_MAGIC_BALL, -1, -1
7109 Yball_1, FALSE, FALSE,
7110 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7113 Xball_2, FALSE, FALSE,
7114 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7117 Yball_2, FALSE, FALSE,
7118 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7121 Yball_blank, FALSE, FALSE,
7122 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7126 Xamoeba_1, TRUE, FALSE,
7127 EL_AMOEBA_DRY, ACTION_OTHER, -1
7130 Xamoeba_2, FALSE, FALSE,
7131 EL_AMOEBA_DRY, ACTION_OTHER, -1
7134 Xamoeba_3, FALSE, FALSE,
7135 EL_AMOEBA_DRY, ACTION_OTHER, -1
7138 Xamoeba_4, FALSE, FALSE,
7139 EL_AMOEBA_DRY, ACTION_OTHER, -1
7142 Xamoeba_5, TRUE, FALSE,
7143 EL_AMOEBA_WET, ACTION_OTHER, -1
7146 Xamoeba_6, FALSE, FALSE,
7147 EL_AMOEBA_WET, ACTION_OTHER, -1
7150 Xamoeba_7, FALSE, FALSE,
7151 EL_AMOEBA_WET, ACTION_OTHER, -1
7154 Xamoeba_8, FALSE, FALSE,
7155 EL_AMOEBA_WET, ACTION_OTHER, -1
7160 EL_AMOEBA_DROP, ACTION_GROWING, -1
7163 Xdrip_fall, FALSE, FALSE,
7164 EL_AMOEBA_DROP, -1, -1
7167 Xdrip_stretch, FALSE, FALSE,
7168 EL_AMOEBA_DROP, ACTION_FALLING, -1
7171 Xdrip_stretchB, FALSE, TRUE,
7172 EL_AMOEBA_DROP, ACTION_FALLING, -1
7175 Ydrip_1_s, FALSE, FALSE,
7176 EL_AMOEBA_DROP, ACTION_FALLING, -1
7179 Ydrip_1_sB, FALSE, TRUE,
7180 EL_AMOEBA_DROP, ACTION_FALLING, -1
7183 Ydrip_2_s, FALSE, FALSE,
7184 EL_AMOEBA_DROP, ACTION_FALLING, -1
7187 Ydrip_2_sB, FALSE, TRUE,
7188 EL_AMOEBA_DROP, ACTION_FALLING, -1
7192 Xwonderwall, TRUE, FALSE,
7193 EL_MAGIC_WALL, -1, -1
7196 Ywonderwall, FALSE, FALSE,
7197 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7201 Xwheel, TRUE, FALSE,
7202 EL_ROBOT_WHEEL, -1, -1
7205 Ywheel, FALSE, FALSE,
7206 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7210 Xswitch, TRUE, FALSE,
7211 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7214 Yswitch, FALSE, FALSE,
7215 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7219 Xbumper, TRUE, FALSE,
7220 EL_EMC_SPRING_BUMPER, -1, -1
7223 Ybumper, FALSE, FALSE,
7224 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7228 Xacid_nw, TRUE, FALSE,
7229 EL_ACID_POOL_TOPLEFT, -1, -1
7232 Xacid_ne, TRUE, FALSE,
7233 EL_ACID_POOL_TOPRIGHT, -1, -1
7236 Xacid_sw, TRUE, FALSE,
7237 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7240 Xacid_s, TRUE, FALSE,
7241 EL_ACID_POOL_BOTTOM, -1, -1
7244 Xacid_se, TRUE, FALSE,
7245 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7249 Xfake_blank, TRUE, FALSE,
7250 EL_INVISIBLE_WALL, -1, -1
7253 Yfake_blank, FALSE, FALSE,
7254 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7258 Xfake_grass, TRUE, FALSE,
7259 EL_EMC_FAKE_GRASS, -1, -1
7262 Yfake_grass, FALSE, FALSE,
7263 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7267 Xfake_amoeba, TRUE, FALSE,
7268 EL_EMC_DRIPPER, -1, -1
7271 Yfake_amoeba, FALSE, FALSE,
7272 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7276 Xlenses, TRUE, FALSE,
7277 EL_EMC_LENSES, -1, -1
7281 Xmagnify, TRUE, FALSE,
7282 EL_EMC_MAGNIFIER, -1, -1
7287 EL_QUICKSAND_EMPTY, -1, -1
7290 Xsand_stone, TRUE, FALSE,
7291 EL_QUICKSAND_FULL, -1, -1
7294 Xsand_stonein_1, FALSE, TRUE,
7295 EL_ROCK, ACTION_FILLING, -1
7298 Xsand_stonein_2, FALSE, TRUE,
7299 EL_ROCK, ACTION_FILLING, -1
7302 Xsand_stonein_3, FALSE, TRUE,
7303 EL_ROCK, ACTION_FILLING, -1
7306 Xsand_stonein_4, FALSE, TRUE,
7307 EL_ROCK, ACTION_FILLING, -1
7310 Xsand_sandstone_1, FALSE, FALSE,
7311 EL_QUICKSAND_FILLING, -1, -1
7314 Xsand_sandstone_2, FALSE, FALSE,
7315 EL_QUICKSAND_FILLING, -1, -1
7318 Xsand_sandstone_3, FALSE, FALSE,
7319 EL_QUICKSAND_FILLING, -1, -1
7322 Xsand_sandstone_4, FALSE, FALSE,
7323 EL_QUICKSAND_FILLING, -1, -1
7326 Xsand_stonesand_1, FALSE, FALSE,
7327 EL_QUICKSAND_EMPTYING, -1, -1
7330 Xsand_stonesand_2, FALSE, FALSE,
7331 EL_QUICKSAND_EMPTYING, -1, -1
7334 Xsand_stonesand_3, FALSE, FALSE,
7335 EL_QUICKSAND_EMPTYING, -1, -1
7338 Xsand_stonesand_4, FALSE, FALSE,
7339 EL_QUICKSAND_EMPTYING, -1, -1
7342 Xsand_stoneout_1, FALSE, FALSE,
7343 EL_ROCK, ACTION_EMPTYING, -1
7346 Xsand_stoneout_2, FALSE, FALSE,
7347 EL_ROCK, ACTION_EMPTYING, -1
7350 Xsand_stonesand_quickout_1, FALSE, FALSE,
7351 EL_QUICKSAND_EMPTYING, -1, -1
7354 Xsand_stonesand_quickout_2, FALSE, FALSE,
7355 EL_QUICKSAND_EMPTYING, -1, -1
7359 Xslide_ns, TRUE, FALSE,
7360 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7363 Yslide_ns_blank, FALSE, FALSE,
7364 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7367 Xslide_ew, TRUE, FALSE,
7368 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7371 Yslide_ew_blank, FALSE, FALSE,
7372 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7376 Xwind_n, TRUE, FALSE,
7377 EL_BALLOON_SWITCH_UP, -1, -1
7380 Xwind_e, TRUE, FALSE,
7381 EL_BALLOON_SWITCH_RIGHT, -1, -1
7384 Xwind_s, TRUE, FALSE,
7385 EL_BALLOON_SWITCH_DOWN, -1, -1
7388 Xwind_w, TRUE, FALSE,
7389 EL_BALLOON_SWITCH_LEFT, -1, -1
7392 Xwind_any, TRUE, FALSE,
7393 EL_BALLOON_SWITCH_ANY, -1, -1
7396 Xwind_stop, TRUE, FALSE,
7397 EL_BALLOON_SWITCH_NONE, -1, -1
7402 EL_EM_EXIT_CLOSED, -1, -1
7405 Xexit_1, TRUE, FALSE,
7406 EL_EM_EXIT_OPEN, -1, -1
7409 Xexit_2, FALSE, FALSE,
7410 EL_EM_EXIT_OPEN, -1, -1
7413 Xexit_3, FALSE, FALSE,
7414 EL_EM_EXIT_OPEN, -1, -1
7418 Xpause, FALSE, FALSE,
7423 Xwall_1, TRUE, FALSE,
7427 Xwall_2, TRUE, FALSE,
7428 EL_EMC_WALL_14, -1, -1
7431 Xwall_3, TRUE, FALSE,
7432 EL_EMC_WALL_15, -1, -1
7435 Xwall_4, TRUE, FALSE,
7436 EL_EMC_WALL_16, -1, -1
7440 Xroundwall_1, TRUE, FALSE,
7441 EL_WALL_SLIPPERY, -1, -1
7444 Xroundwall_2, TRUE, FALSE,
7445 EL_EMC_WALL_SLIPPERY_2, -1, -1
7448 Xroundwall_3, TRUE, FALSE,
7449 EL_EMC_WALL_SLIPPERY_3, -1, -1
7452 Xroundwall_4, TRUE, FALSE,
7453 EL_EMC_WALL_SLIPPERY_4, -1, -1
7457 Xsteel_1, TRUE, FALSE,
7458 EL_STEELWALL, -1, -1
7461 Xsteel_2, TRUE, FALSE,
7462 EL_EMC_STEELWALL_2, -1, -1
7465 Xsteel_3, TRUE, FALSE,
7466 EL_EMC_STEELWALL_3, -1, -1
7469 Xsteel_4, TRUE, FALSE,
7470 EL_EMC_STEELWALL_4, -1, -1
7474 Xdecor_1, TRUE, FALSE,
7475 EL_EMC_WALL_8, -1, -1
7478 Xdecor_2, TRUE, FALSE,
7479 EL_EMC_WALL_6, -1, -1
7482 Xdecor_3, TRUE, FALSE,
7483 EL_EMC_WALL_4, -1, -1
7486 Xdecor_4, TRUE, FALSE,
7487 EL_EMC_WALL_7, -1, -1
7490 Xdecor_5, TRUE, FALSE,
7491 EL_EMC_WALL_5, -1, -1
7494 Xdecor_6, TRUE, FALSE,
7495 EL_EMC_WALL_9, -1, -1
7498 Xdecor_7, TRUE, FALSE,
7499 EL_EMC_WALL_10, -1, -1
7502 Xdecor_8, TRUE, FALSE,
7503 EL_EMC_WALL_1, -1, -1
7506 Xdecor_9, TRUE, FALSE,
7507 EL_EMC_WALL_2, -1, -1
7510 Xdecor_10, TRUE, FALSE,
7511 EL_EMC_WALL_3, -1, -1
7514 Xdecor_11, TRUE, FALSE,
7515 EL_EMC_WALL_11, -1, -1
7518 Xdecor_12, TRUE, FALSE,
7519 EL_EMC_WALL_12, -1, -1
7523 Xalpha_0, TRUE, FALSE,
7524 EL_CHAR('0'), -1, -1
7527 Xalpha_1, TRUE, FALSE,
7528 EL_CHAR('1'), -1, -1
7531 Xalpha_2, TRUE, FALSE,
7532 EL_CHAR('2'), -1, -1
7535 Xalpha_3, TRUE, FALSE,
7536 EL_CHAR('3'), -1, -1
7539 Xalpha_4, TRUE, FALSE,
7540 EL_CHAR('4'), -1, -1
7543 Xalpha_5, TRUE, FALSE,
7544 EL_CHAR('5'), -1, -1
7547 Xalpha_6, TRUE, FALSE,
7548 EL_CHAR('6'), -1, -1
7551 Xalpha_7, TRUE, FALSE,
7552 EL_CHAR('7'), -1, -1
7555 Xalpha_8, TRUE, FALSE,
7556 EL_CHAR('8'), -1, -1
7559 Xalpha_9, TRUE, FALSE,
7560 EL_CHAR('9'), -1, -1
7563 Xalpha_excla, TRUE, FALSE,
7564 EL_CHAR('!'), -1, -1
7567 Xalpha_apost, TRUE, FALSE,
7568 EL_CHAR('\''), -1, -1
7571 Xalpha_comma, TRUE, FALSE,
7572 EL_CHAR(','), -1, -1
7575 Xalpha_minus, TRUE, FALSE,
7576 EL_CHAR('-'), -1, -1
7579 Xalpha_perio, TRUE, FALSE,
7580 EL_CHAR('.'), -1, -1
7583 Xalpha_colon, TRUE, FALSE,
7584 EL_CHAR(':'), -1, -1
7587 Xalpha_quest, TRUE, FALSE,
7588 EL_CHAR('?'), -1, -1
7591 Xalpha_a, TRUE, FALSE,
7592 EL_CHAR('A'), -1, -1
7595 Xalpha_b, TRUE, FALSE,
7596 EL_CHAR('B'), -1, -1
7599 Xalpha_c, TRUE, FALSE,
7600 EL_CHAR('C'), -1, -1
7603 Xalpha_d, TRUE, FALSE,
7604 EL_CHAR('D'), -1, -1
7607 Xalpha_e, TRUE, FALSE,
7608 EL_CHAR('E'), -1, -1
7611 Xalpha_f, TRUE, FALSE,
7612 EL_CHAR('F'), -1, -1
7615 Xalpha_g, TRUE, FALSE,
7616 EL_CHAR('G'), -1, -1
7619 Xalpha_h, TRUE, FALSE,
7620 EL_CHAR('H'), -1, -1
7623 Xalpha_i, TRUE, FALSE,
7624 EL_CHAR('I'), -1, -1
7627 Xalpha_j, TRUE, FALSE,
7628 EL_CHAR('J'), -1, -1
7631 Xalpha_k, TRUE, FALSE,
7632 EL_CHAR('K'), -1, -1
7635 Xalpha_l, TRUE, FALSE,
7636 EL_CHAR('L'), -1, -1
7639 Xalpha_m, TRUE, FALSE,
7640 EL_CHAR('M'), -1, -1
7643 Xalpha_n, TRUE, FALSE,
7644 EL_CHAR('N'), -1, -1
7647 Xalpha_o, TRUE, FALSE,
7648 EL_CHAR('O'), -1, -1
7651 Xalpha_p, TRUE, FALSE,
7652 EL_CHAR('P'), -1, -1
7655 Xalpha_q, TRUE, FALSE,
7656 EL_CHAR('Q'), -1, -1
7659 Xalpha_r, TRUE, FALSE,
7660 EL_CHAR('R'), -1, -1
7663 Xalpha_s, TRUE, FALSE,
7664 EL_CHAR('S'), -1, -1
7667 Xalpha_t, TRUE, FALSE,
7668 EL_CHAR('T'), -1, -1
7671 Xalpha_u, TRUE, FALSE,
7672 EL_CHAR('U'), -1, -1
7675 Xalpha_v, TRUE, FALSE,
7676 EL_CHAR('V'), -1, -1
7679 Xalpha_w, TRUE, FALSE,
7680 EL_CHAR('W'), -1, -1
7683 Xalpha_x, TRUE, FALSE,
7684 EL_CHAR('X'), -1, -1
7687 Xalpha_y, TRUE, FALSE,
7688 EL_CHAR('Y'), -1, -1
7691 Xalpha_z, TRUE, FALSE,
7692 EL_CHAR('Z'), -1, -1
7695 Xalpha_arrow_e, TRUE, FALSE,
7696 EL_CHAR('>'), -1, -1
7699 Xalpha_arrow_w, TRUE, FALSE,
7700 EL_CHAR('<'), -1, -1
7703 Xalpha_copyr, TRUE, FALSE,
7704 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7708 Ykey_1_blank, FALSE, FALSE,
7709 EL_EM_KEY_1, ACTION_COLLECTING, -1
7712 Ykey_2_blank, FALSE, FALSE,
7713 EL_EM_KEY_2, ACTION_COLLECTING, -1
7716 Ykey_3_blank, FALSE, FALSE,
7717 EL_EM_KEY_3, ACTION_COLLECTING, -1
7720 Ykey_4_blank, FALSE, FALSE,
7721 EL_EM_KEY_4, ACTION_COLLECTING, -1
7724 Ykey_5_blank, FALSE, FALSE,
7725 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7728 Ykey_6_blank, FALSE, FALSE,
7729 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7732 Ykey_7_blank, FALSE, FALSE,
7733 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7736 Ykey_8_blank, FALSE, FALSE,
7737 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7740 Ylenses_blank, FALSE, FALSE,
7741 EL_EMC_LENSES, ACTION_COLLECTING, -1
7744 Ymagnify_blank, FALSE, FALSE,
7745 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7748 Ygrass_blank, FALSE, FALSE,
7749 EL_EMC_GRASS, ACTION_SNAPPING, -1
7752 Ydirt_blank, FALSE, FALSE,
7753 EL_SAND, ACTION_SNAPPING, -1
7762 static struct Mapping_EM_to_RND_player
7771 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7775 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7779 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7783 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7787 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7791 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7795 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7799 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7803 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7807 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7811 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7815 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7819 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7823 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7827 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7831 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7835 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7839 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7843 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7847 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7851 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7855 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7859 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7863 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7867 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7871 EL_PLAYER_1, ACTION_DEFAULT, -1,
7875 EL_PLAYER_2, ACTION_DEFAULT, -1,
7879 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7883 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7887 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7891 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7895 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7899 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7903 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7907 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7911 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7915 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7919 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7923 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7927 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7931 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7935 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7939 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7943 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7947 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7951 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7955 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7959 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7963 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7967 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7971 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7975 EL_PLAYER_3, ACTION_DEFAULT, -1,
7979 EL_PLAYER_4, ACTION_DEFAULT, -1,
7988 int map_element_RND_to_EM_cave(int element_rnd)
7990 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7991 static boolean mapping_initialized = FALSE;
7993 if (!mapping_initialized)
7997 // return "Xalpha_quest" for all undefined elements in mapping array
7998 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7999 mapping_RND_to_EM[i] = Xalpha_quest;
8001 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8002 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
8003 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
8004 em_object_mapping_list[i].element_em;
8006 mapping_initialized = TRUE;
8009 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
8011 Warn("invalid RND level element %d", element_rnd);
8016 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
8019 int map_element_EM_to_RND_cave(int element_em_cave)
8021 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8022 static boolean mapping_initialized = FALSE;
8024 if (!mapping_initialized)
8028 // return "EL_UNKNOWN" for all undefined elements in mapping array
8029 for (i = 0; i < GAME_TILE_MAX; i++)
8030 mapping_EM_to_RND[i] = EL_UNKNOWN;
8032 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8033 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8034 em_object_mapping_list[i].element_rnd;
8036 mapping_initialized = TRUE;
8039 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8041 Warn("invalid EM cave element %d", element_em_cave);
8046 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8049 int map_element_EM_to_RND_game(int element_em_game)
8051 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8052 static boolean mapping_initialized = FALSE;
8054 if (!mapping_initialized)
8058 // return "EL_UNKNOWN" for all undefined elements in mapping array
8059 for (i = 0; i < GAME_TILE_MAX; i++)
8060 mapping_EM_to_RND[i] = EL_UNKNOWN;
8062 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8063 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8064 em_object_mapping_list[i].element_rnd;
8066 mapping_initialized = TRUE;
8069 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8071 Warn("invalid EM game element %d", element_em_game);
8076 return mapping_EM_to_RND[element_em_game];
8079 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8081 struct LevelInfo_EM *level_em = level->native_em_level;
8082 struct CAVE *cav = level_em->cav;
8085 for (i = 0; i < GAME_TILE_MAX; i++)
8086 cav->android_array[i] = Cblank;
8088 for (i = 0; i < level->num_android_clone_elements; i++)
8090 int element_rnd = level->android_clone_element[i];
8091 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8093 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8094 if (em_object_mapping_list[j].element_rnd == element_rnd)
8095 cav->android_array[em_object_mapping_list[j].element_em] =
8100 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8102 struct LevelInfo_EM *level_em = level->native_em_level;
8103 struct CAVE *cav = level_em->cav;
8106 level->num_android_clone_elements = 0;
8108 for (i = 0; i < GAME_TILE_MAX; i++)
8110 int element_em_cave = cav->android_array[i];
8112 boolean element_found = FALSE;
8114 if (element_em_cave == Cblank)
8117 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8119 for (j = 0; j < level->num_android_clone_elements; j++)
8120 if (level->android_clone_element[j] == element_rnd)
8121 element_found = TRUE;
8125 level->android_clone_element[level->num_android_clone_elements++] =
8128 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8133 if (level->num_android_clone_elements == 0)
8135 level->num_android_clone_elements = 1;
8136 level->android_clone_element[0] = EL_EMPTY;
8140 int map_direction_RND_to_EM(int direction)
8142 return (direction == MV_UP ? 0 :
8143 direction == MV_RIGHT ? 1 :
8144 direction == MV_DOWN ? 2 :
8145 direction == MV_LEFT ? 3 :
8149 int map_direction_EM_to_RND(int direction)
8151 return (direction == 0 ? MV_UP :
8152 direction == 1 ? MV_RIGHT :
8153 direction == 2 ? MV_DOWN :
8154 direction == 3 ? MV_LEFT :
8158 int map_element_RND_to_SP(int element_rnd)
8160 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8162 if (element_rnd >= EL_SP_START &&
8163 element_rnd <= EL_SP_END)
8164 element_sp = element_rnd - EL_SP_START;
8165 else if (element_rnd == EL_EMPTY_SPACE)
8167 else if (element_rnd == EL_INVISIBLE_WALL)
8173 int map_element_SP_to_RND(int element_sp)
8175 int element_rnd = EL_UNKNOWN;
8177 if (element_sp >= 0x00 &&
8179 element_rnd = EL_SP_START + element_sp;
8180 else if (element_sp == 0x28)
8181 element_rnd = EL_INVISIBLE_WALL;
8186 int map_action_SP_to_RND(int action_sp)
8190 case actActive: return ACTION_ACTIVE;
8191 case actImpact: return ACTION_IMPACT;
8192 case actExploding: return ACTION_EXPLODING;
8193 case actDigging: return ACTION_DIGGING;
8194 case actSnapping: return ACTION_SNAPPING;
8195 case actCollecting: return ACTION_COLLECTING;
8196 case actPassing: return ACTION_PASSING;
8197 case actPushing: return ACTION_PUSHING;
8198 case actDropping: return ACTION_DROPPING;
8200 default: return ACTION_DEFAULT;
8204 int map_element_RND_to_MM(int element_rnd)
8206 return (element_rnd >= EL_MM_START_1 &&
8207 element_rnd <= EL_MM_END_1 ?
8208 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8210 element_rnd >= EL_MM_START_2 &&
8211 element_rnd <= EL_MM_END_2 ?
8212 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8214 element_rnd >= EL_MM_START_3 &&
8215 element_rnd <= EL_MM_END_3 ?
8216 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
8218 element_rnd >= EL_CHAR_START &&
8219 element_rnd <= EL_CHAR_END ?
8220 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8222 element_rnd >= EL_MM_RUNTIME_START &&
8223 element_rnd <= EL_MM_RUNTIME_END ?
8224 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8226 EL_MM_EMPTY_NATIVE);
8229 int map_element_MM_to_RND(int element_mm)
8231 return (element_mm == EL_MM_EMPTY_NATIVE ||
8232 element_mm == EL_DF_EMPTY_NATIVE ?
8235 element_mm >= EL_MM_START_1_NATIVE &&
8236 element_mm <= EL_MM_END_1_NATIVE ?
8237 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8239 element_mm >= EL_MM_START_2_NATIVE &&
8240 element_mm <= EL_MM_END_2_NATIVE ?
8241 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8243 element_mm >= EL_MM_START_3_NATIVE &&
8244 element_mm <= EL_MM_END_3_NATIVE ?
8245 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
8247 element_mm >= EL_MM_CHAR_START_NATIVE &&
8248 element_mm <= EL_MM_CHAR_END_NATIVE ?
8249 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8251 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8252 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8253 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8258 int map_action_MM_to_RND(int action_mm)
8260 // all MM actions are defined to exactly match their RND counterparts
8264 int map_sound_MM_to_RND(int sound_mm)
8268 case SND_MM_GAME_LEVELTIME_CHARGING:
8269 return SND_GAME_LEVELTIME_CHARGING;
8271 case SND_MM_GAME_HEALTH_CHARGING:
8272 return SND_GAME_HEALTH_CHARGING;
8275 return SND_UNDEFINED;
8279 int map_mm_wall_element(int element)
8281 return (element >= EL_MM_STEEL_WALL_START &&
8282 element <= EL_MM_STEEL_WALL_END ?
8285 element >= EL_MM_WOODEN_WALL_START &&
8286 element <= EL_MM_WOODEN_WALL_END ?
8289 element >= EL_MM_ICE_WALL_START &&
8290 element <= EL_MM_ICE_WALL_END ?
8293 element >= EL_MM_AMOEBA_WALL_START &&
8294 element <= EL_MM_AMOEBA_WALL_END ?
8297 element >= EL_DF_STEEL_WALL_START &&
8298 element <= EL_DF_STEEL_WALL_END ?
8301 element >= EL_DF_WOODEN_WALL_START &&
8302 element <= EL_DF_WOODEN_WALL_END ?
8308 int map_mm_wall_element_editor(int element)
8312 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8313 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8314 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8315 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8316 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8317 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8319 default: return element;
8323 int get_next_element(int element)
8327 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8328 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8329 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8330 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8331 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8332 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8333 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8334 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8335 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8336 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8337 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8339 default: return element;
8343 int el2img_mm(int element_mm)
8345 return el2img(map_element_MM_to_RND(element_mm));
8348 int el_act2img_mm(int element_mm, int action)
8350 return el_act2img(map_element_MM_to_RND(element_mm), action);
8353 int el_act_dir2img(int element, int action, int direction)
8355 element = GFX_ELEMENT(element);
8356 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8358 // direction_graphic[][] == graphic[] for undefined direction graphics
8359 return element_info[element].direction_graphic[action][direction];
8362 static int el_act_dir2crm(int element, int action, int direction)
8364 element = GFX_ELEMENT(element);
8365 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8367 // direction_graphic[][] == graphic[] for undefined direction graphics
8368 return element_info[element].direction_crumbled[action][direction];
8371 int el_act2img(int element, int action)
8373 element = GFX_ELEMENT(element);
8375 return element_info[element].graphic[action];
8378 int el_act2crm(int element, int action)
8380 element = GFX_ELEMENT(element);
8382 return element_info[element].crumbled[action];
8385 int el_dir2img(int element, int direction)
8387 element = GFX_ELEMENT(element);
8389 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8392 int el2baseimg(int element)
8394 return element_info[element].graphic[ACTION_DEFAULT];
8397 int el2img(int element)
8399 element = GFX_ELEMENT(element);
8401 return element_info[element].graphic[ACTION_DEFAULT];
8404 int el2edimg(int element)
8406 element = GFX_ELEMENT(element);
8408 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8411 int el2preimg(int element)
8413 element = GFX_ELEMENT(element);
8415 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8418 int el2panelimg(int element)
8420 element = GFX_ELEMENT(element);
8422 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8425 int font2baseimg(int font_nr)
8427 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8430 int getBeltNrFromBeltElement(int element)
8432 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8433 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8434 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8437 int getBeltNrFromBeltActiveElement(int element)
8439 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8440 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8441 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8444 int getBeltNrFromBeltSwitchElement(int element)
8446 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8447 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8448 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8451 int getBeltDirNrFromBeltElement(int element)
8453 static int belt_base_element[4] =
8455 EL_CONVEYOR_BELT_1_LEFT,
8456 EL_CONVEYOR_BELT_2_LEFT,
8457 EL_CONVEYOR_BELT_3_LEFT,
8458 EL_CONVEYOR_BELT_4_LEFT
8461 int belt_nr = getBeltNrFromBeltElement(element);
8462 int belt_dir_nr = element - belt_base_element[belt_nr];
8464 return (belt_dir_nr % 3);
8467 int getBeltDirNrFromBeltSwitchElement(int element)
8469 static int belt_base_element[4] =
8471 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8472 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8473 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8474 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8477 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8478 int belt_dir_nr = element - belt_base_element[belt_nr];
8480 return (belt_dir_nr % 3);
8483 int getBeltDirFromBeltElement(int element)
8485 static int belt_move_dir[3] =
8492 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8494 return belt_move_dir[belt_dir_nr];
8497 int getBeltDirFromBeltSwitchElement(int element)
8499 static int belt_move_dir[3] =
8506 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8508 return belt_move_dir[belt_dir_nr];
8511 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8513 static int belt_base_element[4] =
8515 EL_CONVEYOR_BELT_1_LEFT,
8516 EL_CONVEYOR_BELT_2_LEFT,
8517 EL_CONVEYOR_BELT_3_LEFT,
8518 EL_CONVEYOR_BELT_4_LEFT
8521 return belt_base_element[belt_nr] + belt_dir_nr;
8524 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8526 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8528 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8531 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8533 static int belt_base_element[4] =
8535 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8536 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8537 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8538 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8541 return belt_base_element[belt_nr] + belt_dir_nr;
8544 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8546 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8548 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8551 boolean swapTiles_EM(boolean is_pre_emc_cave)
8553 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8556 boolean getTeamMode_EM(void)
8558 return game.team_mode || network_playing;
8561 boolean isActivePlayer_EM(int player_nr)
8563 return stored_player[player_nr].active;
8566 unsigned int InitRND(int seed)
8568 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8569 return InitEngineRandom_EM(seed);
8570 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8571 return InitEngineRandom_SP(seed);
8572 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8573 return InitEngineRandom_MM(seed);
8575 return InitEngineRandom_RND(seed);
8578 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8579 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8581 static int get_effective_element_EM(int tile, int frame_em)
8583 int element = object_mapping[tile].element_rnd;
8584 int action = object_mapping[tile].action;
8585 boolean is_backside = object_mapping[tile].is_backside;
8586 boolean action_removing = (action == ACTION_DIGGING ||
8587 action == ACTION_SNAPPING ||
8588 action == ACTION_COLLECTING);
8596 return (frame_em > 5 ? EL_EMPTY : element);
8602 else // frame_em == 7
8613 case Ydiamond_stone:
8617 case Xdrip_stretchB:
8633 case Ymagnify_blank:
8636 case Xsand_stonein_1:
8637 case Xsand_stonein_2:
8638 case Xsand_stonein_3:
8639 case Xsand_stonein_4:
8643 return (is_backside || action_removing ? EL_EMPTY : element);
8648 static boolean check_linear_animation_EM(int tile)
8652 case Xsand_stonesand_1:
8653 case Xsand_stonesand_quickout_1:
8654 case Xsand_sandstone_1:
8655 case Xsand_stonein_1:
8656 case Xsand_stoneout_1:
8684 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8685 boolean has_crumbled_graphics,
8686 int crumbled, int sync_frame)
8688 // if element can be crumbled, but certain action graphics are just empty
8689 // space (like instantly snapping sand to empty space in 1 frame), do not
8690 // treat these empty space graphics as crumbled graphics in EMC engine
8691 if (crumbled == IMG_EMPTY_SPACE)
8692 has_crumbled_graphics = FALSE;
8694 if (has_crumbled_graphics)
8696 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8697 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8698 g_crumbled->anim_delay,
8699 g_crumbled->anim_mode,
8700 g_crumbled->anim_start_frame,
8703 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8704 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8706 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8707 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8709 g_em->has_crumbled_graphics = TRUE;
8713 g_em->crumbled_bitmap = NULL;
8714 g_em->crumbled_src_x = 0;
8715 g_em->crumbled_src_y = 0;
8716 g_em->crumbled_border_size = 0;
8717 g_em->crumbled_tile_size = 0;
8719 g_em->has_crumbled_graphics = FALSE;
8724 void ResetGfxAnimation_EM(int x, int y, int tile)
8730 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8731 int tile, int frame_em, int x, int y)
8733 int action = object_mapping[tile].action;
8734 int direction = object_mapping[tile].direction;
8735 int effective_element = get_effective_element_EM(tile, frame_em);
8736 int graphic = (direction == MV_NONE ?
8737 el_act2img(effective_element, action) :
8738 el_act_dir2img(effective_element, action, direction));
8739 struct GraphicInfo *g = &graphic_info[graphic];
8741 boolean action_removing = (action == ACTION_DIGGING ||
8742 action == ACTION_SNAPPING ||
8743 action == ACTION_COLLECTING);
8744 boolean action_moving = (action == ACTION_FALLING ||
8745 action == ACTION_MOVING ||
8746 action == ACTION_PUSHING ||
8747 action == ACTION_EATING ||
8748 action == ACTION_FILLING ||
8749 action == ACTION_EMPTYING);
8750 boolean action_falling = (action == ACTION_FALLING ||
8751 action == ACTION_FILLING ||
8752 action == ACTION_EMPTYING);
8754 // special case: graphic uses "2nd movement tile" and has defined
8755 // 7 frames for movement animation (or less) => use default graphic
8756 // for last (8th) frame which ends the movement animation
8757 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8759 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8760 graphic = (direction == MV_NONE ?
8761 el_act2img(effective_element, action) :
8762 el_act_dir2img(effective_element, action, direction));
8764 g = &graphic_info[graphic];
8767 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8771 else if (action_moving)
8773 boolean is_backside = object_mapping[tile].is_backside;
8777 int direction = object_mapping[tile].direction;
8778 int move_dir = (action_falling ? MV_DOWN : direction);
8783 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8784 if (g->double_movement && frame_em == 0)
8788 if (move_dir == MV_LEFT)
8789 GfxFrame[x - 1][y] = GfxFrame[x][y];
8790 else if (move_dir == MV_RIGHT)
8791 GfxFrame[x + 1][y] = GfxFrame[x][y];
8792 else if (move_dir == MV_UP)
8793 GfxFrame[x][y - 1] = GfxFrame[x][y];
8794 else if (move_dir == MV_DOWN)
8795 GfxFrame[x][y + 1] = GfxFrame[x][y];
8802 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8803 if (tile == Xsand_stonesand_quickout_1 ||
8804 tile == Xsand_stonesand_quickout_2)
8808 if (graphic_info[graphic].anim_global_sync)
8809 sync_frame = FrameCounter;
8810 else if (graphic_info[graphic].anim_global_anim_sync)
8811 sync_frame = getGlobalAnimSyncFrame();
8812 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8813 sync_frame = GfxFrame[x][y];
8815 sync_frame = 0; // playfield border (pseudo steel)
8817 SetRandomAnimationValue(x, y);
8819 int frame = getAnimationFrame(g->anim_frames,
8822 g->anim_start_frame,
8825 g_em->unique_identifier =
8826 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8829 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8830 int tile, int frame_em, int x, int y)
8832 int action = object_mapping[tile].action;
8833 int direction = object_mapping[tile].direction;
8834 boolean is_backside = object_mapping[tile].is_backside;
8835 int effective_element = get_effective_element_EM(tile, frame_em);
8836 int effective_action = action;
8837 int graphic = (direction == MV_NONE ?
8838 el_act2img(effective_element, effective_action) :
8839 el_act_dir2img(effective_element, effective_action,
8841 int crumbled = (direction == MV_NONE ?
8842 el_act2crm(effective_element, effective_action) :
8843 el_act_dir2crm(effective_element, effective_action,
8845 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8846 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8847 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8848 struct GraphicInfo *g = &graphic_info[graphic];
8851 // special case: graphic uses "2nd movement tile" and has defined
8852 // 7 frames for movement animation (or less) => use default graphic
8853 // for last (8th) frame which ends the movement animation
8854 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8856 effective_action = ACTION_DEFAULT;
8857 graphic = (direction == MV_NONE ?
8858 el_act2img(effective_element, effective_action) :
8859 el_act_dir2img(effective_element, effective_action,
8861 crumbled = (direction == MV_NONE ?
8862 el_act2crm(effective_element, effective_action) :
8863 el_act_dir2crm(effective_element, effective_action,
8866 g = &graphic_info[graphic];
8869 if (graphic_info[graphic].anim_global_sync)
8870 sync_frame = FrameCounter;
8871 else if (graphic_info[graphic].anim_global_anim_sync)
8872 sync_frame = getGlobalAnimSyncFrame();
8873 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8874 sync_frame = GfxFrame[x][y];
8876 sync_frame = 0; // playfield border (pseudo steel)
8878 SetRandomAnimationValue(x, y);
8880 int frame = getAnimationFrame(g->anim_frames,
8883 g->anim_start_frame,
8886 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8887 g->double_movement && is_backside);
8889 // (updating the "crumbled" graphic definitions is probably not really needed,
8890 // as animations for crumbled graphics can't be longer than one EMC cycle)
8891 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8895 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8896 int player_nr, int anim, int frame_em)
8898 int element = player_mapping[player_nr][anim].element_rnd;
8899 int action = player_mapping[player_nr][anim].action;
8900 int direction = player_mapping[player_nr][anim].direction;
8901 int graphic = (direction == MV_NONE ?
8902 el_act2img(element, action) :
8903 el_act_dir2img(element, action, direction));
8904 struct GraphicInfo *g = &graphic_info[graphic];
8907 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8909 stored_player[player_nr].StepFrame = frame_em;
8911 sync_frame = stored_player[player_nr].Frame;
8913 int frame = getAnimationFrame(g->anim_frames,
8916 g->anim_start_frame,
8919 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8920 &g_em->src_x, &g_em->src_y, FALSE);
8923 void InitGraphicInfo_EM(void)
8927 // always start with reliable default values
8928 for (i = 0; i < GAME_TILE_MAX; i++)
8930 object_mapping[i].element_rnd = EL_UNKNOWN;
8931 object_mapping[i].is_backside = FALSE;
8932 object_mapping[i].action = ACTION_DEFAULT;
8933 object_mapping[i].direction = MV_NONE;
8936 // always start with reliable default values
8937 for (p = 0; p < MAX_PLAYERS; p++)
8939 for (i = 0; i < PLY_MAX; i++)
8941 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8942 player_mapping[p][i].action = ACTION_DEFAULT;
8943 player_mapping[p][i].direction = MV_NONE;
8947 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8949 int e = em_object_mapping_list[i].element_em;
8951 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8952 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8954 if (em_object_mapping_list[i].action != -1)
8955 object_mapping[e].action = em_object_mapping_list[i].action;
8957 if (em_object_mapping_list[i].direction != -1)
8958 object_mapping[e].direction =
8959 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8962 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8964 int a = em_player_mapping_list[i].action_em;
8965 int p = em_player_mapping_list[i].player_nr;
8967 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8969 if (em_player_mapping_list[i].action != -1)
8970 player_mapping[p][a].action = em_player_mapping_list[i].action;
8972 if (em_player_mapping_list[i].direction != -1)
8973 player_mapping[p][a].direction =
8974 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8977 for (i = 0; i < GAME_TILE_MAX; i++)
8979 int element = object_mapping[i].element_rnd;
8980 int action = object_mapping[i].action;
8981 int direction = object_mapping[i].direction;
8982 boolean is_backside = object_mapping[i].is_backside;
8983 boolean action_exploding = ((action == ACTION_EXPLODING ||
8984 action == ACTION_SMASHED_BY_ROCK ||
8985 action == ACTION_SMASHED_BY_SPRING) &&
8986 element != EL_DIAMOND);
8987 boolean action_active = (action == ACTION_ACTIVE);
8988 boolean action_other = (action == ACTION_OTHER);
8990 for (j = 0; j < 8; j++)
8992 int effective_element = get_effective_element_EM(i, j);
8993 int effective_action = (j < 7 ? action :
8994 i == Xdrip_stretch ? action :
8995 i == Xdrip_stretchB ? action :
8996 i == Ydrip_1_s ? action :
8997 i == Ydrip_1_sB ? action :
8998 i == Yball_1 ? action :
8999 i == Xball_2 ? action :
9000 i == Yball_2 ? action :
9001 i == Yball_blank ? action :
9002 i == Ykey_1_blank ? action :
9003 i == Ykey_2_blank ? action :
9004 i == Ykey_3_blank ? action :
9005 i == Ykey_4_blank ? action :
9006 i == Ykey_5_blank ? action :
9007 i == Ykey_6_blank ? action :
9008 i == Ykey_7_blank ? action :
9009 i == Ykey_8_blank ? action :
9010 i == Ylenses_blank ? action :
9011 i == Ymagnify_blank ? action :
9012 i == Ygrass_blank ? action :
9013 i == Ydirt_blank ? action :
9014 i == Xsand_stonein_1 ? action :
9015 i == Xsand_stonein_2 ? action :
9016 i == Xsand_stonein_3 ? action :
9017 i == Xsand_stonein_4 ? action :
9018 i == Xsand_stoneout_1 ? action :
9019 i == Xsand_stoneout_2 ? action :
9020 i == Xboom_android ? ACTION_EXPLODING :
9021 action_exploding ? ACTION_EXPLODING :
9022 action_active ? action :
9023 action_other ? action :
9025 int graphic = (el_act_dir2img(effective_element, effective_action,
9027 int crumbled = (el_act_dir2crm(effective_element, effective_action,
9029 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
9030 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
9031 boolean has_action_graphics = (graphic != base_graphic);
9032 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9033 struct GraphicInfo *g = &graphic_info[graphic];
9034 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9037 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9038 boolean special_animation = (action != ACTION_DEFAULT &&
9039 g->anim_frames == 3 &&
9040 g->anim_delay == 2 &&
9041 g->anim_mode & ANIM_LINEAR);
9042 int sync_frame = (i == Xdrip_stretch ? 7 :
9043 i == Xdrip_stretchB ? 7 :
9044 i == Ydrip_2_s ? j + 8 :
9045 i == Ydrip_2_sB ? j + 8 :
9054 i == Xfake_acid_1 ? 0 :
9055 i == Xfake_acid_2 ? 10 :
9056 i == Xfake_acid_3 ? 20 :
9057 i == Xfake_acid_4 ? 30 :
9058 i == Xfake_acid_5 ? 40 :
9059 i == Xfake_acid_6 ? 50 :
9060 i == Xfake_acid_7 ? 60 :
9061 i == Xfake_acid_8 ? 70 :
9062 i == Xfake_acid_1_player ? 0 :
9063 i == Xfake_acid_2_player ? 10 :
9064 i == Xfake_acid_3_player ? 20 :
9065 i == Xfake_acid_4_player ? 30 :
9066 i == Xfake_acid_5_player ? 40 :
9067 i == Xfake_acid_6_player ? 50 :
9068 i == Xfake_acid_7_player ? 60 :
9069 i == Xfake_acid_8_player ? 70 :
9071 i == Yball_2 ? j + 8 :
9072 i == Yball_blank ? j + 1 :
9073 i == Ykey_1_blank ? j + 1 :
9074 i == Ykey_2_blank ? j + 1 :
9075 i == Ykey_3_blank ? j + 1 :
9076 i == Ykey_4_blank ? j + 1 :
9077 i == Ykey_5_blank ? j + 1 :
9078 i == Ykey_6_blank ? j + 1 :
9079 i == Ykey_7_blank ? j + 1 :
9080 i == Ykey_8_blank ? j + 1 :
9081 i == Ylenses_blank ? j + 1 :
9082 i == Ymagnify_blank ? j + 1 :
9083 i == Ygrass_blank ? j + 1 :
9084 i == Ydirt_blank ? j + 1 :
9085 i == Xamoeba_1 ? 0 :
9086 i == Xamoeba_2 ? 1 :
9087 i == Xamoeba_3 ? 2 :
9088 i == Xamoeba_4 ? 3 :
9089 i == Xamoeba_5 ? 0 :
9090 i == Xamoeba_6 ? 1 :
9091 i == Xamoeba_7 ? 2 :
9092 i == Xamoeba_8 ? 3 :
9093 i == Xexit_2 ? j + 8 :
9094 i == Xexit_3 ? j + 16 :
9095 i == Xdynamite_1 ? 0 :
9096 i == Xdynamite_2 ? 8 :
9097 i == Xdynamite_3 ? 16 :
9098 i == Xdynamite_4 ? 24 :
9099 i == Xsand_stonein_1 ? j + 1 :
9100 i == Xsand_stonein_2 ? j + 9 :
9101 i == Xsand_stonein_3 ? j + 17 :
9102 i == Xsand_stonein_4 ? j + 25 :
9103 i == Xsand_stoneout_1 && j == 0 ? 0 :
9104 i == Xsand_stoneout_1 && j == 1 ? 0 :
9105 i == Xsand_stoneout_1 && j == 2 ? 1 :
9106 i == Xsand_stoneout_1 && j == 3 ? 2 :
9107 i == Xsand_stoneout_1 && j == 4 ? 2 :
9108 i == Xsand_stoneout_1 && j == 5 ? 3 :
9109 i == Xsand_stoneout_1 && j == 6 ? 4 :
9110 i == Xsand_stoneout_1 && j == 7 ? 4 :
9111 i == Xsand_stoneout_2 && j == 0 ? 5 :
9112 i == Xsand_stoneout_2 && j == 1 ? 6 :
9113 i == Xsand_stoneout_2 && j == 2 ? 7 :
9114 i == Xsand_stoneout_2 && j == 3 ? 8 :
9115 i == Xsand_stoneout_2 && j == 4 ? 9 :
9116 i == Xsand_stoneout_2 && j == 5 ? 11 :
9117 i == Xsand_stoneout_2 && j == 6 ? 13 :
9118 i == Xsand_stoneout_2 && j == 7 ? 15 :
9119 i == Xboom_bug && j == 1 ? 2 :
9120 i == Xboom_bug && j == 2 ? 2 :
9121 i == Xboom_bug && j == 3 ? 4 :
9122 i == Xboom_bug && j == 4 ? 4 :
9123 i == Xboom_bug && j == 5 ? 2 :
9124 i == Xboom_bug && j == 6 ? 2 :
9125 i == Xboom_bug && j == 7 ? 0 :
9126 i == Xboom_tank && j == 1 ? 2 :
9127 i == Xboom_tank && j == 2 ? 2 :
9128 i == Xboom_tank && j == 3 ? 4 :
9129 i == Xboom_tank && j == 4 ? 4 :
9130 i == Xboom_tank && j == 5 ? 2 :
9131 i == Xboom_tank && j == 6 ? 2 :
9132 i == Xboom_tank && j == 7 ? 0 :
9133 i == Xboom_android && j == 7 ? 6 :
9134 i == Xboom_1 && j == 1 ? 2 :
9135 i == Xboom_1 && j == 2 ? 2 :
9136 i == Xboom_1 && j == 3 ? 4 :
9137 i == Xboom_1 && j == 4 ? 4 :
9138 i == Xboom_1 && j == 5 ? 6 :
9139 i == Xboom_1 && j == 6 ? 6 :
9140 i == Xboom_1 && j == 7 ? 8 :
9141 i == Xboom_2 && j == 0 ? 8 :
9142 i == Xboom_2 && j == 1 ? 8 :
9143 i == Xboom_2 && j == 2 ? 10 :
9144 i == Xboom_2 && j == 3 ? 10 :
9145 i == Xboom_2 && j == 4 ? 10 :
9146 i == Xboom_2 && j == 5 ? 12 :
9147 i == Xboom_2 && j == 6 ? 12 :
9148 i == Xboom_2 && j == 7 ? 12 :
9149 special_animation && j == 4 ? 3 :
9150 effective_action != action ? 0 :
9152 int frame = getAnimationFrame(g->anim_frames,
9155 g->anim_start_frame,
9158 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9159 g->double_movement && is_backside);
9161 g_em->bitmap = src_bitmap;
9162 g_em->src_x = src_x;
9163 g_em->src_y = src_y;
9164 g_em->src_offset_x = 0;
9165 g_em->src_offset_y = 0;
9166 g_em->dst_offset_x = 0;
9167 g_em->dst_offset_y = 0;
9168 g_em->width = TILEX;
9169 g_em->height = TILEY;
9171 g_em->preserve_background = FALSE;
9173 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9176 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9177 effective_action == ACTION_MOVING ||
9178 effective_action == ACTION_PUSHING ||
9179 effective_action == ACTION_EATING)) ||
9180 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9181 effective_action == ACTION_EMPTYING)))
9184 (effective_action == ACTION_FALLING ||
9185 effective_action == ACTION_FILLING ||
9186 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9187 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9188 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9189 int num_steps = (i == Ydrip_1_s ? 16 :
9190 i == Ydrip_1_sB ? 16 :
9191 i == Ydrip_2_s ? 16 :
9192 i == Ydrip_2_sB ? 16 :
9193 i == Xsand_stonein_1 ? 32 :
9194 i == Xsand_stonein_2 ? 32 :
9195 i == Xsand_stonein_3 ? 32 :
9196 i == Xsand_stonein_4 ? 32 :
9197 i == Xsand_stoneout_1 ? 16 :
9198 i == Xsand_stoneout_2 ? 16 : 8);
9199 int cx = ABS(dx) * (TILEX / num_steps);
9200 int cy = ABS(dy) * (TILEY / num_steps);
9201 int step_frame = (i == Ydrip_2_s ? j + 8 :
9202 i == Ydrip_2_sB ? j + 8 :
9203 i == Xsand_stonein_2 ? j + 8 :
9204 i == Xsand_stonein_3 ? j + 16 :
9205 i == Xsand_stonein_4 ? j + 24 :
9206 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9207 int step = (is_backside ? step_frame : num_steps - step_frame);
9209 if (is_backside) // tile where movement starts
9211 if (dx < 0 || dy < 0)
9213 g_em->src_offset_x = cx * step;
9214 g_em->src_offset_y = cy * step;
9218 g_em->dst_offset_x = cx * step;
9219 g_em->dst_offset_y = cy * step;
9222 else // tile where movement ends
9224 if (dx < 0 || dy < 0)
9226 g_em->dst_offset_x = cx * step;
9227 g_em->dst_offset_y = cy * step;
9231 g_em->src_offset_x = cx * step;
9232 g_em->src_offset_y = cy * step;
9236 g_em->width = TILEX - cx * step;
9237 g_em->height = TILEY - cy * step;
9240 // create unique graphic identifier to decide if tile must be redrawn
9241 /* bit 31 - 16 (16 bit): EM style graphic
9242 bit 15 - 12 ( 4 bit): EM style frame
9243 bit 11 - 6 ( 6 bit): graphic width
9244 bit 5 - 0 ( 6 bit): graphic height */
9245 g_em->unique_identifier =
9246 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9250 for (i = 0; i < GAME_TILE_MAX; i++)
9252 for (j = 0; j < 8; j++)
9254 int element = object_mapping[i].element_rnd;
9255 int action = object_mapping[i].action;
9256 int direction = object_mapping[i].direction;
9257 boolean is_backside = object_mapping[i].is_backside;
9258 int graphic_action = el_act_dir2img(element, action, direction);
9259 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9261 if ((action == ACTION_SMASHED_BY_ROCK ||
9262 action == ACTION_SMASHED_BY_SPRING ||
9263 action == ACTION_EATING) &&
9264 graphic_action == graphic_default)
9266 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9267 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9268 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9269 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9272 // no separate animation for "smashed by rock" -- use rock instead
9273 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9274 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9276 g_em->bitmap = g_xx->bitmap;
9277 g_em->src_x = g_xx->src_x;
9278 g_em->src_y = g_xx->src_y;
9279 g_em->src_offset_x = g_xx->src_offset_x;
9280 g_em->src_offset_y = g_xx->src_offset_y;
9281 g_em->dst_offset_x = g_xx->dst_offset_x;
9282 g_em->dst_offset_y = g_xx->dst_offset_y;
9283 g_em->width = g_xx->width;
9284 g_em->height = g_xx->height;
9285 g_em->unique_identifier = g_xx->unique_identifier;
9288 g_em->preserve_background = TRUE;
9293 for (p = 0; p < MAX_PLAYERS; p++)
9295 for (i = 0; i < PLY_MAX; i++)
9297 int element = player_mapping[p][i].element_rnd;
9298 int action = player_mapping[p][i].action;
9299 int direction = player_mapping[p][i].direction;
9301 for (j = 0; j < 8; j++)
9303 int effective_element = element;
9304 int effective_action = action;
9305 int graphic = (direction == MV_NONE ?
9306 el_act2img(effective_element, effective_action) :
9307 el_act_dir2img(effective_element, effective_action,
9309 struct GraphicInfo *g = &graphic_info[graphic];
9310 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9314 int frame = getAnimationFrame(g->anim_frames,
9317 g->anim_start_frame,
9320 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9322 g_em->bitmap = src_bitmap;
9323 g_em->src_x = src_x;
9324 g_em->src_y = src_y;
9325 g_em->src_offset_x = 0;
9326 g_em->src_offset_y = 0;
9327 g_em->dst_offset_x = 0;
9328 g_em->dst_offset_y = 0;
9329 g_em->width = TILEX;
9330 g_em->height = TILEY;
9336 static void CheckSaveEngineSnapshot_EM(int frame,
9337 boolean any_player_moving,
9338 boolean any_player_snapping,
9339 boolean any_player_dropping)
9341 if (frame == 7 && !any_player_dropping)
9343 if (!local_player->was_waiting)
9345 if (!CheckSaveEngineSnapshotToList())
9348 local_player->was_waiting = TRUE;
9351 else if (any_player_moving || any_player_snapping || any_player_dropping)
9353 local_player->was_waiting = FALSE;
9357 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9358 boolean murphy_is_dropping)
9360 if (murphy_is_waiting)
9362 if (!local_player->was_waiting)
9364 if (!CheckSaveEngineSnapshotToList())
9367 local_player->was_waiting = TRUE;
9372 local_player->was_waiting = FALSE;
9376 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9377 boolean button_released)
9379 if (button_released)
9381 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9382 CheckSaveEngineSnapshotToList();
9384 else if (element_clicked)
9386 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9387 CheckSaveEngineSnapshotToList();
9389 game.snapshot.changed_action = TRUE;
9393 boolean CheckSingleStepMode_EM(int frame,
9394 boolean any_player_moving,
9395 boolean any_player_snapping,
9396 boolean any_player_dropping)
9398 if (tape.single_step && tape.recording && !tape.pausing)
9399 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9400 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9402 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9403 any_player_snapping, any_player_dropping);
9405 return tape.pausing;
9408 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9409 boolean murphy_is_dropping)
9411 boolean murphy_starts_dropping = FALSE;
9414 for (i = 0; i < MAX_PLAYERS; i++)
9415 if (stored_player[i].force_dropping)
9416 murphy_starts_dropping = TRUE;
9418 if (tape.single_step && tape.recording && !tape.pausing)
9419 if (murphy_is_waiting && !murphy_starts_dropping)
9420 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9422 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9425 void CheckSingleStepMode_MM(boolean element_clicked,
9426 boolean button_released)
9428 if (tape.single_step && tape.recording && !tape.pausing)
9429 if (button_released)
9430 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9432 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9435 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9436 int graphic, int sync_frame)
9438 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9440 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9443 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9445 return (IS_NEXT_FRAME(sync_frame, graphic));
9448 int getGraphicInfo_Delay(int graphic)
9450 return graphic_info[graphic].anim_delay;
9453 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9455 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9458 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9464 void PlayMenuSoundExt(int sound)
9466 if (sound == SND_UNDEFINED)
9469 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9470 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9473 if (IS_LOOP_SOUND(sound))
9474 PlaySoundLoop(sound);
9479 void PlayMenuSound(void)
9481 PlayMenuSoundExt(menu.sound[game_status]);
9484 void PlayMenuSoundStereo(int sound, int stereo_position)
9486 if (sound == SND_UNDEFINED)
9489 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9490 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9493 if (IS_LOOP_SOUND(sound))
9494 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9496 PlaySoundStereo(sound, stereo_position);
9499 void PlayMenuSoundIfLoopExt(int sound)
9501 if (sound == SND_UNDEFINED)
9504 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9505 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9508 if (IS_LOOP_SOUND(sound))
9509 PlaySoundLoop(sound);
9512 void PlayMenuSoundIfLoop(void)
9514 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9517 void PlayMenuMusicExt(int music)
9519 if (music == MUS_UNDEFINED)
9522 if (!setup.sound_music)
9525 if (IS_LOOP_MUSIC(music))
9526 PlayMusicLoop(music);
9531 void PlayMenuMusic(void)
9533 char *curr_music = getCurrentlyPlayingMusicFilename();
9534 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9536 if (!strEqual(curr_music, next_music))
9537 PlayMenuMusicExt(menu.music[game_status]);
9540 void PlayMenuSoundsAndMusic(void)
9546 static void FadeMenuSounds(void)
9551 static void FadeMenuMusic(void)
9553 char *curr_music = getCurrentlyPlayingMusicFilename();
9554 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9556 if (!strEqual(curr_music, next_music))
9560 void FadeMenuSoundsAndMusic(void)
9566 void PlaySoundActivating(void)
9569 PlaySound(SND_MENU_ITEM_ACTIVATING);
9573 void PlaySoundSelecting(void)
9576 PlaySound(SND_MENU_ITEM_SELECTING);
9580 void ToggleFullscreenIfNeeded(void)
9582 // if setup and video fullscreen state are already matching, nothing do do
9583 if (setup.fullscreen == video.fullscreen_enabled ||
9584 !video.fullscreen_available)
9587 SDLSetWindowFullscreen(setup.fullscreen);
9589 // set setup value according to successfully changed fullscreen mode
9590 setup.fullscreen = video.fullscreen_enabled;
9593 void ChangeWindowScalingIfNeeded(void)
9595 // if setup and video window scaling are already matching, nothing do do
9596 if (setup.window_scaling_percent == video.window_scaling_percent ||
9597 video.fullscreen_enabled)
9600 SDLSetWindowScaling(setup.window_scaling_percent);
9602 // set setup value according to successfully changed window scaling
9603 setup.window_scaling_percent = video.window_scaling_percent;
9606 void ChangeVsyncModeIfNeeded(void)
9608 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9609 int video_vsync_mode = video.vsync_mode;
9611 // if setup and video vsync mode are already matching, nothing do do
9612 if (setup_vsync_mode == video_vsync_mode)
9615 // if renderer is using OpenGL, vsync mode can directly be changed
9616 SDLSetScreenVsyncMode(setup.vsync_mode);
9618 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9619 if (video.vsync_mode == video_vsync_mode)
9621 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9623 // save backbuffer content which gets lost when re-creating screen
9624 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9626 // force re-creating screen and renderer to set new vsync mode
9627 video.fullscreen_enabled = !setup.fullscreen;
9629 // when creating new renderer, destroy textures linked to old renderer
9630 FreeAllImageTextures(); // needs old renderer to free the textures
9632 // re-create screen and renderer (including change of vsync mode)
9633 ChangeVideoModeIfNeeded(setup.fullscreen);
9635 // set setup value according to successfully changed fullscreen mode
9636 setup.fullscreen = video.fullscreen_enabled;
9638 // restore backbuffer content from temporary backbuffer backup bitmap
9639 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9640 FreeBitmap(tmp_backbuffer);
9642 // update visible window/screen
9643 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9645 // when changing vsync mode, re-create textures for new renderer
9646 InitImageTextures();
9649 // set setup value according to successfully changed vsync mode
9650 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9653 static void JoinRectangles(int *x, int *y, int *width, int *height,
9654 int x2, int y2, int width2, int height2)
9656 // do not join with "off-screen" rectangle
9657 if (x2 == -1 || y2 == -1)
9662 *width = MAX(*width, width2);
9663 *height = MAX(*height, height2);
9666 void SetAnimStatus(int anim_status_new)
9668 if (anim_status_new == GAME_MODE_MAIN)
9669 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9670 else if (anim_status_new == GAME_MODE_NAMES)
9671 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9672 else if (anim_status_new == GAME_MODE_SCORES)
9673 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9675 global.anim_status_next = anim_status_new;
9677 // directly set screen modes that are entered without fading
9678 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9679 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9680 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9681 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9682 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9683 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9684 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9685 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9686 global.anim_status = global.anim_status_next;
9689 void SetGameStatus(int game_status_new)
9691 if (game_status_new != game_status)
9692 game_status_last_screen = game_status;
9694 game_status = game_status_new;
9696 SetAnimStatus(game_status_new);
9699 void SetFontStatus(int game_status_new)
9701 static int last_game_status = -1;
9703 if (game_status_new != -1)
9705 // set game status for font use after storing last game status
9706 last_game_status = game_status;
9707 game_status = game_status_new;
9711 // reset game status after font use from last stored game status
9712 game_status = last_game_status;
9716 void ResetFontStatus(void)
9721 void SetLevelSetInfo(char *identifier, int level_nr)
9723 setString(&levelset.identifier, identifier);
9725 levelset.level_nr = level_nr;
9728 boolean CheckIfAllViewportsHaveChanged(void)
9730 // if game status has not changed, viewports have not changed either
9731 if (game_status == game_status_last)
9734 // check if all viewports have changed with current game status
9736 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9737 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9738 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9739 int new_real_sx = vp_playfield->x;
9740 int new_real_sy = vp_playfield->y;
9741 int new_full_sxsize = vp_playfield->width;
9742 int new_full_sysize = vp_playfield->height;
9743 int new_dx = vp_door_1->x;
9744 int new_dy = vp_door_1->y;
9745 int new_dxsize = vp_door_1->width;
9746 int new_dysize = vp_door_1->height;
9747 int new_vx = vp_door_2->x;
9748 int new_vy = vp_door_2->y;
9749 int new_vxsize = vp_door_2->width;
9750 int new_vysize = vp_door_2->height;
9752 boolean playfield_viewport_has_changed =
9753 (new_real_sx != REAL_SX ||
9754 new_real_sy != REAL_SY ||
9755 new_full_sxsize != FULL_SXSIZE ||
9756 new_full_sysize != FULL_SYSIZE);
9758 boolean door_1_viewport_has_changed =
9761 new_dxsize != DXSIZE ||
9762 new_dysize != DYSIZE);
9764 boolean door_2_viewport_has_changed =
9767 new_vxsize != VXSIZE ||
9768 new_vysize != VYSIZE ||
9769 game_status_last == GAME_MODE_EDITOR);
9771 return (playfield_viewport_has_changed &&
9772 door_1_viewport_has_changed &&
9773 door_2_viewport_has_changed);
9776 boolean CheckFadeAll(void)
9778 return (CheckIfGlobalBorderHasChanged() ||
9779 CheckIfAllViewportsHaveChanged());
9782 void ChangeViewportPropertiesIfNeeded(void)
9784 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9785 FALSE : setup.small_game_graphics);
9786 int gfx_game_mode = getGlobalGameStatus(game_status);
9787 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9789 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9790 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9791 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9792 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9793 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9794 int new_win_xsize = vp_window->width;
9795 int new_win_ysize = vp_window->height;
9796 int border_left = vp_playfield->border_left;
9797 int border_right = vp_playfield->border_right;
9798 int border_top = vp_playfield->border_top;
9799 int border_bottom = vp_playfield->border_bottom;
9800 int new_sx = vp_playfield->x + border_left;
9801 int new_sy = vp_playfield->y + border_top;
9802 int new_sxsize = vp_playfield->width - border_left - border_right;
9803 int new_sysize = vp_playfield->height - border_top - border_bottom;
9804 int new_real_sx = vp_playfield->x;
9805 int new_real_sy = vp_playfield->y;
9806 int new_full_sxsize = vp_playfield->width;
9807 int new_full_sysize = vp_playfield->height;
9808 int new_dx = vp_door_1->x;
9809 int new_dy = vp_door_1->y;
9810 int new_dxsize = vp_door_1->width;
9811 int new_dysize = vp_door_1->height;
9812 int new_vx = vp_door_2->x;
9813 int new_vy = vp_door_2->y;
9814 int new_vxsize = vp_door_2->width;
9815 int new_vysize = vp_door_2->height;
9816 int new_ex = vp_door_3->x;
9817 int new_ey = vp_door_3->y;
9818 int new_exsize = vp_door_3->width;
9819 int new_eysize = vp_door_3->height;
9820 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9821 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9822 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9823 int new_scr_fieldx = new_sxsize / tilesize;
9824 int new_scr_fieldy = new_sysize / tilesize;
9825 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9826 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9827 boolean init_gfx_buffers = FALSE;
9828 boolean init_video_buffer = FALSE;
9829 boolean init_gadgets_and_anims = FALSE;
9830 boolean init_em_graphics = FALSE;
9832 if (new_win_xsize != WIN_XSIZE ||
9833 new_win_ysize != WIN_YSIZE)
9835 WIN_XSIZE = new_win_xsize;
9836 WIN_YSIZE = new_win_ysize;
9838 init_video_buffer = TRUE;
9839 init_gfx_buffers = TRUE;
9840 init_gadgets_and_anims = TRUE;
9842 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9845 if (new_scr_fieldx != SCR_FIELDX ||
9846 new_scr_fieldy != SCR_FIELDY)
9848 // this always toggles between MAIN and GAME when using small tile size
9850 SCR_FIELDX = new_scr_fieldx;
9851 SCR_FIELDY = new_scr_fieldy;
9853 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9864 new_sxsize != SXSIZE ||
9865 new_sysize != SYSIZE ||
9866 new_dxsize != DXSIZE ||
9867 new_dysize != DYSIZE ||
9868 new_vxsize != VXSIZE ||
9869 new_vysize != VYSIZE ||
9870 new_exsize != EXSIZE ||
9871 new_eysize != EYSIZE ||
9872 new_real_sx != REAL_SX ||
9873 new_real_sy != REAL_SY ||
9874 new_full_sxsize != FULL_SXSIZE ||
9875 new_full_sysize != FULL_SYSIZE ||
9876 new_tilesize_var != TILESIZE_VAR
9879 // ------------------------------------------------------------------------
9880 // determine next fading area for changed viewport definitions
9881 // ------------------------------------------------------------------------
9883 // start with current playfield area (default fading area)
9886 FADE_SXSIZE = FULL_SXSIZE;
9887 FADE_SYSIZE = FULL_SYSIZE;
9889 // add new playfield area if position or size has changed
9890 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9891 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9893 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9894 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9897 // add current and new door 1 area if position or size has changed
9898 if (new_dx != DX || new_dy != DY ||
9899 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9901 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9902 DX, DY, DXSIZE, DYSIZE);
9903 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9904 new_dx, new_dy, new_dxsize, new_dysize);
9907 // add current and new door 2 area if position or size has changed
9908 if (new_vx != VX || new_vy != VY ||
9909 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9911 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9912 VX, VY, VXSIZE, VYSIZE);
9913 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9914 new_vx, new_vy, new_vxsize, new_vysize);
9917 // ------------------------------------------------------------------------
9918 // handle changed tile size
9919 // ------------------------------------------------------------------------
9921 if (new_tilesize_var != TILESIZE_VAR)
9923 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9925 // changing tile size invalidates scroll values of engine snapshots
9926 FreeEngineSnapshotSingle();
9928 // changing tile size requires update of graphic mapping for EM engine
9929 init_em_graphics = TRUE;
9940 SXSIZE = new_sxsize;
9941 SYSIZE = new_sysize;
9942 DXSIZE = new_dxsize;
9943 DYSIZE = new_dysize;
9944 VXSIZE = new_vxsize;
9945 VYSIZE = new_vysize;
9946 EXSIZE = new_exsize;
9947 EYSIZE = new_eysize;
9948 REAL_SX = new_real_sx;
9949 REAL_SY = new_real_sy;
9950 FULL_SXSIZE = new_full_sxsize;
9951 FULL_SYSIZE = new_full_sysize;
9952 TILESIZE_VAR = new_tilesize_var;
9954 init_gfx_buffers = TRUE;
9955 init_gadgets_and_anims = TRUE;
9957 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9958 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9961 if (init_gfx_buffers)
9963 // Debug("tools:viewport", "init_gfx_buffers");
9965 SCR_FIELDX = new_scr_fieldx_buffers;
9966 SCR_FIELDY = new_scr_fieldy_buffers;
9970 SCR_FIELDX = new_scr_fieldx;
9971 SCR_FIELDY = new_scr_fieldy;
9973 SetDrawDeactivationMask(REDRAW_NONE);
9974 SetDrawBackgroundMask(REDRAW_FIELD);
9977 if (init_video_buffer)
9979 // Debug("tools:viewport", "init_video_buffer");
9981 FreeAllImageTextures(); // needs old renderer to free the textures
9983 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9984 InitImageTextures();
9987 if (init_gadgets_and_anims)
9989 // Debug("tools:viewport", "init_gadgets_and_anims");
9992 InitGlobalAnimations();
9995 if (init_em_graphics)
9997 InitGraphicInfo_EM();
10001 void OpenURL(char *url)
10003 #if SDL_VERSION_ATLEAST(2,0,14)
10006 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
10007 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
10008 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
10012 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
10014 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
10018 // ============================================================================
10020 // ============================================================================
10022 #if defined(PLATFORM_WINDOWS)
10023 /* FILETIME of Jan 1 1970 00:00:00. */
10024 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
10027 * timezone information is stored outside the kernel so tzp isn't used anymore.
10029 * Note: this function is not for Win32 high precision timing purpose. See
10032 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
10034 FILETIME file_time;
10035 SYSTEMTIME system_time;
10036 ULARGE_INTEGER ularge;
10038 GetSystemTime(&system_time);
10039 SystemTimeToFileTime(&system_time, &file_time);
10040 ularge.LowPart = file_time.dwLowDateTime;
10041 ularge.HighPart = file_time.dwHighDateTime;
10043 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10044 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10050 static char *test_init_uuid_random_function_simple(void)
10052 static char seed_text[100];
10053 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10055 sprintf(seed_text, "%d", seed);
10060 static char *test_init_uuid_random_function_better(void)
10062 static char seed_text[100];
10063 struct timeval current_time;
10065 gettimeofday(¤t_time, NULL);
10067 prng_seed_bytes(¤t_time, sizeof(current_time));
10069 sprintf(seed_text, "%ld.%ld",
10070 (long)current_time.tv_sec,
10071 (long)current_time.tv_usec);
10076 #if defined(PLATFORM_WINDOWS)
10077 static char *test_init_uuid_random_function_better_windows(void)
10079 static char seed_text[100];
10080 struct timeval current_time;
10082 gettimeofday_windows(¤t_time, NULL);
10084 prng_seed_bytes(¤t_time, sizeof(current_time));
10086 sprintf(seed_text, "%ld.%ld",
10087 (long)current_time.tv_sec,
10088 (long)current_time.tv_usec);
10094 static unsigned int test_uuid_random_function_simple(int max)
10096 return GetSimpleRandom(max);
10099 static unsigned int test_uuid_random_function_better(int max)
10101 return (max > 0 ? prng_get_uint() % max : 0);
10104 #if defined(PLATFORM_WINDOWS)
10105 #define NUM_UUID_TESTS 3
10107 #define NUM_UUID_TESTS 2
10110 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10112 struct hashtable *hash_seeds =
10113 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10114 struct hashtable *hash_uuids =
10115 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10116 static char message[100];
10119 char *random_name = (nr == 0 ? "simple" : "better");
10120 char *random_type = (always_seed ? "always" : "only once");
10121 char *(*init_random_function)(void) =
10123 test_init_uuid_random_function_simple :
10124 test_init_uuid_random_function_better);
10125 unsigned int (*random_function)(int) =
10127 test_uuid_random_function_simple :
10128 test_uuid_random_function_better);
10131 #if defined(PLATFORM_WINDOWS)
10134 random_name = "windows";
10135 init_random_function = test_init_uuid_random_function_better_windows;
10141 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10142 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10144 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10145 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10146 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10148 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10152 // always initialize random number generator at least once
10153 init_random_function();
10155 unsigned int time_start = SDL_GetTicks();
10157 for (i = 0; i < num_uuids; i++)
10161 char *seed = getStringCopy(init_random_function());
10163 hashtable_remove(hash_seeds, seed);
10164 hashtable_insert(hash_seeds, seed, "1");
10167 char *uuid = getStringCopy(getUUIDExt(random_function));
10169 hashtable_remove(hash_uuids, uuid);
10170 hashtable_insert(hash_uuids, uuid, "1");
10173 int num_unique_seeds = hashtable_count(hash_seeds);
10174 int num_unique_uuids = hashtable_count(hash_uuids);
10176 unsigned int time_needed = SDL_GetTicks() - time_start;
10178 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10180 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10183 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10185 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10186 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10188 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10190 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10192 Request(message, REQ_CONFIRM);
10194 hashtable_destroy(hash_seeds, 0);
10195 hashtable_destroy(hash_uuids, 0);
10198 void TestGeneratingUUIDs(void)
10200 int num_uuids = 1000000;
10203 for (i = 0; i < NUM_UUID_TESTS; i++)
10204 for (j = 0; j < 2; j++)
10205 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10207 CloseAllAndExit(0);