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 &&
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 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
3259 int delay_value_normal = request.step_delay;
3260 int delay_value_fast = delay_value_normal / 2;
3261 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3262 boolean no_delay = (tape.warp_forward);
3263 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3264 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3265 DelayCounter anim_delay = { anim_delay_value };
3267 int tile_size = MAX(request.step_offset, 1);
3268 int max_xsize = request.width / tile_size;
3269 int max_ysize = request.height / tile_size;
3270 int max_xsize_inner = max_xsize - 2;
3271 int max_ysize_inner = max_ysize - 2;
3273 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3274 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3275 int xend = max_xsize_inner;
3276 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3277 int xstep = (xstart < xend ? 1 : 0);
3278 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3280 int end = MAX(xend - xstart, yend - ystart);
3283 if (setup.quick_doors)
3290 for (i = start; i <= end; i++)
3292 int last_frame = end; // last frame of this "for" loop
3293 int x = xstart + i * xstep;
3294 int y = ystart + i * ystep;
3295 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3296 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3297 int xsize_size_left = (xsize - 1) * tile_size;
3298 int ysize_size_top = (ysize - 1) * tile_size;
3299 int max_xsize_pos = (max_xsize - 1) * tile_size;
3300 int max_ysize_pos = (max_ysize - 1) * tile_size;
3301 int width = xsize * tile_size;
3302 int height = ysize * tile_size;
3308 HandleGameActions();
3310 setRequestPosition(&src_x, &src_y, FALSE);
3311 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3313 for (yy = 0; yy < 2; yy++)
3315 for (xx = 0; xx < 2; xx++)
3317 int src_xx = src_x + xx * max_xsize_pos;
3318 int src_yy = src_y + yy * max_ysize_pos;
3319 int dst_xx = dst_x + xx * xsize_size_left;
3320 int dst_yy = dst_y + yy * ysize_size_top;
3321 int xx_size = (xx ? tile_size : xsize_size_left);
3322 int yy_size = (yy ? tile_size : ysize_size_top);
3324 // draw partial (animated) envelope request to temporary bitmap
3325 BlitBitmap(bitmap_db_store_1, bitmap_db_store_2,
3326 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3330 // prepare partial (animated) envelope request from temporary bitmap
3331 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, dst_x, dst_y,
3334 redraw_mask |= REDRAW_FIELD;
3338 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3341 ClearAutoRepeatKeyEvents();
3344 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3346 int graphic = IMG_BACKGROUND_REQUEST;
3347 int sound_opening = SND_REQUEST_OPENING;
3348 int sound_closing = SND_REQUEST_CLOSING;
3349 int anim_mode_1 = request.anim_mode; // (higher priority)
3350 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3351 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3352 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3353 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3355 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3357 if (action == ACTION_OPENING)
3359 DrawEnvelopeRequest(text, req_state);
3361 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3363 if (anim_mode == ANIM_DEFAULT)
3364 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3366 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3370 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3372 if (anim_mode != ANIM_NONE)
3373 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3375 if (anim_mode == ANIM_DEFAULT)
3376 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3379 game.envelope_active = FALSE;
3382 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3384 if (IS_MM_WALL(element))
3386 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3392 int graphic = el2preimg(element);
3394 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3395 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3400 void DrawLevel(int draw_background_mask)
3404 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3405 SetDrawBackgroundMask(draw_background_mask);
3409 for (x = BX1; x <= BX2; x++)
3410 for (y = BY1; y <= BY2; y++)
3411 DrawScreenField(x, y);
3413 redraw_mask |= REDRAW_FIELD;
3416 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3421 for (x = 0; x < size_x; x++)
3422 for (y = 0; y < size_y; y++)
3423 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3425 redraw_mask |= REDRAW_FIELD;
3428 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3432 for (x = 0; x < size_x; x++)
3433 for (y = 0; y < size_y; y++)
3434 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3436 redraw_mask |= REDRAW_FIELD;
3439 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3441 boolean show_level_border = (BorderElement != EL_EMPTY);
3442 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3443 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3444 int tile_size = preview.tile_size;
3445 int preview_width = preview.xsize * tile_size;
3446 int preview_height = preview.ysize * tile_size;
3447 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3448 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3449 int real_preview_width = real_preview_xsize * tile_size;
3450 int real_preview_height = real_preview_ysize * tile_size;
3451 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3452 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3455 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3458 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3460 dst_x += (preview_width - real_preview_width) / 2;
3461 dst_y += (preview_height - real_preview_height) / 2;
3463 for (x = 0; x < real_preview_xsize; x++)
3465 for (y = 0; y < real_preview_ysize; y++)
3467 int lx = from_x + x + (show_level_border ? -1 : 0);
3468 int ly = from_y + y + (show_level_border ? -1 : 0);
3469 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3470 getBorderElement(lx, ly));
3472 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3473 element, tile_size);
3477 redraw_mask |= REDRAW_FIELD;
3480 #define MICROLABEL_EMPTY 0
3481 #define MICROLABEL_LEVEL_NAME 1
3482 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3483 #define MICROLABEL_LEVEL_AUTHOR 3
3484 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3485 #define MICROLABEL_IMPORTED_FROM 5
3486 #define MICROLABEL_IMPORTED_BY_HEAD 6
3487 #define MICROLABEL_IMPORTED_BY 7
3489 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3491 int max_text_width = SXSIZE;
3492 int font_width = getFontWidth(font_nr);
3494 if (pos->align == ALIGN_CENTER)
3495 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3496 else if (pos->align == ALIGN_RIGHT)
3497 max_text_width = pos->x;
3499 max_text_width = SXSIZE - pos->x;
3501 return max_text_width / font_width;
3504 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3506 char label_text[MAX_OUTPUT_LINESIZE + 1];
3507 int max_len_label_text;
3508 int font_nr = pos->font;
3511 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3514 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3515 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3516 mode == MICROLABEL_IMPORTED_BY_HEAD)
3517 font_nr = pos->font_alt;
3519 max_len_label_text = getMaxTextLength(pos, font_nr);
3521 if (pos->size != -1)
3522 max_len_label_text = pos->size;
3524 for (i = 0; i < max_len_label_text; i++)
3525 label_text[i] = ' ';
3526 label_text[max_len_label_text] = '\0';
3528 if (strlen(label_text) > 0)
3529 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3532 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3533 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3534 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3535 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3536 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3537 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3538 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3539 max_len_label_text);
3540 label_text[max_len_label_text] = '\0';
3542 if (strlen(label_text) > 0)
3543 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3545 redraw_mask |= REDRAW_FIELD;
3548 static void DrawPreviewLevelLabel(int mode)
3550 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3553 static void DrawPreviewLevelInfo(int mode)
3555 if (mode == MICROLABEL_LEVEL_NAME)
3556 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3557 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3558 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3561 static void DrawPreviewLevelExt(boolean restart)
3563 static DelayCounter scroll_delay = { 0 };
3564 static DelayCounter label_delay = { 0 };
3565 static int from_x, from_y, scroll_direction;
3566 static int label_state, label_counter;
3567 boolean show_level_border = (BorderElement != EL_EMPTY);
3568 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3569 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3571 scroll_delay.value = preview.step_delay;
3572 label_delay.value = MICROLEVEL_LABEL_DELAY;
3579 if (preview.anim_mode == ANIM_CENTERED)
3581 if (level_xsize > preview.xsize)
3582 from_x = (level_xsize - preview.xsize) / 2;
3583 if (level_ysize > preview.ysize)
3584 from_y = (level_ysize - preview.ysize) / 2;
3587 from_x += preview.xoffset;
3588 from_y += preview.yoffset;
3590 scroll_direction = MV_RIGHT;
3594 DrawPreviewLevelPlayfield(from_x, from_y);
3595 DrawPreviewLevelLabel(label_state);
3597 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3598 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3600 // initialize delay counters
3601 ResetDelayCounter(&scroll_delay);
3602 ResetDelayCounter(&label_delay);
3604 if (leveldir_current->name)
3606 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3607 char label_text[MAX_OUTPUT_LINESIZE + 1];
3608 int font_nr = pos->font;
3609 int max_len_label_text = getMaxTextLength(pos, font_nr);
3611 if (pos->size != -1)
3612 max_len_label_text = pos->size;
3614 strncpy(label_text, leveldir_current->name, max_len_label_text);
3615 label_text[max_len_label_text] = '\0';
3617 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3618 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3624 // scroll preview level, if needed
3625 if (preview.anim_mode != ANIM_NONE &&
3626 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3627 DelayReached(&scroll_delay))
3629 switch (scroll_direction)
3634 from_x -= preview.step_offset;
3635 from_x = (from_x < 0 ? 0 : from_x);
3638 scroll_direction = MV_UP;
3642 if (from_x < level_xsize - preview.xsize)
3644 from_x += preview.step_offset;
3645 from_x = (from_x > level_xsize - preview.xsize ?
3646 level_xsize - preview.xsize : from_x);
3649 scroll_direction = MV_DOWN;
3655 from_y -= preview.step_offset;
3656 from_y = (from_y < 0 ? 0 : from_y);
3659 scroll_direction = MV_RIGHT;
3663 if (from_y < level_ysize - preview.ysize)
3665 from_y += preview.step_offset;
3666 from_y = (from_y > level_ysize - preview.ysize ?
3667 level_ysize - preview.ysize : from_y);
3670 scroll_direction = MV_LEFT;
3677 DrawPreviewLevelPlayfield(from_x, from_y);
3680 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3681 // redraw micro level label, if needed
3682 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3683 !strEqual(level.author, ANONYMOUS_NAME) &&
3684 !strEqual(level.author, leveldir_current->name) &&
3685 DelayReached(&label_delay))
3687 int max_label_counter = 23;
3689 if (leveldir_current->imported_from != NULL &&
3690 strlen(leveldir_current->imported_from) > 0)
3691 max_label_counter += 14;
3692 if (leveldir_current->imported_by != NULL &&
3693 strlen(leveldir_current->imported_by) > 0)
3694 max_label_counter += 14;
3696 label_counter = (label_counter + 1) % max_label_counter;
3697 label_state = (label_counter >= 0 && label_counter <= 7 ?
3698 MICROLABEL_LEVEL_NAME :
3699 label_counter >= 9 && label_counter <= 12 ?
3700 MICROLABEL_LEVEL_AUTHOR_HEAD :
3701 label_counter >= 14 && label_counter <= 21 ?
3702 MICROLABEL_LEVEL_AUTHOR :
3703 label_counter >= 23 && label_counter <= 26 ?
3704 MICROLABEL_IMPORTED_FROM_HEAD :
3705 label_counter >= 28 && label_counter <= 35 ?
3706 MICROLABEL_IMPORTED_FROM :
3707 label_counter >= 37 && label_counter <= 40 ?
3708 MICROLABEL_IMPORTED_BY_HEAD :
3709 label_counter >= 42 && label_counter <= 49 ?
3710 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3712 if (leveldir_current->imported_from == NULL &&
3713 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3714 label_state == MICROLABEL_IMPORTED_FROM))
3715 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3716 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3718 DrawPreviewLevelLabel(label_state);
3722 void DrawPreviewPlayers(void)
3724 if (game_status != GAME_MODE_MAIN)
3727 // do not draw preview players if level preview redefined, but players aren't
3728 if (preview.redefined && !menu.main.preview_players.redefined)
3731 boolean player_found[MAX_PLAYERS];
3732 int num_players = 0;
3735 for (i = 0; i < MAX_PLAYERS; i++)
3736 player_found[i] = FALSE;
3738 // check which players can be found in the level (simple approach)
3739 for (x = 0; x < lev_fieldx; x++)
3741 for (y = 0; y < lev_fieldy; y++)
3743 int element = level.field[x][y];
3745 if (IS_PLAYER_ELEMENT(element))
3747 int player_nr = GET_PLAYER_NR(element);
3749 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3751 if (!player_found[player_nr])
3754 player_found[player_nr] = TRUE;
3759 struct TextPosInfo *pos = &menu.main.preview_players;
3760 int tile_size = pos->tile_size;
3761 int border_size = pos->border_size;
3762 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3763 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3764 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3765 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3766 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3767 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3768 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3769 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3770 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3771 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3772 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3773 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3775 // clear area in which the players will be drawn
3776 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3777 max_players_width, max_players_height);
3779 if (!network.enabled && !setup.team_mode)
3782 // only draw players if level is suited for team mode
3783 if (num_players < 2)
3786 // draw all players that were found in the level
3787 for (i = 0; i < MAX_PLAYERS; i++)
3789 if (player_found[i])
3791 int graphic = el2img(EL_PLAYER_1 + i);
3793 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3795 xpos += player_xoffset;
3796 ypos += player_yoffset;
3801 void DrawPreviewLevelInitial(void)
3803 DrawPreviewLevelExt(TRUE);
3804 DrawPreviewPlayers();
3807 void DrawPreviewLevelAnimation(void)
3809 DrawPreviewLevelExt(FALSE);
3812 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3813 int border_size, int font_nr)
3815 int graphic = el2img(EL_PLAYER_1 + player_nr);
3816 int font_height = getFontHeight(font_nr);
3817 int player_height = MAX(tile_size, font_height);
3818 int xoffset_text = tile_size + border_size;
3819 int yoffset_text = (player_height - font_height) / 2;
3820 int yoffset_graphic = (player_height - tile_size) / 2;
3821 char *player_name = getNetworkPlayerName(player_nr + 1);
3823 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3825 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3828 static void DrawNetworkPlayersExt(boolean force)
3830 if (game_status != GAME_MODE_MAIN)
3833 if (!network.connected && !force)
3836 // do not draw network players if level preview redefined, but players aren't
3837 if (preview.redefined && !menu.main.network_players.redefined)
3840 int num_players = 0;
3843 for (i = 0; i < MAX_PLAYERS; i++)
3844 if (stored_player[i].connected_network)
3847 struct TextPosInfo *pos = &menu.main.network_players;
3848 int tile_size = pos->tile_size;
3849 int border_size = pos->border_size;
3850 int xoffset_text = tile_size + border_size;
3851 int font_nr = pos->font;
3852 int font_width = getFontWidth(font_nr);
3853 int font_height = getFontHeight(font_nr);
3854 int player_height = MAX(tile_size, font_height);
3855 int player_yoffset = player_height + border_size;
3856 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3857 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3858 int all_players_height = num_players * player_yoffset - border_size;
3859 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3860 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3861 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3863 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3864 max_players_width, max_players_height);
3866 // first draw local network player ...
3867 for (i = 0; i < MAX_PLAYERS; i++)
3869 if (stored_player[i].connected_network &&
3870 stored_player[i].connected_locally)
3872 char *player_name = getNetworkPlayerName(i + 1);
3873 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3874 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3876 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3878 ypos += player_yoffset;
3882 // ... then draw all other network players
3883 for (i = 0; i < MAX_PLAYERS; i++)
3885 if (stored_player[i].connected_network &&
3886 !stored_player[i].connected_locally)
3888 char *player_name = getNetworkPlayerName(i + 1);
3889 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3890 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3892 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3894 ypos += player_yoffset;
3899 void DrawNetworkPlayers(void)
3901 DrawNetworkPlayersExt(FALSE);
3904 void ClearNetworkPlayers(void)
3906 DrawNetworkPlayersExt(TRUE);
3909 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3910 int graphic, int lx, int ly,
3913 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3915 if (mask_mode == USE_MASKING)
3916 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3918 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3921 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3922 int graphic, int sync_frame, int mask_mode)
3924 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3926 if (mask_mode == USE_MASKING)
3927 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3929 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3932 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3933 int graphic, int sync_frame, int tilesize,
3936 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3938 if (mask_mode == USE_MASKING)
3939 DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
3941 DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
3944 static void DrawGraphicAnimation(int x, int y, int graphic)
3946 int lx = LEVELX(x), ly = LEVELY(y);
3947 int mask_mode = NO_MASKING;
3949 if (!IN_SCR_FIELD(x, y))
3952 if (game.use_masked_elements)
3954 if (Tile[lx][ly] != EL_EMPTY)
3956 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3958 mask_mode = USE_MASKING;
3962 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3963 graphic, lx, ly, mask_mode);
3965 MarkTileDirty(x, y);
3968 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3970 int lx = LEVELX(x), ly = LEVELY(y);
3971 int mask_mode = NO_MASKING;
3973 if (!IN_SCR_FIELD(x, y))
3976 if (game.use_masked_elements)
3978 if (Tile[lx][ly] != EL_EMPTY)
3980 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3982 mask_mode = USE_MASKING;
3986 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3987 graphic, lx, ly, mask_mode);
3989 MarkTileDirty(x, y);
3992 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3994 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3997 void DrawLevelElementAnimation(int x, int y, int element)
3999 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4001 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4004 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
4006 int sx = SCREENX(x), sy = SCREENY(y);
4008 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4011 if (Tile[x][y] == EL_EMPTY)
4012 graphic = el2img(GfxElementEmpty[x][y]);
4014 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4017 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4020 DrawGraphicAnimation(sx, sy, graphic);
4023 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4024 DrawLevelFieldCrumbled(x, y);
4026 if (GFX_CRUMBLED(Tile[x][y]))
4027 DrawLevelFieldCrumbled(x, y);
4031 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4033 int sx = SCREENX(x), sy = SCREENY(y);
4036 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4039 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4041 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4044 DrawGraphicAnimation(sx, sy, graphic);
4046 if (GFX_CRUMBLED(element))
4047 DrawLevelFieldCrumbled(x, y);
4050 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4052 if (player->use_murphy)
4054 // this works only because currently only one player can be "murphy" ...
4055 static int last_horizontal_dir = MV_LEFT;
4056 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4058 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4059 last_horizontal_dir = move_dir;
4061 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4063 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4065 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4071 return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4074 static boolean equalGraphics(int graphic1, int graphic2)
4076 struct GraphicInfo *g1 = &graphic_info[graphic1];
4077 struct GraphicInfo *g2 = &graphic_info[graphic2];
4079 return (g1->bitmap == g2->bitmap &&
4080 g1->src_x == g2->src_x &&
4081 g1->src_y == g2->src_y &&
4082 g1->anim_frames == g2->anim_frames &&
4083 g1->anim_delay == g2->anim_delay &&
4084 g1->anim_mode == g2->anim_mode);
4087 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4091 DRAW_PLAYER_STAGE_INIT = 0,
4092 DRAW_PLAYER_STAGE_LAST_FIELD,
4093 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4094 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4095 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4096 DRAW_PLAYER_STAGE_PLAYER,
4098 DRAW_PLAYER_STAGE_PLAYER,
4099 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4101 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4102 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4104 NUM_DRAW_PLAYER_STAGES
4107 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4109 static int static_last_player_graphic[MAX_PLAYERS];
4110 static int static_last_player_frame[MAX_PLAYERS];
4111 static boolean static_player_is_opaque[MAX_PLAYERS];
4112 static boolean draw_player[MAX_PLAYERS];
4113 int pnr = player->index_nr;
4115 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4117 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4118 static_last_player_frame[pnr] = player->Frame;
4119 static_player_is_opaque[pnr] = FALSE;
4121 draw_player[pnr] = TRUE;
4124 if (!draw_player[pnr])
4128 if (!IN_LEV_FIELD(player->jx, player->jy))
4130 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4131 Debug("draw:DrawPlayerExt", "This should never happen!");
4133 draw_player[pnr] = FALSE;
4139 int last_player_graphic = static_last_player_graphic[pnr];
4140 int last_player_frame = static_last_player_frame[pnr];
4141 boolean player_is_opaque = static_player_is_opaque[pnr];
4143 int jx = player->jx;
4144 int jy = player->jy;
4145 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4146 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4147 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4148 int last_jx = (player->is_moving ? jx - dx : jx);
4149 int last_jy = (player->is_moving ? jy - dy : jy);
4150 int next_jx = jx + dx;
4151 int next_jy = jy + dy;
4152 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4153 int sx = SCREENX(jx);
4154 int sy = SCREENY(jy);
4155 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4156 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4157 int element = Tile[jx][jy];
4158 int last_element = Tile[last_jx][last_jy];
4159 int action = (player->is_pushing ? ACTION_PUSHING :
4160 player->is_digging ? ACTION_DIGGING :
4161 player->is_collecting ? ACTION_COLLECTING :
4162 player->is_moving ? ACTION_MOVING :
4163 player->is_snapping ? ACTION_SNAPPING :
4164 player->is_dropping ? ACTION_DROPPING :
4165 player->is_waiting ? player->action_waiting :
4168 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4170 // ------------------------------------------------------------------------
4171 // initialize drawing the player
4172 // ------------------------------------------------------------------------
4174 draw_player[pnr] = FALSE;
4176 // GfxElement[][] is set to the element the player is digging or collecting;
4177 // remove also for off-screen player if the player is not moving anymore
4178 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4179 GfxElement[jx][jy] = EL_UNDEFINED;
4181 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4184 if (element == EL_EXPLOSION)
4187 InitPlayerGfxAnimation(player, action, move_dir);
4189 draw_player[pnr] = TRUE;
4191 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4193 // ------------------------------------------------------------------------
4194 // draw things in the field the player is leaving, if needed
4195 // ------------------------------------------------------------------------
4197 if (!IN_SCR_FIELD(sx, sy))
4198 draw_player[pnr] = FALSE;
4200 if (!player->is_moving)
4203 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4205 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4207 if (last_element == EL_DYNAMITE_ACTIVE ||
4208 last_element == EL_EM_DYNAMITE_ACTIVE ||
4209 last_element == EL_SP_DISK_RED_ACTIVE)
4210 DrawDynamite(last_jx, last_jy);
4212 DrawLevelFieldThruMask(last_jx, last_jy);
4214 else if (last_element == EL_DYNAMITE_ACTIVE ||
4215 last_element == EL_EM_DYNAMITE_ACTIVE ||
4216 last_element == EL_SP_DISK_RED_ACTIVE)
4217 DrawDynamite(last_jx, last_jy);
4219 DrawLevelField(last_jx, last_jy);
4221 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4223 // ------------------------------------------------------------------------
4224 // draw things behind the player, if needed
4225 // ------------------------------------------------------------------------
4229 DrawLevelElement(jx, jy, Back[jx][jy]);
4234 if (IS_ACTIVE_BOMB(element))
4236 DrawLevelElement(jx, jy, EL_EMPTY);
4241 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4243 int old_element = GfxElement[jx][jy];
4244 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4245 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4247 if (GFX_CRUMBLED(old_element))
4248 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4250 DrawScreenGraphic(sx, sy, old_graphic, frame);
4252 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4253 static_player_is_opaque[pnr] = TRUE;
4257 GfxElement[jx][jy] = EL_UNDEFINED;
4259 // make sure that pushed elements are drawn with correct frame rate
4260 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4262 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4263 GfxFrame[jx][jy] = player->StepFrame;
4265 DrawLevelField(jx, jy);
4268 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4270 // ------------------------------------------------------------------------
4271 // draw things the player is pushing, if needed
4272 // ------------------------------------------------------------------------
4274 if (!player->is_pushing || !player->is_moving)
4277 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4280 int gfx_frame = GfxFrame[jx][jy];
4282 if (!IS_MOVING(jx, jy)) // push movement already finished
4284 element = Tile[next_jx][next_jy];
4285 gfx_frame = GfxFrame[next_jx][next_jy];
4288 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4289 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4290 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4292 // draw background element under pushed element (like the Sokoban field)
4293 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4295 // this allows transparent pushing animation over non-black background
4298 DrawLevelElement(jx, jy, Back[jx][jy]);
4300 DrawLevelElement(jx, jy, EL_EMPTY);
4303 if (Back[next_jx][next_jy])
4304 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4306 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4308 int px = SCREENX(jx), py = SCREENY(jy);
4309 int pxx = (TILEX - ABS(sxx)) * dx;
4310 int pyy = (TILEY - ABS(syy)) * dy;
4313 // do not draw (EM style) pushing animation when pushing is finished
4314 // (two-tile animations usually do not contain start and end frame)
4315 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4316 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4318 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4320 // masked drawing is needed for EMC style (double) movement graphics
4321 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4322 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4325 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4327 // ------------------------------------------------------------------------
4328 // draw player himself
4329 // ------------------------------------------------------------------------
4331 int graphic = getPlayerGraphic(player, move_dir);
4333 // in the case of changed player action or direction, prevent the current
4334 // animation frame from being restarted for identical animations
4335 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4336 player->Frame = last_player_frame;
4338 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4340 if (player_is_opaque)
4341 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4343 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4345 if (SHIELD_ON(player))
4347 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4348 IMG_SHIELD_NORMAL_ACTIVE);
4349 frame = getGraphicAnimationFrame(graphic, -1);
4351 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4354 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4356 // ------------------------------------------------------------------------
4357 // draw things in front of player (active dynamite or dynabombs)
4358 // ------------------------------------------------------------------------
4360 if (IS_ACTIVE_BOMB(element))
4362 int graphic = el2img(element);
4363 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4365 if (game.emulation == EMU_SUPAPLEX)
4366 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4368 DrawGraphicThruMask(sx, sy, graphic, frame);
4371 if (player_is_moving && last_element == EL_EXPLOSION)
4373 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4374 GfxElement[last_jx][last_jy] : EL_EMPTY);
4375 int graphic = el_act2img(element, ACTION_EXPLODING);
4376 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4377 int phase = ExplodePhase[last_jx][last_jy] - 1;
4378 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4381 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4384 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4386 // ------------------------------------------------------------------------
4387 // draw elements the player is just walking/passing through/under
4388 // ------------------------------------------------------------------------
4390 if (player_is_moving)
4392 // handle the field the player is leaving ...
4393 if (IS_ACCESSIBLE_INSIDE(last_element))
4394 DrawLevelField(last_jx, last_jy);
4395 else if (IS_ACCESSIBLE_UNDER(last_element))
4396 DrawLevelFieldThruMask(last_jx, last_jy);
4399 // do not redraw accessible elements if the player is just pushing them
4400 if (!player_is_moving || !player->is_pushing)
4402 // ... and the field the player is entering
4403 if (IS_ACCESSIBLE_INSIDE(element))
4404 DrawLevelField(jx, jy);
4405 else if (IS_ACCESSIBLE_UNDER(element))
4406 DrawLevelFieldThruMask(jx, jy);
4409 MarkTileDirty(sx, sy);
4413 void DrawPlayer(struct PlayerInfo *player)
4417 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4418 DrawPlayerExt(player, i);
4421 void DrawAllPlayers(void)
4425 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4426 for (j = 0; j < MAX_PLAYERS; j++)
4427 if (stored_player[j].active)
4428 DrawPlayerExt(&stored_player[j], i);
4431 void DrawPlayerField(int x, int y)
4433 if (!IS_PLAYER(x, y))
4436 DrawPlayer(PLAYERINFO(x, y));
4439 // ----------------------------------------------------------------------------
4441 void WaitForEventToContinue(void)
4443 boolean first_wait = TRUE;
4444 boolean still_wait = TRUE;
4446 if (program.headless)
4449 // simulate releasing mouse button over last gadget, if still pressed
4451 HandleGadgets(-1, -1, 0);
4453 button_status = MB_RELEASED;
4456 ClearPlayerAction();
4462 if (NextValidEvent(&event))
4466 case EVENT_BUTTONPRESS:
4467 case EVENT_FINGERPRESS:
4471 case EVENT_BUTTONRELEASE:
4472 case EVENT_FINGERRELEASE:
4473 still_wait = first_wait;
4476 case EVENT_KEYPRESS:
4477 case SDL_CONTROLLERBUTTONDOWN:
4478 case SDL_JOYBUTTONDOWN:
4483 HandleOtherEvents(&event);
4487 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4492 if (!PendingEvent())
4497 #define MAX_REQUEST_LINES 13
4498 #define MAX_REQUEST_LINE_FONT1_LEN 7
4499 #define MAX_REQUEST_LINE_FONT2_LEN 10
4501 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4503 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
4504 int draw_buffer_last = GetDrawtoField();
4505 int width = request.width;
4506 int height = request.height;
4510 setRequestPosition(&sx, &sy, FALSE);
4512 button_status = MB_RELEASED;
4514 request_gadget_id = -1;
4521 SetDrawtoField(draw_buffer_game);
4523 HandleGameActions();
4525 SetDrawtoField(DRAW_TO_BACKBUFFER);
4532 while (NextValidEvent(&event))
4536 case EVENT_BUTTONPRESS:
4537 case EVENT_BUTTONRELEASE:
4538 case EVENT_MOTIONNOTIFY:
4540 DrawBuffer *drawto_last = drawto;
4543 if (event.type == EVENT_MOTIONNOTIFY)
4548 motion_status = TRUE;
4549 mx = ((MotionEvent *) &event)->x;
4550 my = ((MotionEvent *) &event)->y;
4554 motion_status = FALSE;
4555 mx = ((ButtonEvent *) &event)->x;
4556 my = ((ButtonEvent *) &event)->y;
4557 if (event.type == EVENT_BUTTONPRESS)
4558 button_status = ((ButtonEvent *) &event)->button;
4560 button_status = MB_RELEASED;
4563 if (global.use_envelope_request)
4565 // draw changed button states to temporary bitmap
4566 drawto = bitmap_db_store_1;
4569 // this sets 'request_gadget_id'
4570 HandleGadgets(mx, my, button_status);
4572 if (global.use_envelope_request)
4574 // restore pointer to drawing buffer
4575 drawto = drawto_last;
4577 // prepare complete envelope request from temporary bitmap
4578 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy,
4582 switch (request_gadget_id)
4584 case TOOL_CTRL_ID_YES:
4585 case TOOL_CTRL_ID_TOUCH_YES:
4588 case TOOL_CTRL_ID_NO:
4589 case TOOL_CTRL_ID_TOUCH_NO:
4592 case TOOL_CTRL_ID_CONFIRM:
4593 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4594 result = TRUE | FALSE;
4597 case TOOL_CTRL_ID_PLAYER_1:
4600 case TOOL_CTRL_ID_PLAYER_2:
4603 case TOOL_CTRL_ID_PLAYER_3:
4606 case TOOL_CTRL_ID_PLAYER_4:
4611 // only check clickable animations if no request gadget clicked
4612 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4619 case SDL_WINDOWEVENT:
4620 HandleWindowEvent((WindowEvent *) &event);
4623 case SDL_APP_WILLENTERBACKGROUND:
4624 case SDL_APP_DIDENTERBACKGROUND:
4625 case SDL_APP_WILLENTERFOREGROUND:
4626 case SDL_APP_DIDENTERFOREGROUND:
4627 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4630 case EVENT_KEYPRESS:
4632 Key key = GetEventKey((KeyEvent *)&event);
4637 if (req_state & REQ_CONFIRM)
4646 #if defined(KSYM_Rewind)
4647 case KSYM_Rewind: // for Amazon Fire TV remote
4656 #if defined(KSYM_FastForward)
4657 case KSYM_FastForward: // for Amazon Fire TV remote
4663 HandleKeysDebug(key, KEY_PRESSED);
4667 if (req_state & REQ_PLAYER)
4669 int old_player_nr = setup.network_player_nr;
4672 result = old_player_nr + 1;
4677 result = old_player_nr + 1;
4708 case EVENT_FINGERRELEASE:
4709 case EVENT_KEYRELEASE:
4710 ClearPlayerAction();
4713 case SDL_CONTROLLERBUTTONDOWN:
4714 switch (event.cbutton.button)
4716 case SDL_CONTROLLER_BUTTON_A:
4717 case SDL_CONTROLLER_BUTTON_X:
4718 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4719 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4723 case SDL_CONTROLLER_BUTTON_B:
4724 case SDL_CONTROLLER_BUTTON_Y:
4725 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4726 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4727 case SDL_CONTROLLER_BUTTON_BACK:
4732 if (req_state & REQ_PLAYER)
4734 int old_player_nr = setup.network_player_nr;
4737 result = old_player_nr + 1;
4739 switch (event.cbutton.button)
4741 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4742 case SDL_CONTROLLER_BUTTON_Y:
4746 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4747 case SDL_CONTROLLER_BUTTON_B:
4751 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4752 case SDL_CONTROLLER_BUTTON_A:
4756 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4757 case SDL_CONTROLLER_BUTTON_X:
4768 case SDL_CONTROLLERBUTTONUP:
4769 HandleJoystickEvent(&event);
4770 ClearPlayerAction();
4774 HandleOtherEvents(&event);
4779 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4781 int joy = AnyJoystick();
4783 if (joy & JOY_BUTTON_1)
4785 else if (joy & JOY_BUTTON_2)
4788 else if (AnyJoystick())
4790 int joy = AnyJoystick();
4792 if (req_state & REQ_PLAYER)
4796 else if (joy & JOY_RIGHT)
4798 else if (joy & JOY_DOWN)
4800 else if (joy & JOY_LEFT)
4808 SetDrawtoField(draw_buffer_last);
4813 static boolean RequestDoor(char *text, unsigned int req_state)
4815 int draw_buffer_last = GetDrawtoField();
4816 unsigned int old_door_state;
4817 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4818 int font_nr = FONT_TEXT_2;
4823 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4825 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4826 font_nr = FONT_TEXT_1;
4829 if (game_status == GAME_MODE_PLAYING)
4830 BlitScreenToBitmap(backbuffer);
4832 // disable deactivated drawing when quick-loading level tape recording
4833 if (tape.playing && tape.deactivate_display)
4834 TapeDeactivateDisplayOff(TRUE);
4836 SetMouseCursor(CURSOR_DEFAULT);
4838 // pause network game while waiting for request to answer
4839 if (network.enabled &&
4840 game_status == GAME_MODE_PLAYING &&
4841 !game.all_players_gone &&
4842 req_state & REQUEST_WAIT_FOR_INPUT)
4843 SendToServer_PausePlaying();
4845 old_door_state = GetDoorState();
4847 // simulate releasing mouse button over last gadget, if still pressed
4849 HandleGadgets(-1, -1, 0);
4853 // draw released gadget before proceeding
4856 if (old_door_state & DOOR_OPEN_1)
4858 CloseDoor(DOOR_CLOSE_1);
4860 // save old door content
4861 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4862 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4865 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4866 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4868 // clear door drawing field
4869 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4871 // force DOOR font inside door area
4872 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4874 // write text for request
4875 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4877 char text_line[max_request_line_len + 1];
4883 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4885 tc = *(text_ptr + tx);
4886 // if (!tc || tc == ' ')
4887 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4891 if ((tc == '?' || tc == '!') && tl == 0)
4901 strncpy(text_line, text_ptr, tl);
4904 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4905 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4906 text_line, font_nr);
4908 text_ptr += tl + (tc == ' ' ? 1 : 0);
4909 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4914 if (req_state & REQ_ASK)
4916 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4917 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4918 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4919 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4921 else if (req_state & REQ_CONFIRM)
4923 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4924 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4926 else if (req_state & REQ_PLAYER)
4928 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4929 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4930 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4931 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4934 // copy request gadgets to door backbuffer
4935 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4937 OpenDoor(DOOR_OPEN_1);
4939 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4941 if (game_status == GAME_MODE_PLAYING)
4943 SetPanelBackground();
4944 SetDrawBackgroundMask(REDRAW_DOOR_1);
4948 SetDrawBackgroundMask(REDRAW_FIELD);
4954 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4956 // ---------- handle request buttons ----------
4957 result = RequestHandleEvents(req_state, draw_buffer_last);
4961 if (!(req_state & REQ_STAY_OPEN))
4963 CloseDoor(DOOR_CLOSE_1);
4965 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4966 (req_state & REQ_REOPEN))
4967 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4972 if (game_status == GAME_MODE_PLAYING)
4974 SetPanelBackground();
4975 SetDrawBackgroundMask(REDRAW_DOOR_1);
4979 SetDrawBackgroundMask(REDRAW_FIELD);
4982 // continue network game after request
4983 if (network.enabled &&
4984 game_status == GAME_MODE_PLAYING &&
4985 !game.all_players_gone &&
4986 req_state & REQUEST_WAIT_FOR_INPUT)
4987 SendToServer_ContinuePlaying();
4989 // restore deactivated drawing when quick-loading level tape recording
4990 if (tape.playing && tape.deactivate_display)
4991 TapeDeactivateDisplayOn();
4996 static boolean RequestEnvelope(char *text, unsigned int req_state)
4998 int draw_buffer_last = GetDrawtoField();
5001 if (game_status == GAME_MODE_PLAYING)
5002 BlitScreenToBitmap(backbuffer);
5004 // disable deactivated drawing when quick-loading level tape recording
5005 if (tape.playing && tape.deactivate_display)
5006 TapeDeactivateDisplayOff(TRUE);
5008 SetMouseCursor(CURSOR_DEFAULT);
5010 // pause network game while waiting for request to answer
5011 if (network.enabled &&
5012 game_status == GAME_MODE_PLAYING &&
5013 !game.all_players_gone &&
5014 req_state & REQUEST_WAIT_FOR_INPUT)
5015 SendToServer_PausePlaying();
5017 // simulate releasing mouse button over last gadget, if still pressed
5019 HandleGadgets(-1, -1, 0);
5023 // (replace with setting corresponding request background)
5024 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5025 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5027 // clear door drawing field
5028 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5030 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5032 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5034 if (game_status == GAME_MODE_PLAYING)
5036 SetPanelBackground();
5037 SetDrawBackgroundMask(REDRAW_DOOR_1);
5041 SetDrawBackgroundMask(REDRAW_FIELD);
5047 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5049 // ---------- handle request buttons ----------
5050 result = RequestHandleEvents(req_state, draw_buffer_last);
5054 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5058 if (game_status == GAME_MODE_PLAYING)
5060 SetPanelBackground();
5061 SetDrawBackgroundMask(REDRAW_DOOR_1);
5065 SetDrawBackgroundMask(REDRAW_FIELD);
5068 // continue network game after request
5069 if (network.enabled &&
5070 game_status == GAME_MODE_PLAYING &&
5071 !game.all_players_gone &&
5072 req_state & REQUEST_WAIT_FOR_INPUT)
5073 SendToServer_ContinuePlaying();
5075 // restore deactivated drawing when quick-loading level tape recording
5076 if (tape.playing && tape.deactivate_display)
5077 TapeDeactivateDisplayOn();
5082 boolean Request(char *text, unsigned int req_state)
5084 boolean game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
5085 boolean overlay_enabled = GetOverlayEnabled();
5088 // when showing request dialog after game ended, deactivate game panel
5090 game.panel.active = FALSE;
5092 game.request_active = 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 = 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 game_ended = (game_status == GAME_MODE_PLAYING && checkGameEnded());
5408 boolean door_panel_drawn[NUM_DOORS];
5409 boolean panel_has_doors[NUM_DOORS];
5410 boolean door_part_skip[MAX_DOOR_PARTS];
5411 boolean door_part_done[MAX_DOOR_PARTS];
5412 boolean door_part_done_all;
5413 int num_steps[MAX_DOOR_PARTS];
5414 int max_move_delay = 0; // delay for complete animations of all doors
5415 int max_step_delay = 0; // delay (ms) between two animation frames
5416 int num_move_steps = 0; // number of animation steps for all doors
5417 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5418 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5422 for (i = 0; i < NUM_DOORS; i++)
5423 panel_has_doors[i] = FALSE;
5425 for (i = 0; i < MAX_DOOR_PARTS; i++)
5427 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5428 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5429 int door_token = dpc->door_token;
5431 door_part_done[i] = FALSE;
5432 door_part_skip[i] = (!(door_state & door_token) ||
5436 for (i = 0; i < MAX_DOOR_PARTS; i++)
5438 int nr = door_part_order[i].nr;
5439 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5440 struct DoorPartPosInfo *pos = dpc->pos;
5441 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5442 int door_token = dpc->door_token;
5443 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5444 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5445 int step_xoffset = ABS(pos->step_xoffset);
5446 int step_yoffset = ABS(pos->step_yoffset);
5447 int step_delay = pos->step_delay;
5448 int current_door_state = door_state & door_token;
5449 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5450 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5451 boolean part_opening = (is_panel ? door_closing : door_opening);
5452 int start_step = (part_opening ? pos->start_step_opening :
5453 pos->start_step_closing);
5454 float move_xsize = (step_xoffset ? g->width : 0);
5455 float move_ysize = (step_yoffset ? g->height : 0);
5456 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5457 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5458 int move_steps = (move_xsteps && move_ysteps ?
5459 MIN(move_xsteps, move_ysteps) :
5460 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5461 int move_delay = move_steps * step_delay;
5463 if (door_part_skip[nr])
5466 max_move_delay = MAX(max_move_delay, move_delay);
5467 max_step_delay = (max_step_delay == 0 ? step_delay :
5468 euclid(max_step_delay, step_delay));
5469 num_steps[nr] = move_steps;
5473 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5475 panel_has_doors[door_index] = TRUE;
5479 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5481 num_move_steps = max_move_delay / max_step_delay;
5482 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5484 door_delay.value = max_step_delay;
5486 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5488 start = num_move_steps - 1;
5492 // opening door sound has priority over simultaneously closing door
5493 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5495 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5497 if (door_state & DOOR_OPEN_1)
5498 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5499 if (door_state & DOOR_OPEN_2)
5500 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5502 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5504 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5506 if (door_state & DOOR_CLOSE_1)
5507 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5508 if (door_state & DOOR_CLOSE_2)
5509 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5513 for (k = start; k < num_move_steps; k++)
5515 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5517 door_part_done_all = TRUE;
5519 for (i = 0; i < NUM_DOORS; i++)
5520 door_panel_drawn[i] = FALSE;
5522 for (i = 0; i < MAX_DOOR_PARTS; i++)
5524 int nr = door_part_order[i].nr;
5525 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5526 struct DoorPartPosInfo *pos = dpc->pos;
5527 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5528 int door_token = dpc->door_token;
5529 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5530 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5531 boolean is_panel_and_door_has_closed = FALSE;
5532 struct Rect *door_rect = &door_rect_list[door_index];
5533 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5535 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5536 int current_door_state = door_state & door_token;
5537 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5538 boolean door_closing = !door_opening;
5539 boolean part_opening = (is_panel ? door_closing : door_opening);
5540 boolean part_closing = !part_opening;
5541 int start_step = (part_opening ? pos->start_step_opening :
5542 pos->start_step_closing);
5543 int step_delay = pos->step_delay;
5544 int step_factor = step_delay / max_step_delay;
5545 int k1 = (step_factor ? k / step_factor + 1 : k);
5546 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5547 int kk = MAX(0, k2);
5550 int src_x, src_y, src_xx, src_yy;
5551 int dst_x, dst_y, dst_xx, dst_yy;
5554 if (door_part_skip[nr])
5557 if (!(door_state & door_token))
5565 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5566 int kk_door = MAX(0, k2_door);
5567 int sync_frame = kk_door * door_delay.value;
5568 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5570 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5571 &g_src_x, &g_src_y);
5576 if (!door_panel_drawn[door_index])
5578 ClearRectangle(drawto, door_rect->x, door_rect->y,
5579 door_rect->width, door_rect->height);
5581 door_panel_drawn[door_index] = TRUE;
5584 // draw opening or closing door parts
5586 if (pos->step_xoffset < 0) // door part on right side
5589 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5592 if (dst_xx + width > door_rect->width)
5593 width = door_rect->width - dst_xx;
5595 else // door part on left side
5598 dst_xx = pos->x - kk * pos->step_xoffset;
5602 src_xx = ABS(dst_xx);
5606 width = g->width - src_xx;
5608 if (width > door_rect->width)
5609 width = door_rect->width;
5611 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5614 if (pos->step_yoffset < 0) // door part on bottom side
5617 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5620 if (dst_yy + height > door_rect->height)
5621 height = door_rect->height - dst_yy;
5623 else // door part on top side
5626 dst_yy = pos->y - kk * pos->step_yoffset;
5630 src_yy = ABS(dst_yy);
5634 height = g->height - src_yy;
5637 src_x = g_src_x + src_xx;
5638 src_y = g_src_y + src_yy;
5640 dst_x = door_rect->x + dst_xx;
5641 dst_y = door_rect->y + dst_yy;
5643 is_panel_and_door_has_closed =
5646 panel_has_doors[door_index] &&
5647 k >= num_move_steps_doors_only - 1);
5649 if (width >= 0 && width <= g->width &&
5650 height >= 0 && height <= g->height &&
5651 !is_panel_and_door_has_closed)
5653 if (is_panel || !pos->draw_masked)
5654 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5657 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5661 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5663 if ((part_opening && (width < 0 || height < 0)) ||
5664 (part_closing && (width >= g->width && height >= g->height)))
5665 door_part_done[nr] = TRUE;
5667 // continue door part animations, but not panel after door has closed
5668 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5669 door_part_done_all = FALSE;
5672 if (!(door_state & DOOR_NO_DELAY))
5675 HandleGameActions();
5679 SkipUntilDelayReached(&door_delay, &k, last_frame);
5681 // prevent OS (Windows) from complaining about program not responding
5685 if (door_part_done_all)
5689 if (!(door_state & DOOR_NO_DELAY))
5691 // wait for specified door action post delay
5692 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5693 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5694 else if (door_state & DOOR_ACTION_1)
5695 door_delay.value = door_1.post_delay;
5696 else if (door_state & DOOR_ACTION_2)
5697 door_delay.value = door_2.post_delay;
5699 while (!DelayReached(&door_delay))
5702 HandleGameActions();
5709 if (door_state & DOOR_ACTION_1)
5710 door1 = door_state & DOOR_ACTION_1;
5711 if (door_state & DOOR_ACTION_2)
5712 door2 = door_state & DOOR_ACTION_2;
5714 // draw masked border over door area
5715 DrawMaskedBorder(REDRAW_DOOR_1);
5716 DrawMaskedBorder(REDRAW_DOOR_2);
5718 ClearAutoRepeatKeyEvents();
5720 return (door1 | door2);
5723 static boolean useSpecialEditorDoor(void)
5725 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5726 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5728 // do not draw special editor door if editor border defined or redefined
5729 if (graphic_info[graphic].bitmap != NULL || redefined)
5732 // do not draw special editor door if global border defined to be empty
5733 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5736 // do not draw special editor door if viewport definitions do not match
5740 EY + EYSIZE != VY + VYSIZE)
5746 void DrawSpecialEditorDoor(void)
5748 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5749 int top_border_width = gfx1->width;
5750 int top_border_height = gfx1->height;
5751 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5752 int ex = EX - outer_border;
5753 int ey = EY - outer_border;
5754 int vy = VY - outer_border;
5755 int exsize = EXSIZE + 2 * outer_border;
5757 if (!useSpecialEditorDoor())
5760 // draw bigger level editor toolbox window
5761 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5762 top_border_width, top_border_height, ex, ey - top_border_height);
5763 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5764 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5766 redraw_mask |= REDRAW_ALL;
5769 void UndrawSpecialEditorDoor(void)
5771 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5772 int top_border_width = gfx1->width;
5773 int top_border_height = gfx1->height;
5774 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5775 int ex = EX - outer_border;
5776 int ey = EY - outer_border;
5777 int ey_top = ey - top_border_height;
5778 int exsize = EXSIZE + 2 * outer_border;
5779 int eysize = EYSIZE + 2 * outer_border;
5781 if (!useSpecialEditorDoor())
5784 // draw normal tape recorder window
5785 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5787 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5788 ex, ey_top, top_border_width, top_border_height,
5790 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5791 ex, ey, exsize, eysize, ex, ey);
5795 // if screen background is set to "[NONE]", clear editor toolbox window
5796 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5797 ClearRectangle(drawto, ex, ey, exsize, eysize);
5800 redraw_mask |= REDRAW_ALL;
5804 // ---------- new tool button stuff -------------------------------------------
5809 struct TextPosInfo *pos;
5811 boolean is_touch_button;
5813 } toolbutton_info[NUM_TOOL_BUTTONS] =
5816 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5817 TOOL_CTRL_ID_YES, FALSE, "yes"
5820 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5821 TOOL_CTRL_ID_NO, FALSE, "no"
5824 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5825 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5828 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5829 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5832 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5833 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5836 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5837 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5840 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5841 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5844 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5845 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5848 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5849 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5852 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5853 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5857 void CreateToolButtons(void)
5861 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5863 int graphic = toolbutton_info[i].graphic;
5864 struct GraphicInfo *gfx = &graphic_info[graphic];
5865 struct TextPosInfo *pos = toolbutton_info[i].pos;
5866 struct GadgetInfo *gi;
5867 Bitmap *deco_bitmap = None;
5868 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5869 unsigned int event_mask = GD_EVENT_RELEASED;
5870 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5871 int base_x = (is_touch_button ? 0 : DX);
5872 int base_y = (is_touch_button ? 0 : DY);
5873 int gd_x = gfx->src_x;
5874 int gd_y = gfx->src_y;
5875 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5876 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5881 // do not use touch buttons if overlay touch buttons are disabled
5882 if (is_touch_button && !setup.touch.overlay_buttons)
5885 if (global.use_envelope_request && !is_touch_button)
5887 setRequestPosition(&base_x, &base_y, TRUE);
5889 // check if request buttons are outside of envelope and fix, if needed
5890 if (x < 0 || x + gfx->width > request.width ||
5891 y < 0 || y + gfx->height > request.height)
5893 if (id == TOOL_CTRL_ID_YES)
5896 y = request.height - 2 * request.border_size - gfx->height;
5898 else if (id == TOOL_CTRL_ID_NO)
5900 x = request.width - 2 * request.border_size - gfx->width;
5901 y = request.height - 2 * request.border_size - gfx->height;
5903 else if (id == TOOL_CTRL_ID_CONFIRM)
5905 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5906 y = request.height - 2 * request.border_size - gfx->height;
5908 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5910 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5912 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5913 y = request.height - 2 * request.border_size - gfx->height * 2;
5915 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5916 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5921 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5924 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5926 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5927 pos->size, &deco_bitmap, &deco_x, &deco_y);
5928 deco_xpos = (gfx->width - pos->size) / 2;
5929 deco_ypos = (gfx->height - pos->size) / 2;
5932 gi = CreateGadget(GDI_CUSTOM_ID, id,
5933 GDI_IMAGE_ID, graphic,
5934 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5937 GDI_WIDTH, gfx->width,
5938 GDI_HEIGHT, gfx->height,
5939 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5940 GDI_STATE, GD_BUTTON_UNPRESSED,
5941 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5942 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5943 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5944 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5945 GDI_DECORATION_SIZE, pos->size, pos->size,
5946 GDI_DECORATION_SHIFTING, 1, 1,
5947 GDI_DIRECT_DRAW, FALSE,
5948 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5949 GDI_EVENT_MASK, event_mask,
5950 GDI_CALLBACK_ACTION, HandleToolButtons,
5954 Fail("cannot create gadget");
5956 tool_gadget[id] = gi;
5960 void FreeToolButtons(void)
5964 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5965 FreeGadget(tool_gadget[i]);
5968 static void UnmapToolButtons(void)
5972 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5973 UnmapGadget(tool_gadget[i]);
5976 static void HandleToolButtons(struct GadgetInfo *gi)
5978 request_gadget_id = gi->custom_id;
5981 static struct Mapping_EM_to_RND_object
5984 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5985 boolean is_backside; // backside of moving element
5991 em_object_mapping_list[GAME_TILE_MAX + 1] =
5994 Zborder, FALSE, FALSE,
5998 Zplayer, FALSE, FALSE,
6007 Ztank, FALSE, FALSE,
6011 Zeater, FALSE, FALSE,
6015 Zdynamite, FALSE, FALSE,
6019 Zboom, FALSE, FALSE,
6024 Xchain, FALSE, FALSE,
6025 EL_DEFAULT, ACTION_EXPLODING, -1
6028 Xboom_bug, FALSE, FALSE,
6029 EL_BUG, ACTION_EXPLODING, -1
6032 Xboom_tank, FALSE, FALSE,
6033 EL_SPACESHIP, ACTION_EXPLODING, -1
6036 Xboom_android, FALSE, FALSE,
6037 EL_EMC_ANDROID, ACTION_OTHER, -1
6040 Xboom_1, FALSE, FALSE,
6041 EL_DEFAULT, ACTION_EXPLODING, -1
6044 Xboom_2, FALSE, FALSE,
6045 EL_DEFAULT, ACTION_EXPLODING, -1
6049 Xblank, TRUE, FALSE,
6054 Xsplash_e, FALSE, FALSE,
6055 EL_ACID_SPLASH_RIGHT, -1, -1
6058 Xsplash_w, FALSE, FALSE,
6059 EL_ACID_SPLASH_LEFT, -1, -1
6063 Xplant, TRUE, FALSE,
6064 EL_EMC_PLANT, -1, -1
6067 Yplant, FALSE, FALSE,
6068 EL_EMC_PLANT, -1, -1
6072 Xacid_1, TRUE, FALSE,
6076 Xacid_2, FALSE, FALSE,
6080 Xacid_3, FALSE, FALSE,
6084 Xacid_4, FALSE, FALSE,
6088 Xacid_5, FALSE, FALSE,
6092 Xacid_6, FALSE, FALSE,
6096 Xacid_7, FALSE, FALSE,
6100 Xacid_8, FALSE, FALSE,
6105 Xfake_acid_1, TRUE, FALSE,
6106 EL_EMC_FAKE_ACID, -1, -1
6109 Xfake_acid_2, FALSE, FALSE,
6110 EL_EMC_FAKE_ACID, -1, -1
6113 Xfake_acid_3, FALSE, FALSE,
6114 EL_EMC_FAKE_ACID, -1, -1
6117 Xfake_acid_4, FALSE, FALSE,
6118 EL_EMC_FAKE_ACID, -1, -1
6121 Xfake_acid_5, FALSE, FALSE,
6122 EL_EMC_FAKE_ACID, -1, -1
6125 Xfake_acid_6, FALSE, FALSE,
6126 EL_EMC_FAKE_ACID, -1, -1
6129 Xfake_acid_7, FALSE, FALSE,
6130 EL_EMC_FAKE_ACID, -1, -1
6133 Xfake_acid_8, FALSE, FALSE,
6134 EL_EMC_FAKE_ACID, -1, -1
6138 Xfake_acid_1_player, FALSE, FALSE,
6139 EL_EMC_FAKE_ACID, -1, -1
6142 Xfake_acid_2_player, FALSE, FALSE,
6143 EL_EMC_FAKE_ACID, -1, -1
6146 Xfake_acid_3_player, FALSE, FALSE,
6147 EL_EMC_FAKE_ACID, -1, -1
6150 Xfake_acid_4_player, FALSE, FALSE,
6151 EL_EMC_FAKE_ACID, -1, -1
6154 Xfake_acid_5_player, FALSE, FALSE,
6155 EL_EMC_FAKE_ACID, -1, -1
6158 Xfake_acid_6_player, FALSE, FALSE,
6159 EL_EMC_FAKE_ACID, -1, -1
6162 Xfake_acid_7_player, FALSE, FALSE,
6163 EL_EMC_FAKE_ACID, -1, -1
6166 Xfake_acid_8_player, FALSE, FALSE,
6167 EL_EMC_FAKE_ACID, -1, -1
6171 Xgrass, TRUE, FALSE,
6172 EL_EMC_GRASS, -1, -1
6175 Ygrass_nB, FALSE, FALSE,
6176 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6179 Ygrass_eB, FALSE, FALSE,
6180 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6183 Ygrass_sB, FALSE, FALSE,
6184 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6187 Ygrass_wB, FALSE, FALSE,
6188 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6196 Ydirt_nB, FALSE, FALSE,
6197 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6200 Ydirt_eB, FALSE, FALSE,
6201 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6204 Ydirt_sB, FALSE, FALSE,
6205 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6208 Ydirt_wB, FALSE, FALSE,
6209 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6213 Xandroid, TRUE, FALSE,
6214 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6217 Xandroid_1_n, FALSE, FALSE,
6218 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6221 Xandroid_2_n, FALSE, FALSE,
6222 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6225 Xandroid_1_e, FALSE, FALSE,
6226 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6229 Xandroid_2_e, FALSE, FALSE,
6230 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6233 Xandroid_1_w, FALSE, FALSE,
6234 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6237 Xandroid_2_w, FALSE, FALSE,
6238 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6241 Xandroid_1_s, FALSE, FALSE,
6242 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6245 Xandroid_2_s, FALSE, FALSE,
6246 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6249 Yandroid_n, FALSE, FALSE,
6250 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6253 Yandroid_nB, FALSE, TRUE,
6254 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6257 Yandroid_ne, FALSE, FALSE,
6258 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6261 Yandroid_neB, FALSE, TRUE,
6262 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6265 Yandroid_e, FALSE, FALSE,
6266 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6269 Yandroid_eB, FALSE, TRUE,
6270 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6273 Yandroid_se, FALSE, FALSE,
6274 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6277 Yandroid_seB, FALSE, TRUE,
6278 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6281 Yandroid_s, FALSE, FALSE,
6282 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6285 Yandroid_sB, FALSE, TRUE,
6286 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6289 Yandroid_sw, FALSE, FALSE,
6290 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6293 Yandroid_swB, FALSE, TRUE,
6294 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6297 Yandroid_w, FALSE, FALSE,
6298 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6301 Yandroid_wB, FALSE, TRUE,
6302 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6305 Yandroid_nw, FALSE, FALSE,
6306 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6309 Yandroid_nwB, FALSE, TRUE,
6310 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6314 Xeater_n, TRUE, FALSE,
6315 EL_YAMYAM_UP, -1, -1
6318 Xeater_e, TRUE, FALSE,
6319 EL_YAMYAM_RIGHT, -1, -1
6322 Xeater_w, TRUE, FALSE,
6323 EL_YAMYAM_LEFT, -1, -1
6326 Xeater_s, TRUE, FALSE,
6327 EL_YAMYAM_DOWN, -1, -1
6330 Yeater_n, FALSE, FALSE,
6331 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6334 Yeater_nB, FALSE, TRUE,
6335 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6338 Yeater_e, FALSE, FALSE,
6339 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6342 Yeater_eB, FALSE, TRUE,
6343 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6346 Yeater_s, FALSE, FALSE,
6347 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6350 Yeater_sB, FALSE, TRUE,
6351 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6354 Yeater_w, FALSE, FALSE,
6355 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6358 Yeater_wB, FALSE, TRUE,
6359 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6362 Yeater_stone, FALSE, FALSE,
6363 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6366 Yeater_spring, FALSE, FALSE,
6367 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6371 Xalien, TRUE, FALSE,
6375 Xalien_pause, FALSE, FALSE,
6379 Yalien_n, FALSE, FALSE,
6380 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6383 Yalien_nB, FALSE, TRUE,
6384 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6387 Yalien_e, FALSE, FALSE,
6388 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6391 Yalien_eB, FALSE, TRUE,
6392 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6395 Yalien_s, FALSE, FALSE,
6396 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6399 Yalien_sB, FALSE, TRUE,
6400 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6403 Yalien_w, FALSE, FALSE,
6404 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6407 Yalien_wB, FALSE, TRUE,
6408 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6411 Yalien_stone, FALSE, FALSE,
6412 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6415 Yalien_spring, FALSE, FALSE,
6416 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6420 Xbug_1_n, TRUE, FALSE,
6424 Xbug_1_e, TRUE, FALSE,
6425 EL_BUG_RIGHT, -1, -1
6428 Xbug_1_s, TRUE, FALSE,
6432 Xbug_1_w, TRUE, FALSE,
6436 Xbug_2_n, FALSE, FALSE,
6440 Xbug_2_e, FALSE, FALSE,
6441 EL_BUG_RIGHT, -1, -1
6444 Xbug_2_s, FALSE, FALSE,
6448 Xbug_2_w, FALSE, FALSE,
6452 Ybug_n, FALSE, FALSE,
6453 EL_BUG, ACTION_MOVING, MV_BIT_UP
6456 Ybug_nB, FALSE, TRUE,
6457 EL_BUG, ACTION_MOVING, MV_BIT_UP
6460 Ybug_e, FALSE, FALSE,
6461 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6464 Ybug_eB, FALSE, TRUE,
6465 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6468 Ybug_s, FALSE, FALSE,
6469 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6472 Ybug_sB, FALSE, TRUE,
6473 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6476 Ybug_w, FALSE, FALSE,
6477 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6480 Ybug_wB, FALSE, TRUE,
6481 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6484 Ybug_w_n, FALSE, FALSE,
6485 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6488 Ybug_n_e, FALSE, FALSE,
6489 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6492 Ybug_e_s, FALSE, FALSE,
6493 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6496 Ybug_s_w, FALSE, FALSE,
6497 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6500 Ybug_e_n, FALSE, FALSE,
6501 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6504 Ybug_s_e, FALSE, FALSE,
6505 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6508 Ybug_w_s, FALSE, FALSE,
6509 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6512 Ybug_n_w, FALSE, FALSE,
6513 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6516 Ybug_stone, FALSE, FALSE,
6517 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6520 Ybug_spring, FALSE, FALSE,
6521 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6525 Xtank_1_n, TRUE, FALSE,
6526 EL_SPACESHIP_UP, -1, -1
6529 Xtank_1_e, TRUE, FALSE,
6530 EL_SPACESHIP_RIGHT, -1, -1
6533 Xtank_1_s, TRUE, FALSE,
6534 EL_SPACESHIP_DOWN, -1, -1
6537 Xtank_1_w, TRUE, FALSE,
6538 EL_SPACESHIP_LEFT, -1, -1
6541 Xtank_2_n, FALSE, FALSE,
6542 EL_SPACESHIP_UP, -1, -1
6545 Xtank_2_e, FALSE, FALSE,
6546 EL_SPACESHIP_RIGHT, -1, -1
6549 Xtank_2_s, FALSE, FALSE,
6550 EL_SPACESHIP_DOWN, -1, -1
6553 Xtank_2_w, FALSE, FALSE,
6554 EL_SPACESHIP_LEFT, -1, -1
6557 Ytank_n, FALSE, FALSE,
6558 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6561 Ytank_nB, FALSE, TRUE,
6562 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6565 Ytank_e, FALSE, FALSE,
6566 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6569 Ytank_eB, FALSE, TRUE,
6570 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6573 Ytank_s, FALSE, FALSE,
6574 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6577 Ytank_sB, FALSE, TRUE,
6578 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6581 Ytank_w, FALSE, FALSE,
6582 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6585 Ytank_wB, FALSE, TRUE,
6586 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6589 Ytank_w_n, FALSE, FALSE,
6590 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6593 Ytank_n_e, FALSE, FALSE,
6594 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6597 Ytank_e_s, FALSE, FALSE,
6598 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6601 Ytank_s_w, FALSE, FALSE,
6602 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6605 Ytank_e_n, FALSE, FALSE,
6606 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6609 Ytank_s_e, FALSE, FALSE,
6610 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6613 Ytank_w_s, FALSE, FALSE,
6614 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6617 Ytank_n_w, FALSE, FALSE,
6618 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6621 Ytank_stone, FALSE, FALSE,
6622 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6625 Ytank_spring, FALSE, FALSE,
6626 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6630 Xemerald, TRUE, FALSE,
6634 Xemerald_pause, FALSE, FALSE,
6638 Xemerald_fall, FALSE, FALSE,
6642 Xemerald_shine, FALSE, FALSE,
6643 EL_EMERALD, ACTION_TWINKLING, -1
6646 Yemerald_s, FALSE, FALSE,
6647 EL_EMERALD, ACTION_FALLING, -1
6650 Yemerald_sB, FALSE, TRUE,
6651 EL_EMERALD, ACTION_FALLING, -1
6654 Yemerald_e, FALSE, FALSE,
6655 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6658 Yemerald_eB, FALSE, TRUE,
6659 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6662 Yemerald_w, FALSE, FALSE,
6663 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6666 Yemerald_wB, FALSE, TRUE,
6667 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6670 Yemerald_blank, FALSE, FALSE,
6671 EL_EMERALD, ACTION_COLLECTING, -1
6675 Xdiamond, TRUE, FALSE,
6679 Xdiamond_pause, FALSE, FALSE,
6683 Xdiamond_fall, FALSE, FALSE,
6687 Xdiamond_shine, FALSE, FALSE,
6688 EL_DIAMOND, ACTION_TWINKLING, -1
6691 Ydiamond_s, FALSE, FALSE,
6692 EL_DIAMOND, ACTION_FALLING, -1
6695 Ydiamond_sB, FALSE, TRUE,
6696 EL_DIAMOND, ACTION_FALLING, -1
6699 Ydiamond_e, FALSE, FALSE,
6700 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6703 Ydiamond_eB, FALSE, TRUE,
6704 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6707 Ydiamond_w, FALSE, FALSE,
6708 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6711 Ydiamond_wB, FALSE, TRUE,
6712 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6715 Ydiamond_blank, FALSE, FALSE,
6716 EL_DIAMOND, ACTION_COLLECTING, -1
6719 Ydiamond_stone, FALSE, FALSE,
6720 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6724 Xstone, TRUE, FALSE,
6728 Xstone_pause, FALSE, FALSE,
6732 Xstone_fall, FALSE, FALSE,
6736 Ystone_s, FALSE, FALSE,
6737 EL_ROCK, ACTION_FALLING, -1
6740 Ystone_sB, FALSE, TRUE,
6741 EL_ROCK, ACTION_FALLING, -1
6744 Ystone_e, FALSE, FALSE,
6745 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6748 Ystone_eB, FALSE, TRUE,
6749 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6752 Ystone_w, FALSE, FALSE,
6753 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6756 Ystone_wB, FALSE, TRUE,
6757 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6765 Xbomb_pause, FALSE, FALSE,
6769 Xbomb_fall, FALSE, FALSE,
6773 Ybomb_s, FALSE, FALSE,
6774 EL_BOMB, ACTION_FALLING, -1
6777 Ybomb_sB, FALSE, TRUE,
6778 EL_BOMB, ACTION_FALLING, -1
6781 Ybomb_e, FALSE, FALSE,
6782 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6785 Ybomb_eB, FALSE, TRUE,
6786 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6789 Ybomb_w, FALSE, FALSE,
6790 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6793 Ybomb_wB, FALSE, TRUE,
6794 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6797 Ybomb_blank, FALSE, FALSE,
6798 EL_BOMB, ACTION_ACTIVATING, -1
6806 Xnut_pause, FALSE, FALSE,
6810 Xnut_fall, FALSE, FALSE,
6814 Ynut_s, FALSE, FALSE,
6815 EL_NUT, ACTION_FALLING, -1
6818 Ynut_sB, FALSE, TRUE,
6819 EL_NUT, ACTION_FALLING, -1
6822 Ynut_e, FALSE, FALSE,
6823 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6826 Ynut_eB, FALSE, TRUE,
6827 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6830 Ynut_w, FALSE, FALSE,
6831 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6834 Ynut_wB, FALSE, TRUE,
6835 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6838 Ynut_stone, FALSE, FALSE,
6839 EL_NUT, ACTION_BREAKING, -1
6843 Xspring, TRUE, FALSE,
6847 Xspring_pause, FALSE, FALSE,
6851 Xspring_e, TRUE, FALSE,
6852 EL_SPRING_RIGHT, -1, -1
6855 Xspring_w, TRUE, FALSE,
6856 EL_SPRING_LEFT, -1, -1
6859 Xspring_fall, FALSE, FALSE,
6863 Yspring_s, FALSE, FALSE,
6864 EL_SPRING, ACTION_FALLING, -1
6867 Yspring_sB, FALSE, TRUE,
6868 EL_SPRING, ACTION_FALLING, -1
6871 Yspring_e, FALSE, FALSE,
6872 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6875 Yspring_eB, FALSE, TRUE,
6876 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6879 Yspring_w, FALSE, FALSE,
6880 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6883 Yspring_wB, FALSE, TRUE,
6884 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6887 Yspring_alien_e, FALSE, FALSE,
6888 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6891 Yspring_alien_eB, FALSE, TRUE,
6892 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6895 Yspring_alien_w, FALSE, FALSE,
6896 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6899 Yspring_alien_wB, FALSE, TRUE,
6900 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6904 Xpush_emerald_e, FALSE, FALSE,
6905 EL_EMERALD, -1, MV_BIT_RIGHT
6908 Xpush_emerald_w, FALSE, FALSE,
6909 EL_EMERALD, -1, MV_BIT_LEFT
6912 Xpush_diamond_e, FALSE, FALSE,
6913 EL_DIAMOND, -1, MV_BIT_RIGHT
6916 Xpush_diamond_w, FALSE, FALSE,
6917 EL_DIAMOND, -1, MV_BIT_LEFT
6920 Xpush_stone_e, FALSE, FALSE,
6921 EL_ROCK, -1, MV_BIT_RIGHT
6924 Xpush_stone_w, FALSE, FALSE,
6925 EL_ROCK, -1, MV_BIT_LEFT
6928 Xpush_bomb_e, FALSE, FALSE,
6929 EL_BOMB, -1, MV_BIT_RIGHT
6932 Xpush_bomb_w, FALSE, FALSE,
6933 EL_BOMB, -1, MV_BIT_LEFT
6936 Xpush_nut_e, FALSE, FALSE,
6937 EL_NUT, -1, MV_BIT_RIGHT
6940 Xpush_nut_w, FALSE, FALSE,
6941 EL_NUT, -1, MV_BIT_LEFT
6944 Xpush_spring_e, FALSE, FALSE,
6945 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6948 Xpush_spring_w, FALSE, FALSE,
6949 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6953 Xdynamite, TRUE, FALSE,
6954 EL_EM_DYNAMITE, -1, -1
6957 Ydynamite_blank, FALSE, FALSE,
6958 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6961 Xdynamite_1, TRUE, FALSE,
6962 EL_EM_DYNAMITE_ACTIVE, -1, -1
6965 Xdynamite_2, FALSE, FALSE,
6966 EL_EM_DYNAMITE_ACTIVE, -1, -1
6969 Xdynamite_3, FALSE, FALSE,
6970 EL_EM_DYNAMITE_ACTIVE, -1, -1
6973 Xdynamite_4, FALSE, FALSE,
6974 EL_EM_DYNAMITE_ACTIVE, -1, -1
6978 Xkey_1, TRUE, FALSE,
6982 Xkey_2, TRUE, FALSE,
6986 Xkey_3, TRUE, FALSE,
6990 Xkey_4, TRUE, FALSE,
6994 Xkey_5, TRUE, FALSE,
6995 EL_EMC_KEY_5, -1, -1
6998 Xkey_6, TRUE, FALSE,
6999 EL_EMC_KEY_6, -1, -1
7002 Xkey_7, TRUE, FALSE,
7003 EL_EMC_KEY_7, -1, -1
7006 Xkey_8, TRUE, FALSE,
7007 EL_EMC_KEY_8, -1, -1
7011 Xdoor_1, TRUE, FALSE,
7012 EL_EM_GATE_1, -1, -1
7015 Xdoor_2, TRUE, FALSE,
7016 EL_EM_GATE_2, -1, -1
7019 Xdoor_3, TRUE, FALSE,
7020 EL_EM_GATE_3, -1, -1
7023 Xdoor_4, TRUE, FALSE,
7024 EL_EM_GATE_4, -1, -1
7027 Xdoor_5, TRUE, FALSE,
7028 EL_EMC_GATE_5, -1, -1
7031 Xdoor_6, TRUE, FALSE,
7032 EL_EMC_GATE_6, -1, -1
7035 Xdoor_7, TRUE, FALSE,
7036 EL_EMC_GATE_7, -1, -1
7039 Xdoor_8, TRUE, FALSE,
7040 EL_EMC_GATE_8, -1, -1
7044 Xfake_door_1, TRUE, FALSE,
7045 EL_EM_GATE_1_GRAY, -1, -1
7048 Xfake_door_2, TRUE, FALSE,
7049 EL_EM_GATE_2_GRAY, -1, -1
7052 Xfake_door_3, TRUE, FALSE,
7053 EL_EM_GATE_3_GRAY, -1, -1
7056 Xfake_door_4, TRUE, FALSE,
7057 EL_EM_GATE_4_GRAY, -1, -1
7060 Xfake_door_5, TRUE, FALSE,
7061 EL_EMC_GATE_5_GRAY, -1, -1
7064 Xfake_door_6, TRUE, FALSE,
7065 EL_EMC_GATE_6_GRAY, -1, -1
7068 Xfake_door_7, TRUE, FALSE,
7069 EL_EMC_GATE_7_GRAY, -1, -1
7072 Xfake_door_8, TRUE, FALSE,
7073 EL_EMC_GATE_8_GRAY, -1, -1
7077 Xballoon, TRUE, FALSE,
7081 Yballoon_n, FALSE, FALSE,
7082 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7085 Yballoon_nB, FALSE, TRUE,
7086 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7089 Yballoon_e, FALSE, FALSE,
7090 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7093 Yballoon_eB, FALSE, TRUE,
7094 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7097 Yballoon_s, FALSE, FALSE,
7098 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7101 Yballoon_sB, FALSE, TRUE,
7102 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7105 Yballoon_w, FALSE, FALSE,
7106 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7109 Yballoon_wB, FALSE, TRUE,
7110 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7114 Xball_1, TRUE, FALSE,
7115 EL_EMC_MAGIC_BALL, -1, -1
7118 Yball_1, FALSE, FALSE,
7119 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7122 Xball_2, FALSE, FALSE,
7123 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7126 Yball_2, FALSE, FALSE,
7127 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7130 Yball_blank, FALSE, FALSE,
7131 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7135 Xamoeba_1, TRUE, FALSE,
7136 EL_AMOEBA_DRY, ACTION_OTHER, -1
7139 Xamoeba_2, FALSE, FALSE,
7140 EL_AMOEBA_DRY, ACTION_OTHER, -1
7143 Xamoeba_3, FALSE, FALSE,
7144 EL_AMOEBA_DRY, ACTION_OTHER, -1
7147 Xamoeba_4, FALSE, FALSE,
7148 EL_AMOEBA_DRY, ACTION_OTHER, -1
7151 Xamoeba_5, TRUE, FALSE,
7152 EL_AMOEBA_WET, ACTION_OTHER, -1
7155 Xamoeba_6, FALSE, FALSE,
7156 EL_AMOEBA_WET, ACTION_OTHER, -1
7159 Xamoeba_7, FALSE, FALSE,
7160 EL_AMOEBA_WET, ACTION_OTHER, -1
7163 Xamoeba_8, FALSE, FALSE,
7164 EL_AMOEBA_WET, ACTION_OTHER, -1
7169 EL_AMOEBA_DROP, ACTION_GROWING, -1
7172 Xdrip_fall, FALSE, FALSE,
7173 EL_AMOEBA_DROP, -1, -1
7176 Xdrip_stretch, FALSE, FALSE,
7177 EL_AMOEBA_DROP, ACTION_FALLING, -1
7180 Xdrip_stretchB, FALSE, TRUE,
7181 EL_AMOEBA_DROP, ACTION_FALLING, -1
7184 Ydrip_1_s, FALSE, FALSE,
7185 EL_AMOEBA_DROP, ACTION_FALLING, -1
7188 Ydrip_1_sB, FALSE, TRUE,
7189 EL_AMOEBA_DROP, ACTION_FALLING, -1
7192 Ydrip_2_s, FALSE, FALSE,
7193 EL_AMOEBA_DROP, ACTION_FALLING, -1
7196 Ydrip_2_sB, FALSE, TRUE,
7197 EL_AMOEBA_DROP, ACTION_FALLING, -1
7201 Xwonderwall, TRUE, FALSE,
7202 EL_MAGIC_WALL, -1, -1
7205 Ywonderwall, FALSE, FALSE,
7206 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7210 Xwheel, TRUE, FALSE,
7211 EL_ROBOT_WHEEL, -1, -1
7214 Ywheel, FALSE, FALSE,
7215 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7219 Xswitch, TRUE, FALSE,
7220 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7223 Yswitch, FALSE, FALSE,
7224 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7228 Xbumper, TRUE, FALSE,
7229 EL_EMC_SPRING_BUMPER, -1, -1
7232 Ybumper, FALSE, FALSE,
7233 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7237 Xacid_nw, TRUE, FALSE,
7238 EL_ACID_POOL_TOPLEFT, -1, -1
7241 Xacid_ne, TRUE, FALSE,
7242 EL_ACID_POOL_TOPRIGHT, -1, -1
7245 Xacid_sw, TRUE, FALSE,
7246 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7249 Xacid_s, TRUE, FALSE,
7250 EL_ACID_POOL_BOTTOM, -1, -1
7253 Xacid_se, TRUE, FALSE,
7254 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7258 Xfake_blank, TRUE, FALSE,
7259 EL_INVISIBLE_WALL, -1, -1
7262 Yfake_blank, FALSE, FALSE,
7263 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7267 Xfake_grass, TRUE, FALSE,
7268 EL_EMC_FAKE_GRASS, -1, -1
7271 Yfake_grass, FALSE, FALSE,
7272 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7276 Xfake_amoeba, TRUE, FALSE,
7277 EL_EMC_DRIPPER, -1, -1
7280 Yfake_amoeba, FALSE, FALSE,
7281 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7285 Xlenses, TRUE, FALSE,
7286 EL_EMC_LENSES, -1, -1
7290 Xmagnify, TRUE, FALSE,
7291 EL_EMC_MAGNIFIER, -1, -1
7296 EL_QUICKSAND_EMPTY, -1, -1
7299 Xsand_stone, TRUE, FALSE,
7300 EL_QUICKSAND_FULL, -1, -1
7303 Xsand_stonein_1, FALSE, TRUE,
7304 EL_ROCK, ACTION_FILLING, -1
7307 Xsand_stonein_2, FALSE, TRUE,
7308 EL_ROCK, ACTION_FILLING, -1
7311 Xsand_stonein_3, FALSE, TRUE,
7312 EL_ROCK, ACTION_FILLING, -1
7315 Xsand_stonein_4, FALSE, TRUE,
7316 EL_ROCK, ACTION_FILLING, -1
7319 Xsand_sandstone_1, FALSE, FALSE,
7320 EL_QUICKSAND_FILLING, -1, -1
7323 Xsand_sandstone_2, FALSE, FALSE,
7324 EL_QUICKSAND_FILLING, -1, -1
7327 Xsand_sandstone_3, FALSE, FALSE,
7328 EL_QUICKSAND_FILLING, -1, -1
7331 Xsand_sandstone_4, FALSE, FALSE,
7332 EL_QUICKSAND_FILLING, -1, -1
7335 Xsand_stonesand_1, FALSE, FALSE,
7336 EL_QUICKSAND_EMPTYING, -1, -1
7339 Xsand_stonesand_2, FALSE, FALSE,
7340 EL_QUICKSAND_EMPTYING, -1, -1
7343 Xsand_stonesand_3, FALSE, FALSE,
7344 EL_QUICKSAND_EMPTYING, -1, -1
7347 Xsand_stonesand_4, FALSE, FALSE,
7348 EL_QUICKSAND_EMPTYING, -1, -1
7351 Xsand_stoneout_1, FALSE, FALSE,
7352 EL_ROCK, ACTION_EMPTYING, -1
7355 Xsand_stoneout_2, FALSE, FALSE,
7356 EL_ROCK, ACTION_EMPTYING, -1
7359 Xsand_stonesand_quickout_1, FALSE, FALSE,
7360 EL_QUICKSAND_EMPTYING, -1, -1
7363 Xsand_stonesand_quickout_2, FALSE, FALSE,
7364 EL_QUICKSAND_EMPTYING, -1, -1
7368 Xslide_ns, TRUE, FALSE,
7369 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7372 Yslide_ns_blank, FALSE, FALSE,
7373 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7376 Xslide_ew, TRUE, FALSE,
7377 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7380 Yslide_ew_blank, FALSE, FALSE,
7381 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7385 Xwind_n, TRUE, FALSE,
7386 EL_BALLOON_SWITCH_UP, -1, -1
7389 Xwind_e, TRUE, FALSE,
7390 EL_BALLOON_SWITCH_RIGHT, -1, -1
7393 Xwind_s, TRUE, FALSE,
7394 EL_BALLOON_SWITCH_DOWN, -1, -1
7397 Xwind_w, TRUE, FALSE,
7398 EL_BALLOON_SWITCH_LEFT, -1, -1
7401 Xwind_any, TRUE, FALSE,
7402 EL_BALLOON_SWITCH_ANY, -1, -1
7405 Xwind_stop, TRUE, FALSE,
7406 EL_BALLOON_SWITCH_NONE, -1, -1
7411 EL_EM_EXIT_CLOSED, -1, -1
7414 Xexit_1, TRUE, FALSE,
7415 EL_EM_EXIT_OPEN, -1, -1
7418 Xexit_2, FALSE, FALSE,
7419 EL_EM_EXIT_OPEN, -1, -1
7422 Xexit_3, FALSE, FALSE,
7423 EL_EM_EXIT_OPEN, -1, -1
7427 Xpause, FALSE, FALSE,
7432 Xwall_1, TRUE, FALSE,
7436 Xwall_2, TRUE, FALSE,
7437 EL_EMC_WALL_14, -1, -1
7440 Xwall_3, TRUE, FALSE,
7441 EL_EMC_WALL_15, -1, -1
7444 Xwall_4, TRUE, FALSE,
7445 EL_EMC_WALL_16, -1, -1
7449 Xroundwall_1, TRUE, FALSE,
7450 EL_WALL_SLIPPERY, -1, -1
7453 Xroundwall_2, TRUE, FALSE,
7454 EL_EMC_WALL_SLIPPERY_2, -1, -1
7457 Xroundwall_3, TRUE, FALSE,
7458 EL_EMC_WALL_SLIPPERY_3, -1, -1
7461 Xroundwall_4, TRUE, FALSE,
7462 EL_EMC_WALL_SLIPPERY_4, -1, -1
7466 Xsteel_1, TRUE, FALSE,
7467 EL_STEELWALL, -1, -1
7470 Xsteel_2, TRUE, FALSE,
7471 EL_EMC_STEELWALL_2, -1, -1
7474 Xsteel_3, TRUE, FALSE,
7475 EL_EMC_STEELWALL_3, -1, -1
7478 Xsteel_4, TRUE, FALSE,
7479 EL_EMC_STEELWALL_4, -1, -1
7483 Xdecor_1, TRUE, FALSE,
7484 EL_EMC_WALL_8, -1, -1
7487 Xdecor_2, TRUE, FALSE,
7488 EL_EMC_WALL_6, -1, -1
7491 Xdecor_3, TRUE, FALSE,
7492 EL_EMC_WALL_4, -1, -1
7495 Xdecor_4, TRUE, FALSE,
7496 EL_EMC_WALL_7, -1, -1
7499 Xdecor_5, TRUE, FALSE,
7500 EL_EMC_WALL_5, -1, -1
7503 Xdecor_6, TRUE, FALSE,
7504 EL_EMC_WALL_9, -1, -1
7507 Xdecor_7, TRUE, FALSE,
7508 EL_EMC_WALL_10, -1, -1
7511 Xdecor_8, TRUE, FALSE,
7512 EL_EMC_WALL_1, -1, -1
7515 Xdecor_9, TRUE, FALSE,
7516 EL_EMC_WALL_2, -1, -1
7519 Xdecor_10, TRUE, FALSE,
7520 EL_EMC_WALL_3, -1, -1
7523 Xdecor_11, TRUE, FALSE,
7524 EL_EMC_WALL_11, -1, -1
7527 Xdecor_12, TRUE, FALSE,
7528 EL_EMC_WALL_12, -1, -1
7532 Xalpha_0, TRUE, FALSE,
7533 EL_CHAR('0'), -1, -1
7536 Xalpha_1, TRUE, FALSE,
7537 EL_CHAR('1'), -1, -1
7540 Xalpha_2, TRUE, FALSE,
7541 EL_CHAR('2'), -1, -1
7544 Xalpha_3, TRUE, FALSE,
7545 EL_CHAR('3'), -1, -1
7548 Xalpha_4, TRUE, FALSE,
7549 EL_CHAR('4'), -1, -1
7552 Xalpha_5, TRUE, FALSE,
7553 EL_CHAR('5'), -1, -1
7556 Xalpha_6, TRUE, FALSE,
7557 EL_CHAR('6'), -1, -1
7560 Xalpha_7, TRUE, FALSE,
7561 EL_CHAR('7'), -1, -1
7564 Xalpha_8, TRUE, FALSE,
7565 EL_CHAR('8'), -1, -1
7568 Xalpha_9, TRUE, FALSE,
7569 EL_CHAR('9'), -1, -1
7572 Xalpha_excla, TRUE, FALSE,
7573 EL_CHAR('!'), -1, -1
7576 Xalpha_apost, TRUE, FALSE,
7577 EL_CHAR('\''), -1, -1
7580 Xalpha_comma, TRUE, FALSE,
7581 EL_CHAR(','), -1, -1
7584 Xalpha_minus, TRUE, FALSE,
7585 EL_CHAR('-'), -1, -1
7588 Xalpha_perio, TRUE, FALSE,
7589 EL_CHAR('.'), -1, -1
7592 Xalpha_colon, TRUE, FALSE,
7593 EL_CHAR(':'), -1, -1
7596 Xalpha_quest, TRUE, FALSE,
7597 EL_CHAR('?'), -1, -1
7600 Xalpha_a, TRUE, FALSE,
7601 EL_CHAR('A'), -1, -1
7604 Xalpha_b, TRUE, FALSE,
7605 EL_CHAR('B'), -1, -1
7608 Xalpha_c, TRUE, FALSE,
7609 EL_CHAR('C'), -1, -1
7612 Xalpha_d, TRUE, FALSE,
7613 EL_CHAR('D'), -1, -1
7616 Xalpha_e, TRUE, FALSE,
7617 EL_CHAR('E'), -1, -1
7620 Xalpha_f, TRUE, FALSE,
7621 EL_CHAR('F'), -1, -1
7624 Xalpha_g, TRUE, FALSE,
7625 EL_CHAR('G'), -1, -1
7628 Xalpha_h, TRUE, FALSE,
7629 EL_CHAR('H'), -1, -1
7632 Xalpha_i, TRUE, FALSE,
7633 EL_CHAR('I'), -1, -1
7636 Xalpha_j, TRUE, FALSE,
7637 EL_CHAR('J'), -1, -1
7640 Xalpha_k, TRUE, FALSE,
7641 EL_CHAR('K'), -1, -1
7644 Xalpha_l, TRUE, FALSE,
7645 EL_CHAR('L'), -1, -1
7648 Xalpha_m, TRUE, FALSE,
7649 EL_CHAR('M'), -1, -1
7652 Xalpha_n, TRUE, FALSE,
7653 EL_CHAR('N'), -1, -1
7656 Xalpha_o, TRUE, FALSE,
7657 EL_CHAR('O'), -1, -1
7660 Xalpha_p, TRUE, FALSE,
7661 EL_CHAR('P'), -1, -1
7664 Xalpha_q, TRUE, FALSE,
7665 EL_CHAR('Q'), -1, -1
7668 Xalpha_r, TRUE, FALSE,
7669 EL_CHAR('R'), -1, -1
7672 Xalpha_s, TRUE, FALSE,
7673 EL_CHAR('S'), -1, -1
7676 Xalpha_t, TRUE, FALSE,
7677 EL_CHAR('T'), -1, -1
7680 Xalpha_u, TRUE, FALSE,
7681 EL_CHAR('U'), -1, -1
7684 Xalpha_v, TRUE, FALSE,
7685 EL_CHAR('V'), -1, -1
7688 Xalpha_w, TRUE, FALSE,
7689 EL_CHAR('W'), -1, -1
7692 Xalpha_x, TRUE, FALSE,
7693 EL_CHAR('X'), -1, -1
7696 Xalpha_y, TRUE, FALSE,
7697 EL_CHAR('Y'), -1, -1
7700 Xalpha_z, TRUE, FALSE,
7701 EL_CHAR('Z'), -1, -1
7704 Xalpha_arrow_e, TRUE, FALSE,
7705 EL_CHAR('>'), -1, -1
7708 Xalpha_arrow_w, TRUE, FALSE,
7709 EL_CHAR('<'), -1, -1
7712 Xalpha_copyr, TRUE, FALSE,
7713 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7717 Ykey_1_blank, FALSE, FALSE,
7718 EL_EM_KEY_1, ACTION_COLLECTING, -1
7721 Ykey_2_blank, FALSE, FALSE,
7722 EL_EM_KEY_2, ACTION_COLLECTING, -1
7725 Ykey_3_blank, FALSE, FALSE,
7726 EL_EM_KEY_3, ACTION_COLLECTING, -1
7729 Ykey_4_blank, FALSE, FALSE,
7730 EL_EM_KEY_4, ACTION_COLLECTING, -1
7733 Ykey_5_blank, FALSE, FALSE,
7734 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7737 Ykey_6_blank, FALSE, FALSE,
7738 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7741 Ykey_7_blank, FALSE, FALSE,
7742 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7745 Ykey_8_blank, FALSE, FALSE,
7746 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7749 Ylenses_blank, FALSE, FALSE,
7750 EL_EMC_LENSES, ACTION_COLLECTING, -1
7753 Ymagnify_blank, FALSE, FALSE,
7754 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7757 Ygrass_blank, FALSE, FALSE,
7758 EL_EMC_GRASS, ACTION_SNAPPING, -1
7761 Ydirt_blank, FALSE, FALSE,
7762 EL_SAND, ACTION_SNAPPING, -1
7771 static struct Mapping_EM_to_RND_player
7780 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7784 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7788 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7792 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7796 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7800 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7804 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7808 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7812 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7816 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7820 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7824 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7828 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7832 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7836 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7840 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7844 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7848 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7852 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7856 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7860 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7864 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7868 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7872 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7876 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7880 EL_PLAYER_1, ACTION_DEFAULT, -1,
7884 EL_PLAYER_2, ACTION_DEFAULT, -1,
7888 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7892 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7896 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7900 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7904 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7908 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7912 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7916 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7920 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7924 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7928 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7932 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7936 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7940 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7944 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7948 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7952 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7956 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7960 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7964 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7968 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7972 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7976 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7980 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7984 EL_PLAYER_3, ACTION_DEFAULT, -1,
7988 EL_PLAYER_4, ACTION_DEFAULT, -1,
7997 int map_element_RND_to_EM_cave(int element_rnd)
7999 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
8000 static boolean mapping_initialized = FALSE;
8002 if (!mapping_initialized)
8006 // return "Xalpha_quest" for all undefined elements in mapping array
8007 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8008 mapping_RND_to_EM[i] = Xalpha_quest;
8010 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8011 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
8012 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
8013 em_object_mapping_list[i].element_em;
8015 mapping_initialized = TRUE;
8018 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
8020 Warn("invalid RND level element %d", element_rnd);
8025 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
8028 int map_element_EM_to_RND_cave(int element_em_cave)
8030 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8031 static boolean mapping_initialized = FALSE;
8033 if (!mapping_initialized)
8037 // return "EL_UNKNOWN" for all undefined elements in mapping array
8038 for (i = 0; i < GAME_TILE_MAX; i++)
8039 mapping_EM_to_RND[i] = EL_UNKNOWN;
8041 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8042 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8043 em_object_mapping_list[i].element_rnd;
8045 mapping_initialized = TRUE;
8048 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8050 Warn("invalid EM cave element %d", element_em_cave);
8055 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8058 int map_element_EM_to_RND_game(int element_em_game)
8060 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8061 static boolean mapping_initialized = FALSE;
8063 if (!mapping_initialized)
8067 // return "EL_UNKNOWN" for all undefined elements in mapping array
8068 for (i = 0; i < GAME_TILE_MAX; i++)
8069 mapping_EM_to_RND[i] = EL_UNKNOWN;
8071 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8072 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8073 em_object_mapping_list[i].element_rnd;
8075 mapping_initialized = TRUE;
8078 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8080 Warn("invalid EM game element %d", element_em_game);
8085 return mapping_EM_to_RND[element_em_game];
8088 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8090 struct LevelInfo_EM *level_em = level->native_em_level;
8091 struct CAVE *cav = level_em->cav;
8094 for (i = 0; i < GAME_TILE_MAX; i++)
8095 cav->android_array[i] = Cblank;
8097 for (i = 0; i < level->num_android_clone_elements; i++)
8099 int element_rnd = level->android_clone_element[i];
8100 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8102 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8103 if (em_object_mapping_list[j].element_rnd == element_rnd)
8104 cav->android_array[em_object_mapping_list[j].element_em] =
8109 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8111 struct LevelInfo_EM *level_em = level->native_em_level;
8112 struct CAVE *cav = level_em->cav;
8115 level->num_android_clone_elements = 0;
8117 for (i = 0; i < GAME_TILE_MAX; i++)
8119 int element_em_cave = cav->android_array[i];
8121 boolean element_found = FALSE;
8123 if (element_em_cave == Cblank)
8126 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8128 for (j = 0; j < level->num_android_clone_elements; j++)
8129 if (level->android_clone_element[j] == element_rnd)
8130 element_found = TRUE;
8134 level->android_clone_element[level->num_android_clone_elements++] =
8137 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8142 if (level->num_android_clone_elements == 0)
8144 level->num_android_clone_elements = 1;
8145 level->android_clone_element[0] = EL_EMPTY;
8149 int map_direction_RND_to_EM(int direction)
8151 return (direction == MV_UP ? 0 :
8152 direction == MV_RIGHT ? 1 :
8153 direction == MV_DOWN ? 2 :
8154 direction == MV_LEFT ? 3 :
8158 int map_direction_EM_to_RND(int direction)
8160 return (direction == 0 ? MV_UP :
8161 direction == 1 ? MV_RIGHT :
8162 direction == 2 ? MV_DOWN :
8163 direction == 3 ? MV_LEFT :
8167 int map_element_RND_to_SP(int element_rnd)
8169 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8171 if (element_rnd >= EL_SP_START &&
8172 element_rnd <= EL_SP_END)
8173 element_sp = element_rnd - EL_SP_START;
8174 else if (element_rnd == EL_EMPTY_SPACE)
8176 else if (element_rnd == EL_INVISIBLE_WALL)
8182 int map_element_SP_to_RND(int element_sp)
8184 int element_rnd = EL_UNKNOWN;
8186 if (element_sp >= 0x00 &&
8188 element_rnd = EL_SP_START + element_sp;
8189 else if (element_sp == 0x28)
8190 element_rnd = EL_INVISIBLE_WALL;
8195 int map_action_SP_to_RND(int action_sp)
8199 case actActive: return ACTION_ACTIVE;
8200 case actImpact: return ACTION_IMPACT;
8201 case actExploding: return ACTION_EXPLODING;
8202 case actDigging: return ACTION_DIGGING;
8203 case actSnapping: return ACTION_SNAPPING;
8204 case actCollecting: return ACTION_COLLECTING;
8205 case actPassing: return ACTION_PASSING;
8206 case actPushing: return ACTION_PUSHING;
8207 case actDropping: return ACTION_DROPPING;
8209 default: return ACTION_DEFAULT;
8213 int map_element_RND_to_MM(int element_rnd)
8215 return (element_rnd >= EL_MM_START_1 &&
8216 element_rnd <= EL_MM_END_1 ?
8217 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8219 element_rnd >= EL_MM_START_2 &&
8220 element_rnd <= EL_MM_END_2 ?
8221 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8223 element_rnd >= EL_MM_START_3 &&
8224 element_rnd <= EL_MM_END_3 ?
8225 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
8227 element_rnd >= EL_CHAR_START &&
8228 element_rnd <= EL_CHAR_END ?
8229 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8231 element_rnd >= EL_MM_RUNTIME_START &&
8232 element_rnd <= EL_MM_RUNTIME_END ?
8233 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8235 EL_MM_EMPTY_NATIVE);
8238 int map_element_MM_to_RND(int element_mm)
8240 return (element_mm == EL_MM_EMPTY_NATIVE ||
8241 element_mm == EL_DF_EMPTY_NATIVE ?
8244 element_mm >= EL_MM_START_1_NATIVE &&
8245 element_mm <= EL_MM_END_1_NATIVE ?
8246 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8248 element_mm >= EL_MM_START_2_NATIVE &&
8249 element_mm <= EL_MM_END_2_NATIVE ?
8250 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8252 element_mm >= EL_MM_START_3_NATIVE &&
8253 element_mm <= EL_MM_END_3_NATIVE ?
8254 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
8256 element_mm >= EL_MM_CHAR_START_NATIVE &&
8257 element_mm <= EL_MM_CHAR_END_NATIVE ?
8258 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8260 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8261 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8262 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8267 int map_action_MM_to_RND(int action_mm)
8269 // all MM actions are defined to exactly match their RND counterparts
8273 int map_sound_MM_to_RND(int sound_mm)
8277 case SND_MM_GAME_LEVELTIME_CHARGING:
8278 return SND_GAME_LEVELTIME_CHARGING;
8280 case SND_MM_GAME_HEALTH_CHARGING:
8281 return SND_GAME_HEALTH_CHARGING;
8284 return SND_UNDEFINED;
8288 int map_mm_wall_element(int element)
8290 return (element >= EL_MM_STEEL_WALL_START &&
8291 element <= EL_MM_STEEL_WALL_END ?
8294 element >= EL_MM_WOODEN_WALL_START &&
8295 element <= EL_MM_WOODEN_WALL_END ?
8298 element >= EL_MM_ICE_WALL_START &&
8299 element <= EL_MM_ICE_WALL_END ?
8302 element >= EL_MM_AMOEBA_WALL_START &&
8303 element <= EL_MM_AMOEBA_WALL_END ?
8306 element >= EL_DF_STEEL_WALL_START &&
8307 element <= EL_DF_STEEL_WALL_END ?
8310 element >= EL_DF_WOODEN_WALL_START &&
8311 element <= EL_DF_WOODEN_WALL_END ?
8317 int map_mm_wall_element_editor(int element)
8321 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8322 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8323 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8324 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8325 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8326 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8328 default: return element;
8332 int get_next_element(int element)
8336 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8337 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8338 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8339 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8340 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8341 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8342 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8343 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8344 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8345 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8346 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8348 default: return element;
8352 int el2img_mm(int element_mm)
8354 return el2img(map_element_MM_to_RND(element_mm));
8357 int el_act2img_mm(int element_mm, int action)
8359 return el_act2img(map_element_MM_to_RND(element_mm), action);
8362 int el_act_dir2img(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_graphic[action][direction];
8371 static int el_act_dir2crm(int element, int action, int direction)
8373 element = GFX_ELEMENT(element);
8374 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8376 // direction_graphic[][] == graphic[] for undefined direction graphics
8377 return element_info[element].direction_crumbled[action][direction];
8380 int el_act2img(int element, int action)
8382 element = GFX_ELEMENT(element);
8384 return element_info[element].graphic[action];
8387 int el_act2crm(int element, int action)
8389 element = GFX_ELEMENT(element);
8391 return element_info[element].crumbled[action];
8394 int el_dir2img(int element, int direction)
8396 element = GFX_ELEMENT(element);
8398 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8401 int el2baseimg(int element)
8403 return element_info[element].graphic[ACTION_DEFAULT];
8406 int el2img(int element)
8408 element = GFX_ELEMENT(element);
8410 return element_info[element].graphic[ACTION_DEFAULT];
8413 int el2edimg(int element)
8415 element = GFX_ELEMENT(element);
8417 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8420 int el2preimg(int element)
8422 element = GFX_ELEMENT(element);
8424 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8427 int el2panelimg(int element)
8429 element = GFX_ELEMENT(element);
8431 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8434 int font2baseimg(int font_nr)
8436 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8439 int getBeltNrFromBeltElement(int element)
8441 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8442 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8443 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8446 int getBeltNrFromBeltActiveElement(int element)
8448 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8449 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8450 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8453 int getBeltNrFromBeltSwitchElement(int element)
8455 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8456 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8457 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8460 int getBeltDirNrFromBeltElement(int element)
8462 static int belt_base_element[4] =
8464 EL_CONVEYOR_BELT_1_LEFT,
8465 EL_CONVEYOR_BELT_2_LEFT,
8466 EL_CONVEYOR_BELT_3_LEFT,
8467 EL_CONVEYOR_BELT_4_LEFT
8470 int belt_nr = getBeltNrFromBeltElement(element);
8471 int belt_dir_nr = element - belt_base_element[belt_nr];
8473 return (belt_dir_nr % 3);
8476 int getBeltDirNrFromBeltSwitchElement(int element)
8478 static int belt_base_element[4] =
8480 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8481 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8482 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8483 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8486 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8487 int belt_dir_nr = element - belt_base_element[belt_nr];
8489 return (belt_dir_nr % 3);
8492 int getBeltDirFromBeltElement(int element)
8494 static int belt_move_dir[3] =
8501 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8503 return belt_move_dir[belt_dir_nr];
8506 int getBeltDirFromBeltSwitchElement(int element)
8508 static int belt_move_dir[3] =
8515 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8517 return belt_move_dir[belt_dir_nr];
8520 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8522 static int belt_base_element[4] =
8524 EL_CONVEYOR_BELT_1_LEFT,
8525 EL_CONVEYOR_BELT_2_LEFT,
8526 EL_CONVEYOR_BELT_3_LEFT,
8527 EL_CONVEYOR_BELT_4_LEFT
8530 return belt_base_element[belt_nr] + belt_dir_nr;
8533 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8535 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8537 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8540 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8542 static int belt_base_element[4] =
8544 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8545 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8546 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8547 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8550 return belt_base_element[belt_nr] + belt_dir_nr;
8553 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8555 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8557 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8560 boolean swapTiles_EM(boolean is_pre_emc_cave)
8562 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8565 boolean getTeamMode_EM(void)
8567 return game.team_mode || network_playing;
8570 boolean isActivePlayer_EM(int player_nr)
8572 return stored_player[player_nr].active;
8575 unsigned int InitRND(int seed)
8577 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8578 return InitEngineRandom_EM(seed);
8579 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8580 return InitEngineRandom_SP(seed);
8581 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8582 return InitEngineRandom_MM(seed);
8584 return InitEngineRandom_RND(seed);
8587 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8588 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8590 static int get_effective_element_EM(int tile, int frame_em)
8592 int element = object_mapping[tile].element_rnd;
8593 int action = object_mapping[tile].action;
8594 boolean is_backside = object_mapping[tile].is_backside;
8595 boolean action_removing = (action == ACTION_DIGGING ||
8596 action == ACTION_SNAPPING ||
8597 action == ACTION_COLLECTING);
8605 return (frame_em > 5 ? EL_EMPTY : element);
8611 else // frame_em == 7
8622 case Ydiamond_stone:
8626 case Xdrip_stretchB:
8642 case Ymagnify_blank:
8645 case Xsand_stonein_1:
8646 case Xsand_stonein_2:
8647 case Xsand_stonein_3:
8648 case Xsand_stonein_4:
8652 return (is_backside || action_removing ? EL_EMPTY : element);
8657 static boolean check_linear_animation_EM(int tile)
8661 case Xsand_stonesand_1:
8662 case Xsand_stonesand_quickout_1:
8663 case Xsand_sandstone_1:
8664 case Xsand_stonein_1:
8665 case Xsand_stoneout_1:
8693 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8694 boolean has_crumbled_graphics,
8695 int crumbled, int sync_frame)
8697 // if element can be crumbled, but certain action graphics are just empty
8698 // space (like instantly snapping sand to empty space in 1 frame), do not
8699 // treat these empty space graphics as crumbled graphics in EMC engine
8700 if (crumbled == IMG_EMPTY_SPACE)
8701 has_crumbled_graphics = FALSE;
8703 if (has_crumbled_graphics)
8705 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8706 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8707 g_crumbled->anim_delay,
8708 g_crumbled->anim_mode,
8709 g_crumbled->anim_start_frame,
8712 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8713 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8715 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8716 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8718 g_em->has_crumbled_graphics = TRUE;
8722 g_em->crumbled_bitmap = NULL;
8723 g_em->crumbled_src_x = 0;
8724 g_em->crumbled_src_y = 0;
8725 g_em->crumbled_border_size = 0;
8726 g_em->crumbled_tile_size = 0;
8728 g_em->has_crumbled_graphics = FALSE;
8733 void ResetGfxAnimation_EM(int x, int y, int tile)
8739 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8740 int tile, int frame_em, int x, int y)
8742 int action = object_mapping[tile].action;
8743 int direction = object_mapping[tile].direction;
8744 int effective_element = get_effective_element_EM(tile, frame_em);
8745 int graphic = (direction == MV_NONE ?
8746 el_act2img(effective_element, action) :
8747 el_act_dir2img(effective_element, action, direction));
8748 struct GraphicInfo *g = &graphic_info[graphic];
8750 boolean action_removing = (action == ACTION_DIGGING ||
8751 action == ACTION_SNAPPING ||
8752 action == ACTION_COLLECTING);
8753 boolean action_moving = (action == ACTION_FALLING ||
8754 action == ACTION_MOVING ||
8755 action == ACTION_PUSHING ||
8756 action == ACTION_EATING ||
8757 action == ACTION_FILLING ||
8758 action == ACTION_EMPTYING);
8759 boolean action_falling = (action == ACTION_FALLING ||
8760 action == ACTION_FILLING ||
8761 action == ACTION_EMPTYING);
8763 // special case: graphic uses "2nd movement tile" and has defined
8764 // 7 frames for movement animation (or less) => use default graphic
8765 // for last (8th) frame which ends the movement animation
8766 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8768 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8769 graphic = (direction == MV_NONE ?
8770 el_act2img(effective_element, action) :
8771 el_act_dir2img(effective_element, action, direction));
8773 g = &graphic_info[graphic];
8776 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8780 else if (action_moving)
8782 boolean is_backside = object_mapping[tile].is_backside;
8786 int direction = object_mapping[tile].direction;
8787 int move_dir = (action_falling ? MV_DOWN : direction);
8792 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8793 if (g->double_movement && frame_em == 0)
8797 if (move_dir == MV_LEFT)
8798 GfxFrame[x - 1][y] = GfxFrame[x][y];
8799 else if (move_dir == MV_RIGHT)
8800 GfxFrame[x + 1][y] = GfxFrame[x][y];
8801 else if (move_dir == MV_UP)
8802 GfxFrame[x][y - 1] = GfxFrame[x][y];
8803 else if (move_dir == MV_DOWN)
8804 GfxFrame[x][y + 1] = GfxFrame[x][y];
8811 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8812 if (tile == Xsand_stonesand_quickout_1 ||
8813 tile == Xsand_stonesand_quickout_2)
8817 if (graphic_info[graphic].anim_global_sync)
8818 sync_frame = FrameCounter;
8819 else if (graphic_info[graphic].anim_global_anim_sync)
8820 sync_frame = getGlobalAnimSyncFrame();
8821 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8822 sync_frame = GfxFrame[x][y];
8824 sync_frame = 0; // playfield border (pseudo steel)
8826 SetRandomAnimationValue(x, y);
8828 int frame = getAnimationFrame(g->anim_frames,
8831 g->anim_start_frame,
8834 g_em->unique_identifier =
8835 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8838 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8839 int tile, int frame_em, int x, int y)
8841 int action = object_mapping[tile].action;
8842 int direction = object_mapping[tile].direction;
8843 boolean is_backside = object_mapping[tile].is_backside;
8844 int effective_element = get_effective_element_EM(tile, frame_em);
8845 int effective_action = action;
8846 int graphic = (direction == MV_NONE ?
8847 el_act2img(effective_element, effective_action) :
8848 el_act_dir2img(effective_element, effective_action,
8850 int crumbled = (direction == MV_NONE ?
8851 el_act2crm(effective_element, effective_action) :
8852 el_act_dir2crm(effective_element, effective_action,
8854 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8855 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8856 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8857 struct GraphicInfo *g = &graphic_info[graphic];
8860 // special case: graphic uses "2nd movement tile" and has defined
8861 // 7 frames for movement animation (or less) => use default graphic
8862 // for last (8th) frame which ends the movement animation
8863 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8865 effective_action = ACTION_DEFAULT;
8866 graphic = (direction == MV_NONE ?
8867 el_act2img(effective_element, effective_action) :
8868 el_act_dir2img(effective_element, effective_action,
8870 crumbled = (direction == MV_NONE ?
8871 el_act2crm(effective_element, effective_action) :
8872 el_act_dir2crm(effective_element, effective_action,
8875 g = &graphic_info[graphic];
8878 if (graphic_info[graphic].anim_global_sync)
8879 sync_frame = FrameCounter;
8880 else if (graphic_info[graphic].anim_global_anim_sync)
8881 sync_frame = getGlobalAnimSyncFrame();
8882 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8883 sync_frame = GfxFrame[x][y];
8885 sync_frame = 0; // playfield border (pseudo steel)
8887 SetRandomAnimationValue(x, y);
8889 int frame = getAnimationFrame(g->anim_frames,
8892 g->anim_start_frame,
8895 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8896 g->double_movement && is_backside);
8898 // (updating the "crumbled" graphic definitions is probably not really needed,
8899 // as animations for crumbled graphics can't be longer than one EMC cycle)
8900 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8904 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8905 int player_nr, int anim, int frame_em)
8907 int element = player_mapping[player_nr][anim].element_rnd;
8908 int action = player_mapping[player_nr][anim].action;
8909 int direction = player_mapping[player_nr][anim].direction;
8910 int graphic = (direction == MV_NONE ?
8911 el_act2img(element, action) :
8912 el_act_dir2img(element, action, direction));
8913 struct GraphicInfo *g = &graphic_info[graphic];
8916 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8918 stored_player[player_nr].StepFrame = frame_em;
8920 sync_frame = stored_player[player_nr].Frame;
8922 int frame = getAnimationFrame(g->anim_frames,
8925 g->anim_start_frame,
8928 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8929 &g_em->src_x, &g_em->src_y, FALSE);
8932 void InitGraphicInfo_EM(void)
8936 // always start with reliable default values
8937 for (i = 0; i < GAME_TILE_MAX; i++)
8939 object_mapping[i].element_rnd = EL_UNKNOWN;
8940 object_mapping[i].is_backside = FALSE;
8941 object_mapping[i].action = ACTION_DEFAULT;
8942 object_mapping[i].direction = MV_NONE;
8945 // always start with reliable default values
8946 for (p = 0; p < MAX_PLAYERS; p++)
8948 for (i = 0; i < PLY_MAX; i++)
8950 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8951 player_mapping[p][i].action = ACTION_DEFAULT;
8952 player_mapping[p][i].direction = MV_NONE;
8956 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8958 int e = em_object_mapping_list[i].element_em;
8960 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8961 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8963 if (em_object_mapping_list[i].action != -1)
8964 object_mapping[e].action = em_object_mapping_list[i].action;
8966 if (em_object_mapping_list[i].direction != -1)
8967 object_mapping[e].direction =
8968 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8971 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8973 int a = em_player_mapping_list[i].action_em;
8974 int p = em_player_mapping_list[i].player_nr;
8976 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8978 if (em_player_mapping_list[i].action != -1)
8979 player_mapping[p][a].action = em_player_mapping_list[i].action;
8981 if (em_player_mapping_list[i].direction != -1)
8982 player_mapping[p][a].direction =
8983 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8986 for (i = 0; i < GAME_TILE_MAX; i++)
8988 int element = object_mapping[i].element_rnd;
8989 int action = object_mapping[i].action;
8990 int direction = object_mapping[i].direction;
8991 boolean is_backside = object_mapping[i].is_backside;
8992 boolean action_exploding = ((action == ACTION_EXPLODING ||
8993 action == ACTION_SMASHED_BY_ROCK ||
8994 action == ACTION_SMASHED_BY_SPRING) &&
8995 element != EL_DIAMOND);
8996 boolean action_active = (action == ACTION_ACTIVE);
8997 boolean action_other = (action == ACTION_OTHER);
8999 for (j = 0; j < 8; j++)
9001 int effective_element = get_effective_element_EM(i, j);
9002 int effective_action = (j < 7 ? action :
9003 i == Xdrip_stretch ? action :
9004 i == Xdrip_stretchB ? action :
9005 i == Ydrip_1_s ? action :
9006 i == Ydrip_1_sB ? action :
9007 i == Yball_1 ? action :
9008 i == Xball_2 ? action :
9009 i == Yball_2 ? action :
9010 i == Yball_blank ? action :
9011 i == Ykey_1_blank ? action :
9012 i == Ykey_2_blank ? action :
9013 i == Ykey_3_blank ? action :
9014 i == Ykey_4_blank ? action :
9015 i == Ykey_5_blank ? action :
9016 i == Ykey_6_blank ? action :
9017 i == Ykey_7_blank ? action :
9018 i == Ykey_8_blank ? action :
9019 i == Ylenses_blank ? action :
9020 i == Ymagnify_blank ? action :
9021 i == Ygrass_blank ? action :
9022 i == Ydirt_blank ? action :
9023 i == Xsand_stonein_1 ? action :
9024 i == Xsand_stonein_2 ? action :
9025 i == Xsand_stonein_3 ? action :
9026 i == Xsand_stonein_4 ? action :
9027 i == Xsand_stoneout_1 ? action :
9028 i == Xsand_stoneout_2 ? action :
9029 i == Xboom_android ? ACTION_EXPLODING :
9030 action_exploding ? ACTION_EXPLODING :
9031 action_active ? action :
9032 action_other ? action :
9034 int graphic = (el_act_dir2img(effective_element, effective_action,
9036 int crumbled = (el_act_dir2crm(effective_element, effective_action,
9038 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
9039 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
9040 boolean has_action_graphics = (graphic != base_graphic);
9041 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9042 struct GraphicInfo *g = &graphic_info[graphic];
9043 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9046 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9047 boolean special_animation = (action != ACTION_DEFAULT &&
9048 g->anim_frames == 3 &&
9049 g->anim_delay == 2 &&
9050 g->anim_mode & ANIM_LINEAR);
9051 int sync_frame = (i == Xdrip_stretch ? 7 :
9052 i == Xdrip_stretchB ? 7 :
9053 i == Ydrip_2_s ? j + 8 :
9054 i == Ydrip_2_sB ? j + 8 :
9063 i == Xfake_acid_1 ? 0 :
9064 i == Xfake_acid_2 ? 10 :
9065 i == Xfake_acid_3 ? 20 :
9066 i == Xfake_acid_4 ? 30 :
9067 i == Xfake_acid_5 ? 40 :
9068 i == Xfake_acid_6 ? 50 :
9069 i == Xfake_acid_7 ? 60 :
9070 i == Xfake_acid_8 ? 70 :
9071 i == Xfake_acid_1_player ? 0 :
9072 i == Xfake_acid_2_player ? 10 :
9073 i == Xfake_acid_3_player ? 20 :
9074 i == Xfake_acid_4_player ? 30 :
9075 i == Xfake_acid_5_player ? 40 :
9076 i == Xfake_acid_6_player ? 50 :
9077 i == Xfake_acid_7_player ? 60 :
9078 i == Xfake_acid_8_player ? 70 :
9080 i == Yball_2 ? j + 8 :
9081 i == Yball_blank ? j + 1 :
9082 i == Ykey_1_blank ? j + 1 :
9083 i == Ykey_2_blank ? j + 1 :
9084 i == Ykey_3_blank ? j + 1 :
9085 i == Ykey_4_blank ? j + 1 :
9086 i == Ykey_5_blank ? j + 1 :
9087 i == Ykey_6_blank ? j + 1 :
9088 i == Ykey_7_blank ? j + 1 :
9089 i == Ykey_8_blank ? j + 1 :
9090 i == Ylenses_blank ? j + 1 :
9091 i == Ymagnify_blank ? j + 1 :
9092 i == Ygrass_blank ? j + 1 :
9093 i == Ydirt_blank ? j + 1 :
9094 i == Xamoeba_1 ? 0 :
9095 i == Xamoeba_2 ? 1 :
9096 i == Xamoeba_3 ? 2 :
9097 i == Xamoeba_4 ? 3 :
9098 i == Xamoeba_5 ? 0 :
9099 i == Xamoeba_6 ? 1 :
9100 i == Xamoeba_7 ? 2 :
9101 i == Xamoeba_8 ? 3 :
9102 i == Xexit_2 ? j + 8 :
9103 i == Xexit_3 ? j + 16 :
9104 i == Xdynamite_1 ? 0 :
9105 i == Xdynamite_2 ? 8 :
9106 i == Xdynamite_3 ? 16 :
9107 i == Xdynamite_4 ? 24 :
9108 i == Xsand_stonein_1 ? j + 1 :
9109 i == Xsand_stonein_2 ? j + 9 :
9110 i == Xsand_stonein_3 ? j + 17 :
9111 i == Xsand_stonein_4 ? j + 25 :
9112 i == Xsand_stoneout_1 && j == 0 ? 0 :
9113 i == Xsand_stoneout_1 && j == 1 ? 0 :
9114 i == Xsand_stoneout_1 && j == 2 ? 1 :
9115 i == Xsand_stoneout_1 && j == 3 ? 2 :
9116 i == Xsand_stoneout_1 && j == 4 ? 2 :
9117 i == Xsand_stoneout_1 && j == 5 ? 3 :
9118 i == Xsand_stoneout_1 && j == 6 ? 4 :
9119 i == Xsand_stoneout_1 && j == 7 ? 4 :
9120 i == Xsand_stoneout_2 && j == 0 ? 5 :
9121 i == Xsand_stoneout_2 && j == 1 ? 6 :
9122 i == Xsand_stoneout_2 && j == 2 ? 7 :
9123 i == Xsand_stoneout_2 && j == 3 ? 8 :
9124 i == Xsand_stoneout_2 && j == 4 ? 9 :
9125 i == Xsand_stoneout_2 && j == 5 ? 11 :
9126 i == Xsand_stoneout_2 && j == 6 ? 13 :
9127 i == Xsand_stoneout_2 && j == 7 ? 15 :
9128 i == Xboom_bug && j == 1 ? 2 :
9129 i == Xboom_bug && j == 2 ? 2 :
9130 i == Xboom_bug && j == 3 ? 4 :
9131 i == Xboom_bug && j == 4 ? 4 :
9132 i == Xboom_bug && j == 5 ? 2 :
9133 i == Xboom_bug && j == 6 ? 2 :
9134 i == Xboom_bug && j == 7 ? 0 :
9135 i == Xboom_tank && j == 1 ? 2 :
9136 i == Xboom_tank && j == 2 ? 2 :
9137 i == Xboom_tank && j == 3 ? 4 :
9138 i == Xboom_tank && j == 4 ? 4 :
9139 i == Xboom_tank && j == 5 ? 2 :
9140 i == Xboom_tank && j == 6 ? 2 :
9141 i == Xboom_tank && j == 7 ? 0 :
9142 i == Xboom_android && j == 7 ? 6 :
9143 i == Xboom_1 && j == 1 ? 2 :
9144 i == Xboom_1 && j == 2 ? 2 :
9145 i == Xboom_1 && j == 3 ? 4 :
9146 i == Xboom_1 && j == 4 ? 4 :
9147 i == Xboom_1 && j == 5 ? 6 :
9148 i == Xboom_1 && j == 6 ? 6 :
9149 i == Xboom_1 && j == 7 ? 8 :
9150 i == Xboom_2 && j == 0 ? 8 :
9151 i == Xboom_2 && j == 1 ? 8 :
9152 i == Xboom_2 && j == 2 ? 10 :
9153 i == Xboom_2 && j == 3 ? 10 :
9154 i == Xboom_2 && j == 4 ? 10 :
9155 i == Xboom_2 && j == 5 ? 12 :
9156 i == Xboom_2 && j == 6 ? 12 :
9157 i == Xboom_2 && j == 7 ? 12 :
9158 special_animation && j == 4 ? 3 :
9159 effective_action != action ? 0 :
9161 int frame = getAnimationFrame(g->anim_frames,
9164 g->anim_start_frame,
9167 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9168 g->double_movement && is_backside);
9170 g_em->bitmap = src_bitmap;
9171 g_em->src_x = src_x;
9172 g_em->src_y = src_y;
9173 g_em->src_offset_x = 0;
9174 g_em->src_offset_y = 0;
9175 g_em->dst_offset_x = 0;
9176 g_em->dst_offset_y = 0;
9177 g_em->width = TILEX;
9178 g_em->height = TILEY;
9180 g_em->preserve_background = FALSE;
9182 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9185 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9186 effective_action == ACTION_MOVING ||
9187 effective_action == ACTION_PUSHING ||
9188 effective_action == ACTION_EATING)) ||
9189 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9190 effective_action == ACTION_EMPTYING)))
9193 (effective_action == ACTION_FALLING ||
9194 effective_action == ACTION_FILLING ||
9195 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9196 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9197 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9198 int num_steps = (i == Ydrip_1_s ? 16 :
9199 i == Ydrip_1_sB ? 16 :
9200 i == Ydrip_2_s ? 16 :
9201 i == Ydrip_2_sB ? 16 :
9202 i == Xsand_stonein_1 ? 32 :
9203 i == Xsand_stonein_2 ? 32 :
9204 i == Xsand_stonein_3 ? 32 :
9205 i == Xsand_stonein_4 ? 32 :
9206 i == Xsand_stoneout_1 ? 16 :
9207 i == Xsand_stoneout_2 ? 16 : 8);
9208 int cx = ABS(dx) * (TILEX / num_steps);
9209 int cy = ABS(dy) * (TILEY / num_steps);
9210 int step_frame = (i == Ydrip_2_s ? j + 8 :
9211 i == Ydrip_2_sB ? j + 8 :
9212 i == Xsand_stonein_2 ? j + 8 :
9213 i == Xsand_stonein_3 ? j + 16 :
9214 i == Xsand_stonein_4 ? j + 24 :
9215 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9216 int step = (is_backside ? step_frame : num_steps - step_frame);
9218 if (is_backside) // tile where movement starts
9220 if (dx < 0 || dy < 0)
9222 g_em->src_offset_x = cx * step;
9223 g_em->src_offset_y = cy * step;
9227 g_em->dst_offset_x = cx * step;
9228 g_em->dst_offset_y = cy * step;
9231 else // tile where movement ends
9233 if (dx < 0 || dy < 0)
9235 g_em->dst_offset_x = cx * step;
9236 g_em->dst_offset_y = cy * step;
9240 g_em->src_offset_x = cx * step;
9241 g_em->src_offset_y = cy * step;
9245 g_em->width = TILEX - cx * step;
9246 g_em->height = TILEY - cy * step;
9249 // create unique graphic identifier to decide if tile must be redrawn
9250 /* bit 31 - 16 (16 bit): EM style graphic
9251 bit 15 - 12 ( 4 bit): EM style frame
9252 bit 11 - 6 ( 6 bit): graphic width
9253 bit 5 - 0 ( 6 bit): graphic height */
9254 g_em->unique_identifier =
9255 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9259 for (i = 0; i < GAME_TILE_MAX; i++)
9261 for (j = 0; j < 8; j++)
9263 int element = object_mapping[i].element_rnd;
9264 int action = object_mapping[i].action;
9265 int direction = object_mapping[i].direction;
9266 boolean is_backside = object_mapping[i].is_backside;
9267 int graphic_action = el_act_dir2img(element, action, direction);
9268 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9270 if ((action == ACTION_SMASHED_BY_ROCK ||
9271 action == ACTION_SMASHED_BY_SPRING ||
9272 action == ACTION_EATING) &&
9273 graphic_action == graphic_default)
9275 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9276 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9277 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9278 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9281 // no separate animation for "smashed by rock" -- use rock instead
9282 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9283 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9285 g_em->bitmap = g_xx->bitmap;
9286 g_em->src_x = g_xx->src_x;
9287 g_em->src_y = g_xx->src_y;
9288 g_em->src_offset_x = g_xx->src_offset_x;
9289 g_em->src_offset_y = g_xx->src_offset_y;
9290 g_em->dst_offset_x = g_xx->dst_offset_x;
9291 g_em->dst_offset_y = g_xx->dst_offset_y;
9292 g_em->width = g_xx->width;
9293 g_em->height = g_xx->height;
9294 g_em->unique_identifier = g_xx->unique_identifier;
9297 g_em->preserve_background = TRUE;
9302 for (p = 0; p < MAX_PLAYERS; p++)
9304 for (i = 0; i < PLY_MAX; i++)
9306 int element = player_mapping[p][i].element_rnd;
9307 int action = player_mapping[p][i].action;
9308 int direction = player_mapping[p][i].direction;
9310 for (j = 0; j < 8; j++)
9312 int effective_element = element;
9313 int effective_action = action;
9314 int graphic = (direction == MV_NONE ?
9315 el_act2img(effective_element, effective_action) :
9316 el_act_dir2img(effective_element, effective_action,
9318 struct GraphicInfo *g = &graphic_info[graphic];
9319 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9323 int frame = getAnimationFrame(g->anim_frames,
9326 g->anim_start_frame,
9329 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9331 g_em->bitmap = src_bitmap;
9332 g_em->src_x = src_x;
9333 g_em->src_y = src_y;
9334 g_em->src_offset_x = 0;
9335 g_em->src_offset_y = 0;
9336 g_em->dst_offset_x = 0;
9337 g_em->dst_offset_y = 0;
9338 g_em->width = TILEX;
9339 g_em->height = TILEY;
9345 static void CheckSaveEngineSnapshot_EM(int frame,
9346 boolean any_player_moving,
9347 boolean any_player_snapping,
9348 boolean any_player_dropping)
9350 if (frame == 7 && !any_player_dropping)
9352 if (!local_player->was_waiting)
9354 if (!CheckSaveEngineSnapshotToList())
9357 local_player->was_waiting = TRUE;
9360 else if (any_player_moving || any_player_snapping || any_player_dropping)
9362 local_player->was_waiting = FALSE;
9366 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9367 boolean murphy_is_dropping)
9369 if (murphy_is_waiting)
9371 if (!local_player->was_waiting)
9373 if (!CheckSaveEngineSnapshotToList())
9376 local_player->was_waiting = TRUE;
9381 local_player->was_waiting = FALSE;
9385 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9386 boolean button_released)
9388 if (button_released)
9390 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9391 CheckSaveEngineSnapshotToList();
9393 else if (element_clicked)
9395 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9396 CheckSaveEngineSnapshotToList();
9398 game.snapshot.changed_action = TRUE;
9402 boolean CheckSingleStepMode_EM(int frame,
9403 boolean any_player_moving,
9404 boolean any_player_snapping,
9405 boolean any_player_dropping)
9407 if (tape.single_step && tape.recording && !tape.pausing)
9408 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9409 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9411 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9412 any_player_snapping, any_player_dropping);
9414 return tape.pausing;
9417 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9418 boolean murphy_is_dropping)
9420 boolean murphy_starts_dropping = FALSE;
9423 for (i = 0; i < MAX_PLAYERS; i++)
9424 if (stored_player[i].force_dropping)
9425 murphy_starts_dropping = TRUE;
9427 if (tape.single_step && tape.recording && !tape.pausing)
9428 if (murphy_is_waiting && !murphy_starts_dropping)
9429 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9431 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9434 void CheckSingleStepMode_MM(boolean element_clicked,
9435 boolean button_released)
9437 if (tape.single_step && tape.recording && !tape.pausing)
9438 if (button_released)
9439 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9441 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9444 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9445 int graphic, int sync_frame)
9447 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9449 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9452 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9454 return (IS_NEXT_FRAME(sync_frame, graphic));
9457 int getGraphicInfo_Delay(int graphic)
9459 return graphic_info[graphic].anim_delay;
9462 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9464 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9467 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9473 void PlayMenuSoundExt(int sound)
9475 if (sound == SND_UNDEFINED)
9478 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9479 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9482 if (IS_LOOP_SOUND(sound))
9483 PlaySoundLoop(sound);
9488 void PlayMenuSound(void)
9490 PlayMenuSoundExt(menu.sound[game_status]);
9493 void PlayMenuSoundStereo(int sound, int stereo_position)
9495 if (sound == SND_UNDEFINED)
9498 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9499 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9502 if (IS_LOOP_SOUND(sound))
9503 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9505 PlaySoundStereo(sound, stereo_position);
9508 void PlayMenuSoundIfLoopExt(int sound)
9510 if (sound == SND_UNDEFINED)
9513 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9514 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9517 if (IS_LOOP_SOUND(sound))
9518 PlaySoundLoop(sound);
9521 void PlayMenuSoundIfLoop(void)
9523 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9526 void PlayMenuMusicExt(int music)
9528 if (music == MUS_UNDEFINED)
9531 if (!setup.sound_music)
9534 if (IS_LOOP_MUSIC(music))
9535 PlayMusicLoop(music);
9540 void PlayMenuMusic(void)
9542 char *curr_music = getCurrentlyPlayingMusicFilename();
9543 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9545 if (!strEqual(curr_music, next_music))
9546 PlayMenuMusicExt(menu.music[game_status]);
9549 void PlayMenuSoundsAndMusic(void)
9555 static void FadeMenuSounds(void)
9560 static void FadeMenuMusic(void)
9562 char *curr_music = getCurrentlyPlayingMusicFilename();
9563 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9565 if (!strEqual(curr_music, next_music))
9569 void FadeMenuSoundsAndMusic(void)
9575 void PlaySoundActivating(void)
9578 PlaySound(SND_MENU_ITEM_ACTIVATING);
9582 void PlaySoundSelecting(void)
9585 PlaySound(SND_MENU_ITEM_SELECTING);
9589 void ToggleFullscreenIfNeeded(void)
9591 // if setup and video fullscreen state are already matching, nothing do do
9592 if (setup.fullscreen == video.fullscreen_enabled ||
9593 !video.fullscreen_available)
9596 SDLSetWindowFullscreen(setup.fullscreen);
9598 // set setup value according to successfully changed fullscreen mode
9599 setup.fullscreen = video.fullscreen_enabled;
9602 void ChangeWindowScalingIfNeeded(void)
9604 // if setup and video window scaling are already matching, nothing do do
9605 if (setup.window_scaling_percent == video.window_scaling_percent ||
9606 video.fullscreen_enabled)
9609 SDLSetWindowScaling(setup.window_scaling_percent);
9611 // set setup value according to successfully changed window scaling
9612 setup.window_scaling_percent = video.window_scaling_percent;
9615 void ChangeVsyncModeIfNeeded(void)
9617 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9618 int video_vsync_mode = video.vsync_mode;
9620 // if setup and video vsync mode are already matching, nothing do do
9621 if (setup_vsync_mode == video_vsync_mode)
9624 // if renderer is using OpenGL, vsync mode can directly be changed
9625 SDLSetScreenVsyncMode(setup.vsync_mode);
9627 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9628 if (video.vsync_mode == video_vsync_mode)
9630 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9632 // save backbuffer content which gets lost when re-creating screen
9633 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9635 // force re-creating screen and renderer to set new vsync mode
9636 video.fullscreen_enabled = !setup.fullscreen;
9638 // when creating new renderer, destroy textures linked to old renderer
9639 FreeAllImageTextures(); // needs old renderer to free the textures
9641 // re-create screen and renderer (including change of vsync mode)
9642 ChangeVideoModeIfNeeded(setup.fullscreen);
9644 // set setup value according to successfully changed fullscreen mode
9645 setup.fullscreen = video.fullscreen_enabled;
9647 // restore backbuffer content from temporary backbuffer backup bitmap
9648 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9649 FreeBitmap(tmp_backbuffer);
9651 // update visible window/screen
9652 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9654 // when changing vsync mode, re-create textures for new renderer
9655 InitImageTextures();
9658 // set setup value according to successfully changed vsync mode
9659 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9662 static void JoinRectangles(int *x, int *y, int *width, int *height,
9663 int x2, int y2, int width2, int height2)
9665 // do not join with "off-screen" rectangle
9666 if (x2 == -1 || y2 == -1)
9671 *width = MAX(*width, width2);
9672 *height = MAX(*height, height2);
9675 void SetAnimStatus(int anim_status_new)
9677 if (anim_status_new == GAME_MODE_MAIN)
9678 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9679 else if (anim_status_new == GAME_MODE_NAMES)
9680 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9681 else if (anim_status_new == GAME_MODE_SCORES)
9682 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9684 global.anim_status_next = anim_status_new;
9686 // directly set screen modes that are entered without fading
9687 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9688 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9689 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9690 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9691 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9692 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9693 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9694 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9695 global.anim_status = global.anim_status_next;
9698 void SetGameStatus(int game_status_new)
9700 if (game_status_new != game_status)
9701 game_status_last_screen = game_status;
9703 game_status = game_status_new;
9705 SetAnimStatus(game_status_new);
9708 void SetFontStatus(int game_status_new)
9710 static int last_game_status = -1;
9712 if (game_status_new != -1)
9714 // set game status for font use after storing last game status
9715 last_game_status = game_status;
9716 game_status = game_status_new;
9720 // reset game status after font use from last stored game status
9721 game_status = last_game_status;
9725 void ResetFontStatus(void)
9730 void SetLevelSetInfo(char *identifier, int level_nr)
9732 setString(&levelset.identifier, identifier);
9734 levelset.level_nr = level_nr;
9737 boolean CheckIfAllViewportsHaveChanged(void)
9739 // if game status has not changed, viewports have not changed either
9740 if (game_status == game_status_last)
9743 // check if all viewports have changed with current game status
9745 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9746 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9747 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9748 int new_real_sx = vp_playfield->x;
9749 int new_real_sy = vp_playfield->y;
9750 int new_full_sxsize = vp_playfield->width;
9751 int new_full_sysize = vp_playfield->height;
9752 int new_dx = vp_door_1->x;
9753 int new_dy = vp_door_1->y;
9754 int new_dxsize = vp_door_1->width;
9755 int new_dysize = vp_door_1->height;
9756 int new_vx = vp_door_2->x;
9757 int new_vy = vp_door_2->y;
9758 int new_vxsize = vp_door_2->width;
9759 int new_vysize = vp_door_2->height;
9761 boolean playfield_viewport_has_changed =
9762 (new_real_sx != REAL_SX ||
9763 new_real_sy != REAL_SY ||
9764 new_full_sxsize != FULL_SXSIZE ||
9765 new_full_sysize != FULL_SYSIZE);
9767 boolean door_1_viewport_has_changed =
9770 new_dxsize != DXSIZE ||
9771 new_dysize != DYSIZE);
9773 boolean door_2_viewport_has_changed =
9776 new_vxsize != VXSIZE ||
9777 new_vysize != VYSIZE ||
9778 game_status_last == GAME_MODE_EDITOR);
9780 return (playfield_viewport_has_changed &&
9781 door_1_viewport_has_changed &&
9782 door_2_viewport_has_changed);
9785 boolean CheckFadeAll(void)
9787 return (CheckIfGlobalBorderHasChanged() ||
9788 CheckIfAllViewportsHaveChanged());
9791 void ChangeViewportPropertiesIfNeeded(void)
9793 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9794 FALSE : setup.small_game_graphics);
9795 int gfx_game_mode = getGlobalGameStatus(game_status);
9796 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9798 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9799 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9800 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9801 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9802 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9803 int new_win_xsize = vp_window->width;
9804 int new_win_ysize = vp_window->height;
9805 int border_left = vp_playfield->border_left;
9806 int border_right = vp_playfield->border_right;
9807 int border_top = vp_playfield->border_top;
9808 int border_bottom = vp_playfield->border_bottom;
9809 int new_sx = vp_playfield->x + border_left;
9810 int new_sy = vp_playfield->y + border_top;
9811 int new_sxsize = vp_playfield->width - border_left - border_right;
9812 int new_sysize = vp_playfield->height - border_top - border_bottom;
9813 int new_real_sx = vp_playfield->x;
9814 int new_real_sy = vp_playfield->y;
9815 int new_full_sxsize = vp_playfield->width;
9816 int new_full_sysize = vp_playfield->height;
9817 int new_dx = vp_door_1->x;
9818 int new_dy = vp_door_1->y;
9819 int new_dxsize = vp_door_1->width;
9820 int new_dysize = vp_door_1->height;
9821 int new_vx = vp_door_2->x;
9822 int new_vy = vp_door_2->y;
9823 int new_vxsize = vp_door_2->width;
9824 int new_vysize = vp_door_2->height;
9825 int new_ex = vp_door_3->x;
9826 int new_ey = vp_door_3->y;
9827 int new_exsize = vp_door_3->width;
9828 int new_eysize = vp_door_3->height;
9829 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9830 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9831 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9832 int new_scr_fieldx = new_sxsize / tilesize;
9833 int new_scr_fieldy = new_sysize / tilesize;
9834 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9835 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9836 boolean init_gfx_buffers = FALSE;
9837 boolean init_video_buffer = FALSE;
9838 boolean init_gadgets_and_anims = FALSE;
9839 boolean init_em_graphics = FALSE;
9841 if (new_win_xsize != WIN_XSIZE ||
9842 new_win_ysize != WIN_YSIZE)
9844 WIN_XSIZE = new_win_xsize;
9845 WIN_YSIZE = new_win_ysize;
9847 init_video_buffer = TRUE;
9848 init_gfx_buffers = TRUE;
9849 init_gadgets_and_anims = TRUE;
9851 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9854 if (new_scr_fieldx != SCR_FIELDX ||
9855 new_scr_fieldy != SCR_FIELDY)
9857 // this always toggles between MAIN and GAME when using small tile size
9859 SCR_FIELDX = new_scr_fieldx;
9860 SCR_FIELDY = new_scr_fieldy;
9862 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9873 new_sxsize != SXSIZE ||
9874 new_sysize != SYSIZE ||
9875 new_dxsize != DXSIZE ||
9876 new_dysize != DYSIZE ||
9877 new_vxsize != VXSIZE ||
9878 new_vysize != VYSIZE ||
9879 new_exsize != EXSIZE ||
9880 new_eysize != EYSIZE ||
9881 new_real_sx != REAL_SX ||
9882 new_real_sy != REAL_SY ||
9883 new_full_sxsize != FULL_SXSIZE ||
9884 new_full_sysize != FULL_SYSIZE ||
9885 new_tilesize_var != TILESIZE_VAR
9888 // ------------------------------------------------------------------------
9889 // determine next fading area for changed viewport definitions
9890 // ------------------------------------------------------------------------
9892 // start with current playfield area (default fading area)
9895 FADE_SXSIZE = FULL_SXSIZE;
9896 FADE_SYSIZE = FULL_SYSIZE;
9898 // add new playfield area if position or size has changed
9899 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9900 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9902 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9903 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9906 // add current and new door 1 area if position or size has changed
9907 if (new_dx != DX || new_dy != DY ||
9908 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9910 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9911 DX, DY, DXSIZE, DYSIZE);
9912 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9913 new_dx, new_dy, new_dxsize, new_dysize);
9916 // add current and new door 2 area if position or size has changed
9917 if (new_vx != VX || new_vy != VY ||
9918 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9920 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9921 VX, VY, VXSIZE, VYSIZE);
9922 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9923 new_vx, new_vy, new_vxsize, new_vysize);
9926 // ------------------------------------------------------------------------
9927 // handle changed tile size
9928 // ------------------------------------------------------------------------
9930 if (new_tilesize_var != TILESIZE_VAR)
9932 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9934 // changing tile size invalidates scroll values of engine snapshots
9935 FreeEngineSnapshotSingle();
9937 // changing tile size requires update of graphic mapping for EM engine
9938 init_em_graphics = TRUE;
9949 SXSIZE = new_sxsize;
9950 SYSIZE = new_sysize;
9951 DXSIZE = new_dxsize;
9952 DYSIZE = new_dysize;
9953 VXSIZE = new_vxsize;
9954 VYSIZE = new_vysize;
9955 EXSIZE = new_exsize;
9956 EYSIZE = new_eysize;
9957 REAL_SX = new_real_sx;
9958 REAL_SY = new_real_sy;
9959 FULL_SXSIZE = new_full_sxsize;
9960 FULL_SYSIZE = new_full_sysize;
9961 TILESIZE_VAR = new_tilesize_var;
9963 init_gfx_buffers = TRUE;
9964 init_gadgets_and_anims = TRUE;
9966 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9967 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9970 if (init_gfx_buffers)
9972 // Debug("tools:viewport", "init_gfx_buffers");
9974 SCR_FIELDX = new_scr_fieldx_buffers;
9975 SCR_FIELDY = new_scr_fieldy_buffers;
9979 SCR_FIELDX = new_scr_fieldx;
9980 SCR_FIELDY = new_scr_fieldy;
9982 SetDrawDeactivationMask(REDRAW_NONE);
9983 SetDrawBackgroundMask(REDRAW_FIELD);
9986 if (init_video_buffer)
9988 // Debug("tools:viewport", "init_video_buffer");
9990 FreeAllImageTextures(); // needs old renderer to free the textures
9992 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9993 InitImageTextures();
9996 if (init_gadgets_and_anims)
9998 // Debug("tools:viewport", "init_gadgets_and_anims");
10001 InitGlobalAnimations();
10004 if (init_em_graphics)
10006 InitGraphicInfo_EM();
10010 void OpenURL(char *url)
10012 #if SDL_VERSION_ATLEAST(2,0,14)
10015 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
10016 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
10017 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
10021 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
10023 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
10027 // ============================================================================
10029 // ============================================================================
10031 #if defined(PLATFORM_WINDOWS)
10032 /* FILETIME of Jan 1 1970 00:00:00. */
10033 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
10036 * timezone information is stored outside the kernel so tzp isn't used anymore.
10038 * Note: this function is not for Win32 high precision timing purpose. See
10041 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
10043 FILETIME file_time;
10044 SYSTEMTIME system_time;
10045 ULARGE_INTEGER ularge;
10047 GetSystemTime(&system_time);
10048 SystemTimeToFileTime(&system_time, &file_time);
10049 ularge.LowPart = file_time.dwLowDateTime;
10050 ularge.HighPart = file_time.dwHighDateTime;
10052 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10053 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10059 static char *test_init_uuid_random_function_simple(void)
10061 static char seed_text[100];
10062 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10064 sprintf(seed_text, "%d", seed);
10069 static char *test_init_uuid_random_function_better(void)
10071 static char seed_text[100];
10072 struct timeval current_time;
10074 gettimeofday(¤t_time, NULL);
10076 prng_seed_bytes(¤t_time, sizeof(current_time));
10078 sprintf(seed_text, "%ld.%ld",
10079 (long)current_time.tv_sec,
10080 (long)current_time.tv_usec);
10085 #if defined(PLATFORM_WINDOWS)
10086 static char *test_init_uuid_random_function_better_windows(void)
10088 static char seed_text[100];
10089 struct timeval current_time;
10091 gettimeofday_windows(¤t_time, NULL);
10093 prng_seed_bytes(¤t_time, sizeof(current_time));
10095 sprintf(seed_text, "%ld.%ld",
10096 (long)current_time.tv_sec,
10097 (long)current_time.tv_usec);
10103 static unsigned int test_uuid_random_function_simple(int max)
10105 return GetSimpleRandom(max);
10108 static unsigned int test_uuid_random_function_better(int max)
10110 return (max > 0 ? prng_get_uint() % max : 0);
10113 #if defined(PLATFORM_WINDOWS)
10114 #define NUM_UUID_TESTS 3
10116 #define NUM_UUID_TESTS 2
10119 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10121 struct hashtable *hash_seeds =
10122 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10123 struct hashtable *hash_uuids =
10124 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10125 static char message[100];
10128 char *random_name = (nr == 0 ? "simple" : "better");
10129 char *random_type = (always_seed ? "always" : "only once");
10130 char *(*init_random_function)(void) =
10132 test_init_uuid_random_function_simple :
10133 test_init_uuid_random_function_better);
10134 unsigned int (*random_function)(int) =
10136 test_uuid_random_function_simple :
10137 test_uuid_random_function_better);
10140 #if defined(PLATFORM_WINDOWS)
10143 random_name = "windows";
10144 init_random_function = test_init_uuid_random_function_better_windows;
10150 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10151 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10153 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10154 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10155 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10157 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10161 // always initialize random number generator at least once
10162 init_random_function();
10164 unsigned int time_start = SDL_GetTicks();
10166 for (i = 0; i < num_uuids; i++)
10170 char *seed = getStringCopy(init_random_function());
10172 hashtable_remove(hash_seeds, seed);
10173 hashtable_insert(hash_seeds, seed, "1");
10176 char *uuid = getStringCopy(getUUIDExt(random_function));
10178 hashtable_remove(hash_uuids, uuid);
10179 hashtable_insert(hash_uuids, uuid, "1");
10182 int num_unique_seeds = hashtable_count(hash_seeds);
10183 int num_unique_uuids = hashtable_count(hash_uuids);
10185 unsigned int time_needed = SDL_GetTicks() - time_start;
10187 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10189 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10192 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10194 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10195 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10197 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10199 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10201 Request(message, REQ_CONFIRM);
10203 hashtable_destroy(hash_seeds, 0);
10204 hashtable_destroy(hash_uuids, 0);
10207 void TestGeneratingUUIDs(void)
10209 int num_uuids = 1000000;
10212 for (i = 0; i < NUM_UUID_TESTS; i++)
10213 for (j = 0; j < 2; j++)
10214 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10216 CloseAllAndExit(0);