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 SDLFreeBitmapTextures(request.bitmap);
3043 SDLCreateBitmapTextures(request.bitmap);
3045 // set envelope request run-time values
3048 request.xsize = xsize;
3049 request.ysize = ysize;
3052 void DrawEnvelopeRequestToScreen(int drawing_target)
3054 if (global.use_envelope_request &&
3055 game.request_active_or_moving &&
3056 drawing_target == DRAW_TO_SCREEN)
3058 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3059 request.sx, request.sy);
3063 static void setRequestBasePosition(int *x, int *y)
3065 int sx_base, sy_base;
3067 if (request.x != -1)
3068 sx_base = request.x;
3069 else if (request.align == ALIGN_LEFT)
3071 else if (request.align == ALIGN_RIGHT)
3072 sx_base = SX + SXSIZE;
3074 sx_base = SX + SXSIZE / 2;
3076 if (request.y != -1)
3077 sy_base = request.y;
3078 else if (request.valign == VALIGN_TOP)
3080 else if (request.valign == VALIGN_BOTTOM)
3081 sy_base = SY + SYSIZE;
3083 sy_base = SY + SYSIZE / 2;
3089 static void setRequestPositionExt(int *x, int *y, int width, int height,
3090 boolean add_border_size)
3092 int border_size = request.border_size;
3093 int sx_base, sy_base;
3096 setRequestBasePosition(&sx_base, &sy_base);
3098 if (request.align == ALIGN_LEFT)
3100 else if (request.align == ALIGN_RIGHT)
3101 sx = sx_base - width;
3103 sx = sx_base - width / 2;
3105 if (request.valign == VALIGN_TOP)
3107 else if (request.valign == VALIGN_BOTTOM)
3108 sy = sy_base - height;
3110 sy = sy_base - height / 2;
3112 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3113 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3115 if (add_border_size)
3125 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3127 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3130 static void DrawEnvelopeRequest(char *text)
3132 char *text_final = text;
3133 char *text_door_style = NULL;
3134 int graphic = IMG_BACKGROUND_REQUEST;
3135 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3136 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3137 int font_nr = FONT_REQUEST;
3138 int font_width = getFontWidth(font_nr);
3139 int font_height = getFontHeight(font_nr);
3140 int border_size = request.border_size;
3141 int line_spacing = request.line_spacing;
3142 int line_height = font_height + line_spacing;
3143 int max_text_width = request.width - 2 * border_size;
3144 int max_text_height = request.height - 2 * border_size;
3145 int line_length = max_text_width / font_width;
3146 int max_lines = max_text_height / line_height;
3147 int text_width = line_length * font_width;
3148 int width = request.width;
3149 int height = request.height;
3150 int tile_size = MAX(request.step_offset, 1);
3151 int x_steps = width / tile_size;
3152 int y_steps = height / tile_size;
3153 int sx_offset = border_size;
3154 int sy_offset = border_size;
3158 if (request.centered)
3159 sx_offset = (request.width - text_width) / 2;
3161 if (request.wrap_single_words && !request.autowrap)
3163 char *src_text_ptr, *dst_text_ptr;
3165 text_door_style = checked_malloc(2 * strlen(text) + 1);
3167 src_text_ptr = text;
3168 dst_text_ptr = text_door_style;
3170 while (*src_text_ptr)
3172 if (*src_text_ptr == ' ' ||
3173 *src_text_ptr == '?' ||
3174 *src_text_ptr == '!')
3175 *dst_text_ptr++ = '\n';
3177 if (*src_text_ptr != ' ')
3178 *dst_text_ptr++ = *src_text_ptr;
3183 *dst_text_ptr = '\0';
3185 text_final = text_door_style;
3188 setRequestPosition(&sx, &sy, FALSE);
3190 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3192 for (y = 0; y < y_steps; y++)
3193 for (x = 0; x < x_steps; x++)
3194 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3195 x, y, x_steps, y_steps,
3196 tile_size, tile_size);
3198 // force DOOR font inside door area
3199 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3201 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3202 line_length, -1, max_lines, line_spacing, mask_mode,
3203 request.autowrap, request.centered, FALSE);
3207 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3208 RedrawGadget(tool_gadget[i]);
3210 // store readily prepared envelope request for later use when animating
3211 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3213 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3215 if (text_door_style)
3216 free(text_door_style);
3219 static void AnimateEnvelopeRequest(int anim_mode, int action)
3221 int graphic = IMG_BACKGROUND_REQUEST;
3222 boolean draw_masked = graphic_info[graphic].draw_masked;
3223 int delay_value_normal = request.step_delay;
3224 int delay_value_fast = delay_value_normal / 2;
3225 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3226 boolean no_delay = (tape.warp_forward);
3227 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3228 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3229 DelayCounter anim_delay = { anim_delay_value };
3231 int tile_size = MAX(request.step_offset, 1);
3232 int max_xsize = request.width / tile_size;
3233 int max_ysize = request.height / tile_size;
3234 int max_xsize_inner = max_xsize - 2;
3235 int max_ysize_inner = max_ysize - 2;
3237 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3238 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3239 int xend = max_xsize_inner;
3240 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3241 int xstep = (xstart < xend ? 1 : 0);
3242 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3244 int end = MAX(xend - xstart, yend - ystart);
3247 if (setup.quick_doors)
3254 for (i = start; i <= end; i++)
3256 int last_frame = end; // last frame of this "for" loop
3257 int x = xstart + i * xstep;
3258 int y = ystart + i * ystep;
3259 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3260 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3261 int xsize_size_left = (xsize - 1) * tile_size;
3262 int ysize_size_top = (ysize - 1) * tile_size;
3263 int max_xsize_pos = (max_xsize - 1) * tile_size;
3264 int max_ysize_pos = (max_ysize - 1) * tile_size;
3265 int width = xsize * tile_size;
3266 int height = ysize * tile_size;
3271 setRequestPosition(&src_x, &src_y, FALSE);
3272 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3274 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3276 for (yy = 0; yy < 2; yy++)
3278 for (xx = 0; xx < 2; xx++)
3280 int src_xx = src_x + xx * max_xsize_pos;
3281 int src_yy = src_y + yy * max_ysize_pos;
3282 int dst_xx = dst_x + xx * xsize_size_left;
3283 int dst_yy = dst_y + yy * ysize_size_top;
3284 int xx_size = (xx ? tile_size : xsize_size_left);
3285 int yy_size = (yy ? tile_size : ysize_size_top);
3288 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3289 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3291 BlitBitmap(bitmap_db_store_2, backbuffer,
3292 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3296 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3298 redraw_mask |= REDRAW_FIELD;
3302 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3305 ClearAutoRepeatKeyEvents();
3308 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3310 int graphic = IMG_BACKGROUND_REQUEST;
3311 int sound_opening = SND_REQUEST_OPENING;
3312 int sound_closing = SND_REQUEST_CLOSING;
3313 int anim_mode_1 = request.anim_mode; // (higher priority)
3314 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3315 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3316 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3317 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3319 if (game_status == GAME_MODE_PLAYING)
3320 BlitScreenToBitmap(backbuffer);
3322 SetDrawtoField(DRAW_TO_BACKBUFFER);
3324 // SetDrawBackgroundMask(REDRAW_NONE);
3326 if (action == ACTION_OPENING)
3328 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3330 if (req_state & REQ_ASK)
3332 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3333 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3334 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3335 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3337 else if (req_state & REQ_CONFIRM)
3339 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3340 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3342 else if (req_state & REQ_PLAYER)
3344 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3345 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3346 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3347 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3350 DrawEnvelopeRequest(text);
3353 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3355 if (action == ACTION_OPENING)
3357 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3359 if (anim_mode == ANIM_DEFAULT)
3360 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3362 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3366 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3368 if (anim_mode != ANIM_NONE)
3369 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3371 if (anim_mode == ANIM_DEFAULT)
3372 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3375 game.envelope_active = FALSE;
3377 if (action == ACTION_CLOSING)
3378 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3380 // SetDrawBackgroundMask(last_draw_background_mask);
3382 redraw_mask |= REDRAW_FIELD;
3386 if (action == ACTION_CLOSING &&
3387 game_status == GAME_MODE_PLAYING &&
3388 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3389 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3392 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3394 if (IS_MM_WALL(element))
3396 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3402 int graphic = el2preimg(element);
3404 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3405 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3410 void DrawLevel(int draw_background_mask)
3414 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3415 SetDrawBackgroundMask(draw_background_mask);
3419 for (x = BX1; x <= BX2; x++)
3420 for (y = BY1; y <= BY2; y++)
3421 DrawScreenField(x, y);
3423 redraw_mask |= REDRAW_FIELD;
3426 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3431 for (x = 0; x < size_x; x++)
3432 for (y = 0; y < size_y; y++)
3433 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3435 redraw_mask |= REDRAW_FIELD;
3438 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3442 for (x = 0; x < size_x; x++)
3443 for (y = 0; y < size_y; y++)
3444 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3446 redraw_mask |= REDRAW_FIELD;
3449 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3451 boolean show_level_border = (BorderElement != EL_EMPTY);
3452 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3453 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3454 int tile_size = preview.tile_size;
3455 int preview_width = preview.xsize * tile_size;
3456 int preview_height = preview.ysize * tile_size;
3457 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3458 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3459 int real_preview_width = real_preview_xsize * tile_size;
3460 int real_preview_height = real_preview_ysize * tile_size;
3461 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3462 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3465 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3468 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3470 dst_x += (preview_width - real_preview_width) / 2;
3471 dst_y += (preview_height - real_preview_height) / 2;
3473 for (x = 0; x < real_preview_xsize; x++)
3475 for (y = 0; y < real_preview_ysize; y++)
3477 int lx = from_x + x + (show_level_border ? -1 : 0);
3478 int ly = from_y + y + (show_level_border ? -1 : 0);
3479 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3480 getBorderElement(lx, ly));
3482 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3483 element, tile_size);
3487 redraw_mask |= REDRAW_FIELD;
3490 #define MICROLABEL_EMPTY 0
3491 #define MICROLABEL_LEVEL_NAME 1
3492 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3493 #define MICROLABEL_LEVEL_AUTHOR 3
3494 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3495 #define MICROLABEL_IMPORTED_FROM 5
3496 #define MICROLABEL_IMPORTED_BY_HEAD 6
3497 #define MICROLABEL_IMPORTED_BY 7
3499 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3501 int max_text_width = SXSIZE;
3502 int font_width = getFontWidth(font_nr);
3504 if (pos->align == ALIGN_CENTER)
3505 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3506 else if (pos->align == ALIGN_RIGHT)
3507 max_text_width = pos->x;
3509 max_text_width = SXSIZE - pos->x;
3511 return max_text_width / font_width;
3514 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3516 char label_text[MAX_OUTPUT_LINESIZE + 1];
3517 int max_len_label_text;
3518 int font_nr = pos->font;
3521 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3524 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3525 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3526 mode == MICROLABEL_IMPORTED_BY_HEAD)
3527 font_nr = pos->font_alt;
3529 max_len_label_text = getMaxTextLength(pos, font_nr);
3531 if (pos->size != -1)
3532 max_len_label_text = pos->size;
3534 for (i = 0; i < max_len_label_text; i++)
3535 label_text[i] = ' ';
3536 label_text[max_len_label_text] = '\0';
3538 if (strlen(label_text) > 0)
3539 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3542 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3543 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3544 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3545 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3546 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3547 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3548 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3549 max_len_label_text);
3550 label_text[max_len_label_text] = '\0';
3552 if (strlen(label_text) > 0)
3553 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3555 redraw_mask |= REDRAW_FIELD;
3558 static void DrawPreviewLevelLabel(int mode)
3560 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3563 static void DrawPreviewLevelInfo(int mode)
3565 if (mode == MICROLABEL_LEVEL_NAME)
3566 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3567 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3568 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3571 static void DrawPreviewLevelExt(boolean restart)
3573 static DelayCounter scroll_delay = { 0 };
3574 static DelayCounter label_delay = { 0 };
3575 static int from_x, from_y, scroll_direction;
3576 static int label_state, label_counter;
3577 boolean show_level_border = (BorderElement != EL_EMPTY);
3578 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3579 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3581 scroll_delay.value = preview.step_delay;
3582 label_delay.value = MICROLEVEL_LABEL_DELAY;
3589 if (preview.anim_mode == ANIM_CENTERED)
3591 if (level_xsize > preview.xsize)
3592 from_x = (level_xsize - preview.xsize) / 2;
3593 if (level_ysize > preview.ysize)
3594 from_y = (level_ysize - preview.ysize) / 2;
3597 from_x += preview.xoffset;
3598 from_y += preview.yoffset;
3600 scroll_direction = MV_RIGHT;
3604 DrawPreviewLevelPlayfield(from_x, from_y);
3605 DrawPreviewLevelLabel(label_state);
3607 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3608 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3610 // initialize delay counters
3611 ResetDelayCounter(&scroll_delay);
3612 ResetDelayCounter(&label_delay);
3614 if (leveldir_current->name)
3616 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3617 char label_text[MAX_OUTPUT_LINESIZE + 1];
3618 int font_nr = pos->font;
3619 int max_len_label_text = getMaxTextLength(pos, font_nr);
3621 if (pos->size != -1)
3622 max_len_label_text = pos->size;
3624 strncpy(label_text, leveldir_current->name, max_len_label_text);
3625 label_text[max_len_label_text] = '\0';
3627 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3628 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3634 // scroll preview level, if needed
3635 if (preview.anim_mode != ANIM_NONE &&
3636 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3637 DelayReached(&scroll_delay))
3639 switch (scroll_direction)
3644 from_x -= preview.step_offset;
3645 from_x = (from_x < 0 ? 0 : from_x);
3648 scroll_direction = MV_UP;
3652 if (from_x < level_xsize - preview.xsize)
3654 from_x += preview.step_offset;
3655 from_x = (from_x > level_xsize - preview.xsize ?
3656 level_xsize - preview.xsize : from_x);
3659 scroll_direction = MV_DOWN;
3665 from_y -= preview.step_offset;
3666 from_y = (from_y < 0 ? 0 : from_y);
3669 scroll_direction = MV_RIGHT;
3673 if (from_y < level_ysize - preview.ysize)
3675 from_y += preview.step_offset;
3676 from_y = (from_y > level_ysize - preview.ysize ?
3677 level_ysize - preview.ysize : from_y);
3680 scroll_direction = MV_LEFT;
3687 DrawPreviewLevelPlayfield(from_x, from_y);
3690 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3691 // redraw micro level label, if needed
3692 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3693 !strEqual(level.author, ANONYMOUS_NAME) &&
3694 !strEqual(level.author, leveldir_current->name) &&
3695 DelayReached(&label_delay))
3697 int max_label_counter = 23;
3699 if (leveldir_current->imported_from != NULL &&
3700 strlen(leveldir_current->imported_from) > 0)
3701 max_label_counter += 14;
3702 if (leveldir_current->imported_by != NULL &&
3703 strlen(leveldir_current->imported_by) > 0)
3704 max_label_counter += 14;
3706 label_counter = (label_counter + 1) % max_label_counter;
3707 label_state = (label_counter >= 0 && label_counter <= 7 ?
3708 MICROLABEL_LEVEL_NAME :
3709 label_counter >= 9 && label_counter <= 12 ?
3710 MICROLABEL_LEVEL_AUTHOR_HEAD :
3711 label_counter >= 14 && label_counter <= 21 ?
3712 MICROLABEL_LEVEL_AUTHOR :
3713 label_counter >= 23 && label_counter <= 26 ?
3714 MICROLABEL_IMPORTED_FROM_HEAD :
3715 label_counter >= 28 && label_counter <= 35 ?
3716 MICROLABEL_IMPORTED_FROM :
3717 label_counter >= 37 && label_counter <= 40 ?
3718 MICROLABEL_IMPORTED_BY_HEAD :
3719 label_counter >= 42 && label_counter <= 49 ?
3720 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3722 if (leveldir_current->imported_from == NULL &&
3723 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3724 label_state == MICROLABEL_IMPORTED_FROM))
3725 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3726 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3728 DrawPreviewLevelLabel(label_state);
3732 void DrawPreviewPlayers(void)
3734 if (game_status != GAME_MODE_MAIN)
3737 // do not draw preview players if level preview redefined, but players aren't
3738 if (preview.redefined && !menu.main.preview_players.redefined)
3741 boolean player_found[MAX_PLAYERS];
3742 int num_players = 0;
3745 for (i = 0; i < MAX_PLAYERS; i++)
3746 player_found[i] = FALSE;
3748 // check which players can be found in the level (simple approach)
3749 for (x = 0; x < lev_fieldx; x++)
3751 for (y = 0; y < lev_fieldy; y++)
3753 int element = level.field[x][y];
3755 if (IS_PLAYER_ELEMENT(element))
3757 int player_nr = GET_PLAYER_NR(element);
3759 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3761 if (!player_found[player_nr])
3764 player_found[player_nr] = TRUE;
3769 struct TextPosInfo *pos = &menu.main.preview_players;
3770 int tile_size = pos->tile_size;
3771 int border_size = pos->border_size;
3772 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3773 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3774 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3775 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3776 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3777 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3778 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3779 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3780 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3781 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3782 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3783 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3785 // clear area in which the players will be drawn
3786 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3787 max_players_width, max_players_height);
3789 if (!network.enabled && !setup.team_mode)
3792 // only draw players if level is suited for team mode
3793 if (num_players < 2)
3796 // draw all players that were found in the level
3797 for (i = 0; i < MAX_PLAYERS; i++)
3799 if (player_found[i])
3801 int graphic = el2img(EL_PLAYER_1 + i);
3803 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3805 xpos += player_xoffset;
3806 ypos += player_yoffset;
3811 void DrawPreviewLevelInitial(void)
3813 DrawPreviewLevelExt(TRUE);
3814 DrawPreviewPlayers();
3817 void DrawPreviewLevelAnimation(void)
3819 DrawPreviewLevelExt(FALSE);
3822 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3823 int border_size, int font_nr)
3825 int graphic = el2img(EL_PLAYER_1 + player_nr);
3826 int font_height = getFontHeight(font_nr);
3827 int player_height = MAX(tile_size, font_height);
3828 int xoffset_text = tile_size + border_size;
3829 int yoffset_text = (player_height - font_height) / 2;
3830 int yoffset_graphic = (player_height - tile_size) / 2;
3831 char *player_name = getNetworkPlayerName(player_nr + 1);
3833 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3835 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3838 static void DrawNetworkPlayersExt(boolean force)
3840 if (game_status != GAME_MODE_MAIN)
3843 if (!network.connected && !force)
3846 // do not draw network players if level preview redefined, but players aren't
3847 if (preview.redefined && !menu.main.network_players.redefined)
3850 int num_players = 0;
3853 for (i = 0; i < MAX_PLAYERS; i++)
3854 if (stored_player[i].connected_network)
3857 struct TextPosInfo *pos = &menu.main.network_players;
3858 int tile_size = pos->tile_size;
3859 int border_size = pos->border_size;
3860 int xoffset_text = tile_size + border_size;
3861 int font_nr = pos->font;
3862 int font_width = getFontWidth(font_nr);
3863 int font_height = getFontHeight(font_nr);
3864 int player_height = MAX(tile_size, font_height);
3865 int player_yoffset = player_height + border_size;
3866 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3867 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3868 int all_players_height = num_players * player_yoffset - border_size;
3869 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3870 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3871 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3873 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3874 max_players_width, max_players_height);
3876 // first draw local network player ...
3877 for (i = 0; i < MAX_PLAYERS; i++)
3879 if (stored_player[i].connected_network &&
3880 stored_player[i].connected_locally)
3882 char *player_name = getNetworkPlayerName(i + 1);
3883 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3884 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3886 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3888 ypos += player_yoffset;
3892 // ... then draw all other network players
3893 for (i = 0; i < MAX_PLAYERS; i++)
3895 if (stored_player[i].connected_network &&
3896 !stored_player[i].connected_locally)
3898 char *player_name = getNetworkPlayerName(i + 1);
3899 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3900 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3902 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3904 ypos += player_yoffset;
3909 void DrawNetworkPlayers(void)
3911 DrawNetworkPlayersExt(FALSE);
3914 void ClearNetworkPlayers(void)
3916 DrawNetworkPlayersExt(TRUE);
3919 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3920 int graphic, int lx, int ly,
3923 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3925 if (mask_mode == USE_MASKING)
3926 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3928 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3931 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3932 int graphic, int sync_frame, int mask_mode)
3934 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3936 if (mask_mode == USE_MASKING)
3937 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3939 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3942 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3943 int graphic, int sync_frame, int tilesize,
3946 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3948 if (mask_mode == USE_MASKING)
3949 DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
3951 DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
3954 static void DrawGraphicAnimation(int x, int y, int graphic)
3956 int lx = LEVELX(x), ly = LEVELY(y);
3957 int mask_mode = NO_MASKING;
3959 if (!IN_SCR_FIELD(x, y))
3962 if (game.use_masked_elements)
3964 if (Tile[lx][ly] != EL_EMPTY)
3966 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3968 mask_mode = USE_MASKING;
3972 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3973 graphic, lx, ly, mask_mode);
3975 MarkTileDirty(x, y);
3978 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3980 int lx = LEVELX(x), ly = LEVELY(y);
3981 int mask_mode = NO_MASKING;
3983 if (!IN_SCR_FIELD(x, y))
3986 if (game.use_masked_elements)
3988 if (Tile[lx][ly] != EL_EMPTY)
3990 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3992 mask_mode = USE_MASKING;
3996 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3997 graphic, lx, ly, mask_mode);
3999 MarkTileDirty(x, y);
4002 void DrawLevelGraphicAnimation(int x, int y, int graphic)
4004 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4007 void DrawLevelElementAnimation(int x, int y, int element)
4009 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4011 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4014 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
4016 int sx = SCREENX(x), sy = SCREENY(y);
4018 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4021 if (Tile[x][y] == EL_EMPTY)
4022 graphic = el2img(GfxElementEmpty[x][y]);
4024 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4027 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4030 DrawGraphicAnimation(sx, sy, graphic);
4033 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4034 DrawLevelFieldCrumbled(x, y);
4036 if (GFX_CRUMBLED(Tile[x][y]))
4037 DrawLevelFieldCrumbled(x, y);
4041 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4043 int sx = SCREENX(x), sy = SCREENY(y);
4046 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4049 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4051 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4054 DrawGraphicAnimation(sx, sy, graphic);
4056 if (GFX_CRUMBLED(element))
4057 DrawLevelFieldCrumbled(x, y);
4060 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4062 if (player->use_murphy)
4064 // this works only because currently only one player can be "murphy" ...
4065 static int last_horizontal_dir = MV_LEFT;
4066 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4068 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4069 last_horizontal_dir = move_dir;
4071 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4073 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4075 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4081 return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4084 static boolean equalGraphics(int graphic1, int graphic2)
4086 struct GraphicInfo *g1 = &graphic_info[graphic1];
4087 struct GraphicInfo *g2 = &graphic_info[graphic2];
4089 return (g1->bitmap == g2->bitmap &&
4090 g1->src_x == g2->src_x &&
4091 g1->src_y == g2->src_y &&
4092 g1->anim_frames == g2->anim_frames &&
4093 g1->anim_delay == g2->anim_delay &&
4094 g1->anim_mode == g2->anim_mode);
4097 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4101 DRAW_PLAYER_STAGE_INIT = 0,
4102 DRAW_PLAYER_STAGE_LAST_FIELD,
4103 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4104 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4105 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4106 DRAW_PLAYER_STAGE_PLAYER,
4108 DRAW_PLAYER_STAGE_PLAYER,
4109 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4111 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4112 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4114 NUM_DRAW_PLAYER_STAGES
4117 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4119 static int static_last_player_graphic[MAX_PLAYERS];
4120 static int static_last_player_frame[MAX_PLAYERS];
4121 static boolean static_player_is_opaque[MAX_PLAYERS];
4122 static boolean draw_player[MAX_PLAYERS];
4123 int pnr = player->index_nr;
4125 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4127 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4128 static_last_player_frame[pnr] = player->Frame;
4129 static_player_is_opaque[pnr] = FALSE;
4131 draw_player[pnr] = TRUE;
4134 if (!draw_player[pnr])
4138 if (!IN_LEV_FIELD(player->jx, player->jy))
4140 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4141 Debug("draw:DrawPlayerExt", "This should never happen!");
4143 draw_player[pnr] = FALSE;
4149 int last_player_graphic = static_last_player_graphic[pnr];
4150 int last_player_frame = static_last_player_frame[pnr];
4151 boolean player_is_opaque = static_player_is_opaque[pnr];
4153 int jx = player->jx;
4154 int jy = player->jy;
4155 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4156 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4157 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4158 int last_jx = (player->is_moving ? jx - dx : jx);
4159 int last_jy = (player->is_moving ? jy - dy : jy);
4160 int next_jx = jx + dx;
4161 int next_jy = jy + dy;
4162 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4163 int sx = SCREENX(jx);
4164 int sy = SCREENY(jy);
4165 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4166 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4167 int element = Tile[jx][jy];
4168 int last_element = Tile[last_jx][last_jy];
4169 int action = (player->is_pushing ? ACTION_PUSHING :
4170 player->is_digging ? ACTION_DIGGING :
4171 player->is_collecting ? ACTION_COLLECTING :
4172 player->is_moving ? ACTION_MOVING :
4173 player->is_snapping ? ACTION_SNAPPING :
4174 player->is_dropping ? ACTION_DROPPING :
4175 player->is_waiting ? player->action_waiting :
4178 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4180 // ------------------------------------------------------------------------
4181 // initialize drawing the player
4182 // ------------------------------------------------------------------------
4184 draw_player[pnr] = FALSE;
4186 // GfxElement[][] is set to the element the player is digging or collecting;
4187 // remove also for off-screen player if the player is not moving anymore
4188 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4189 GfxElement[jx][jy] = EL_UNDEFINED;
4191 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4194 if (element == EL_EXPLOSION)
4197 InitPlayerGfxAnimation(player, action, move_dir);
4199 draw_player[pnr] = TRUE;
4201 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4203 // ------------------------------------------------------------------------
4204 // draw things in the field the player is leaving, if needed
4205 // ------------------------------------------------------------------------
4207 if (!IN_SCR_FIELD(sx, sy))
4208 draw_player[pnr] = FALSE;
4210 if (!player->is_moving)
4213 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4215 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4217 if (last_element == EL_DYNAMITE_ACTIVE ||
4218 last_element == EL_EM_DYNAMITE_ACTIVE ||
4219 last_element == EL_SP_DISK_RED_ACTIVE)
4220 DrawDynamite(last_jx, last_jy);
4222 DrawLevelFieldThruMask(last_jx, last_jy);
4224 else if (last_element == EL_DYNAMITE_ACTIVE ||
4225 last_element == EL_EM_DYNAMITE_ACTIVE ||
4226 last_element == EL_SP_DISK_RED_ACTIVE)
4227 DrawDynamite(last_jx, last_jy);
4229 DrawLevelField(last_jx, last_jy);
4231 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4233 // ------------------------------------------------------------------------
4234 // draw things behind the player, if needed
4235 // ------------------------------------------------------------------------
4239 DrawLevelElement(jx, jy, Back[jx][jy]);
4244 if (IS_ACTIVE_BOMB(element))
4246 DrawLevelElement(jx, jy, EL_EMPTY);
4251 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4253 int old_element = GfxElement[jx][jy];
4254 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4255 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4257 if (GFX_CRUMBLED(old_element))
4258 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4260 DrawScreenGraphic(sx, sy, old_graphic, frame);
4262 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4263 static_player_is_opaque[pnr] = TRUE;
4267 GfxElement[jx][jy] = EL_UNDEFINED;
4269 // make sure that pushed elements are drawn with correct frame rate
4270 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4272 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4273 GfxFrame[jx][jy] = player->StepFrame;
4275 DrawLevelField(jx, jy);
4278 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4280 // ------------------------------------------------------------------------
4281 // draw things the player is pushing, if needed
4282 // ------------------------------------------------------------------------
4284 if (!player->is_pushing || !player->is_moving)
4287 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4290 int gfx_frame = GfxFrame[jx][jy];
4292 if (!IS_MOVING(jx, jy)) // push movement already finished
4294 element = Tile[next_jx][next_jy];
4295 gfx_frame = GfxFrame[next_jx][next_jy];
4298 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4299 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4300 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4302 // draw background element under pushed element (like the Sokoban field)
4303 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4305 // this allows transparent pushing animation over non-black background
4308 DrawLevelElement(jx, jy, Back[jx][jy]);
4310 DrawLevelElement(jx, jy, EL_EMPTY);
4313 if (Back[next_jx][next_jy])
4314 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4316 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4318 int px = SCREENX(jx), py = SCREENY(jy);
4319 int pxx = (TILEX - ABS(sxx)) * dx;
4320 int pyy = (TILEY - ABS(syy)) * dy;
4323 // do not draw (EM style) pushing animation when pushing is finished
4324 // (two-tile animations usually do not contain start and end frame)
4325 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4326 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4328 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4330 // masked drawing is needed for EMC style (double) movement graphics
4331 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4332 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4335 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4337 // ------------------------------------------------------------------------
4338 // draw player himself
4339 // ------------------------------------------------------------------------
4341 int graphic = getPlayerGraphic(player, move_dir);
4343 // in the case of changed player action or direction, prevent the current
4344 // animation frame from being restarted for identical animations
4345 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4346 player->Frame = last_player_frame;
4348 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4350 if (player_is_opaque)
4351 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4353 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4355 if (SHIELD_ON(player))
4357 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4358 IMG_SHIELD_NORMAL_ACTIVE);
4359 frame = getGraphicAnimationFrame(graphic, -1);
4361 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4364 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4366 // ------------------------------------------------------------------------
4367 // draw things in front of player (active dynamite or dynabombs)
4368 // ------------------------------------------------------------------------
4370 if (IS_ACTIVE_BOMB(element))
4372 int graphic = el2img(element);
4373 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4375 if (game.emulation == EMU_SUPAPLEX)
4376 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4378 DrawGraphicThruMask(sx, sy, graphic, frame);
4381 if (player_is_moving && last_element == EL_EXPLOSION)
4383 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4384 GfxElement[last_jx][last_jy] : EL_EMPTY);
4385 int graphic = el_act2img(element, ACTION_EXPLODING);
4386 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4387 int phase = ExplodePhase[last_jx][last_jy] - 1;
4388 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4391 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4394 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4396 // ------------------------------------------------------------------------
4397 // draw elements the player is just walking/passing through/under
4398 // ------------------------------------------------------------------------
4400 if (player_is_moving)
4402 // handle the field the player is leaving ...
4403 if (IS_ACCESSIBLE_INSIDE(last_element))
4404 DrawLevelField(last_jx, last_jy);
4405 else if (IS_ACCESSIBLE_UNDER(last_element))
4406 DrawLevelFieldThruMask(last_jx, last_jy);
4409 // do not redraw accessible elements if the player is just pushing them
4410 if (!player_is_moving || !player->is_pushing)
4412 // ... and the field the player is entering
4413 if (IS_ACCESSIBLE_INSIDE(element))
4414 DrawLevelField(jx, jy);
4415 else if (IS_ACCESSIBLE_UNDER(element))
4416 DrawLevelFieldThruMask(jx, jy);
4419 MarkTileDirty(sx, sy);
4423 void DrawPlayer(struct PlayerInfo *player)
4427 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4428 DrawPlayerExt(player, i);
4431 void DrawAllPlayers(void)
4435 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4436 for (j = 0; j < MAX_PLAYERS; j++)
4437 if (stored_player[j].active)
4438 DrawPlayerExt(&stored_player[j], i);
4441 void DrawPlayerField(int x, int y)
4443 if (!IS_PLAYER(x, y))
4446 DrawPlayer(PLAYERINFO(x, y));
4449 // ----------------------------------------------------------------------------
4451 void WaitForEventToContinue(void)
4453 boolean first_wait = TRUE;
4454 boolean still_wait = TRUE;
4456 if (program.headless)
4459 // simulate releasing mouse button over last gadget, if still pressed
4461 HandleGadgets(-1, -1, 0);
4463 button_status = MB_RELEASED;
4466 ClearPlayerAction();
4472 if (NextValidEvent(&event))
4476 case EVENT_BUTTONPRESS:
4477 case EVENT_FINGERPRESS:
4481 case EVENT_BUTTONRELEASE:
4482 case EVENT_FINGERRELEASE:
4483 still_wait = first_wait;
4486 case EVENT_KEYPRESS:
4487 case SDL_CONTROLLERBUTTONDOWN:
4488 case SDL_JOYBUTTONDOWN:
4493 HandleOtherEvents(&event);
4497 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4502 if (!PendingEvent())
4507 #define MAX_REQUEST_LINES 13
4508 #define MAX_REQUEST_LINE_FONT1_LEN 7
4509 #define MAX_REQUEST_LINE_FONT2_LEN 10
4511 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4513 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4515 int draw_buffer_last = GetDrawtoField();
4516 int width = request.width;
4517 int height = request.height;
4521 // when showing request dialog after game ended, deactivate game panel
4522 if (game_just_ended)
4523 game.panel.active = FALSE;
4525 game.request_active = TRUE;
4527 setRequestPosition(&sx, &sy, FALSE);
4529 button_status = MB_RELEASED;
4531 request_gadget_id = -1;
4536 boolean event_handled = FALSE;
4538 if (game_just_ended)
4540 SetDrawtoField(draw_buffer_game);
4542 HandleGameActions();
4544 SetDrawtoField(DRAW_TO_BACKBUFFER);
4546 if (global.use_envelope_request)
4548 // copy current state of request area to middle of playfield area
4549 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4557 while (NextValidEvent(&event))
4559 event_handled = TRUE;
4563 case EVENT_BUTTONPRESS:
4564 case EVENT_BUTTONRELEASE:
4565 case EVENT_MOTIONNOTIFY:
4569 if (event.type == EVENT_MOTIONNOTIFY)
4574 motion_status = TRUE;
4575 mx = ((MotionEvent *) &event)->x;
4576 my = ((MotionEvent *) &event)->y;
4580 motion_status = FALSE;
4581 mx = ((ButtonEvent *) &event)->x;
4582 my = ((ButtonEvent *) &event)->y;
4583 if (event.type == EVENT_BUTTONPRESS)
4584 button_status = ((ButtonEvent *) &event)->button;
4586 button_status = MB_RELEASED;
4589 // this sets 'request_gadget_id'
4590 HandleGadgets(mx, my, button_status);
4592 switch (request_gadget_id)
4594 case TOOL_CTRL_ID_YES:
4595 case TOOL_CTRL_ID_TOUCH_YES:
4598 case TOOL_CTRL_ID_NO:
4599 case TOOL_CTRL_ID_TOUCH_NO:
4602 case TOOL_CTRL_ID_CONFIRM:
4603 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4604 result = TRUE | FALSE;
4607 case TOOL_CTRL_ID_PLAYER_1:
4610 case TOOL_CTRL_ID_PLAYER_2:
4613 case TOOL_CTRL_ID_PLAYER_3:
4616 case TOOL_CTRL_ID_PLAYER_4:
4621 // only check clickable animations if no request gadget clicked
4622 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4629 case SDL_WINDOWEVENT:
4630 HandleWindowEvent((WindowEvent *) &event);
4633 case SDL_APP_WILLENTERBACKGROUND:
4634 case SDL_APP_DIDENTERBACKGROUND:
4635 case SDL_APP_WILLENTERFOREGROUND:
4636 case SDL_APP_DIDENTERFOREGROUND:
4637 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4640 case EVENT_KEYPRESS:
4642 Key key = GetEventKey((KeyEvent *)&event);
4647 if (req_state & REQ_CONFIRM)
4656 #if defined(KSYM_Rewind)
4657 case KSYM_Rewind: // for Amazon Fire TV remote
4666 #if defined(KSYM_FastForward)
4667 case KSYM_FastForward: // for Amazon Fire TV remote
4673 HandleKeysDebug(key, KEY_PRESSED);
4677 if (req_state & REQ_PLAYER)
4679 int old_player_nr = setup.network_player_nr;
4682 result = old_player_nr + 1;
4687 result = old_player_nr + 1;
4718 case EVENT_FINGERRELEASE:
4719 case EVENT_KEYRELEASE:
4720 ClearPlayerAction();
4723 case SDL_CONTROLLERBUTTONDOWN:
4724 switch (event.cbutton.button)
4726 case SDL_CONTROLLER_BUTTON_A:
4727 case SDL_CONTROLLER_BUTTON_X:
4728 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4729 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4733 case SDL_CONTROLLER_BUTTON_B:
4734 case SDL_CONTROLLER_BUTTON_Y:
4735 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4736 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4737 case SDL_CONTROLLER_BUTTON_BACK:
4742 if (req_state & REQ_PLAYER)
4744 int old_player_nr = setup.network_player_nr;
4747 result = old_player_nr + 1;
4749 switch (event.cbutton.button)
4751 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4752 case SDL_CONTROLLER_BUTTON_Y:
4756 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4757 case SDL_CONTROLLER_BUTTON_B:
4761 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4762 case SDL_CONTROLLER_BUTTON_A:
4766 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4767 case SDL_CONTROLLER_BUTTON_X:
4778 case SDL_CONTROLLERBUTTONUP:
4779 HandleJoystickEvent(&event);
4780 ClearPlayerAction();
4784 HandleOtherEvents(&event);
4789 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4791 int joy = AnyJoystick();
4793 if (joy & JOY_BUTTON_1)
4795 else if (joy & JOY_BUTTON_2)
4798 else if (AnyJoystick())
4800 int joy = AnyJoystick();
4802 if (req_state & REQ_PLAYER)
4806 else if (joy & JOY_RIGHT)
4808 else if (joy & JOY_DOWN)
4810 else if (joy & JOY_LEFT)
4817 if (game_just_ended)
4819 if (global.use_envelope_request)
4821 // copy back current state of pressed buttons inside request area
4822 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4826 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4832 SetDrawtoField(draw_buffer_last);
4834 game.request_active = FALSE;
4839 static boolean RequestDoor(char *text, unsigned int req_state)
4841 int draw_buffer_last = GetDrawtoField();
4842 unsigned int old_door_state;
4843 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4844 int font_nr = FONT_TEXT_2;
4849 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4851 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4852 font_nr = FONT_TEXT_1;
4855 if (game_status == GAME_MODE_PLAYING)
4856 BlitScreenToBitmap(backbuffer);
4858 // disable deactivated drawing when quick-loading level tape recording
4859 if (tape.playing && tape.deactivate_display)
4860 TapeDeactivateDisplayOff(TRUE);
4862 SetMouseCursor(CURSOR_DEFAULT);
4864 // pause network game while waiting for request to answer
4865 if (network.enabled &&
4866 game_status == GAME_MODE_PLAYING &&
4867 !game.all_players_gone &&
4868 req_state & REQUEST_WAIT_FOR_INPUT)
4869 SendToServer_PausePlaying();
4871 old_door_state = GetDoorState();
4873 // simulate releasing mouse button over last gadget, if still pressed
4875 HandleGadgets(-1, -1, 0);
4879 // draw released gadget before proceeding
4882 if (old_door_state & DOOR_OPEN_1)
4884 CloseDoor(DOOR_CLOSE_1);
4886 // save old door content
4887 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4888 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4891 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4892 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4894 // clear door drawing field
4895 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4897 // force DOOR font inside door area
4898 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4900 // write text for request
4901 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4903 char text_line[max_request_line_len + 1];
4909 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4911 tc = *(text_ptr + tx);
4912 // if (!tc || tc == ' ')
4913 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4917 if ((tc == '?' || tc == '!') && tl == 0)
4927 strncpy(text_line, text_ptr, tl);
4930 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4931 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4932 text_line, font_nr);
4934 text_ptr += tl + (tc == ' ' ? 1 : 0);
4935 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4940 if (req_state & REQ_ASK)
4942 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4943 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4944 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4945 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4947 else if (req_state & REQ_CONFIRM)
4949 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4950 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4952 else if (req_state & REQ_PLAYER)
4954 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4955 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4956 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4957 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4960 // copy request gadgets to door backbuffer
4961 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4963 OpenDoor(DOOR_OPEN_1);
4965 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4967 if (game_status == GAME_MODE_PLAYING)
4969 SetPanelBackground();
4970 SetDrawBackgroundMask(REDRAW_DOOR_1);
4974 SetDrawBackgroundMask(REDRAW_FIELD);
4980 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4982 // ---------- handle request buttons ----------
4983 result = RequestHandleEvents(req_state, draw_buffer_last);
4987 if (!(req_state & REQ_STAY_OPEN))
4989 CloseDoor(DOOR_CLOSE_1);
4991 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4992 (req_state & REQ_REOPEN))
4993 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4998 if (game_status == GAME_MODE_PLAYING)
5000 SetPanelBackground();
5001 SetDrawBackgroundMask(REDRAW_DOOR_1);
5005 SetDrawBackgroundMask(REDRAW_FIELD);
5008 // continue network game after request
5009 if (network.enabled &&
5010 game_status == GAME_MODE_PLAYING &&
5011 !game.all_players_gone &&
5012 req_state & REQUEST_WAIT_FOR_INPUT)
5013 SendToServer_ContinuePlaying();
5015 // restore deactivated drawing when quick-loading level tape recording
5016 if (tape.playing && tape.deactivate_display)
5017 TapeDeactivateDisplayOn();
5022 static boolean RequestEnvelope(char *text, unsigned int req_state)
5024 int draw_buffer_last = GetDrawtoField();
5027 if (game_status == GAME_MODE_PLAYING)
5028 BlitScreenToBitmap(backbuffer);
5030 // disable deactivated drawing when quick-loading level tape recording
5031 if (tape.playing && tape.deactivate_display)
5032 TapeDeactivateDisplayOff(TRUE);
5034 SetMouseCursor(CURSOR_DEFAULT);
5036 // pause network game while waiting for request to answer
5037 if (network.enabled &&
5038 game_status == GAME_MODE_PLAYING &&
5039 !game.all_players_gone &&
5040 req_state & REQUEST_WAIT_FOR_INPUT)
5041 SendToServer_PausePlaying();
5043 // simulate releasing mouse button over last gadget, if still pressed
5045 HandleGadgets(-1, -1, 0);
5049 // (replace with setting corresponding request background)
5050 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5051 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5053 // clear door drawing field
5054 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5056 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5058 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5060 if (game_status == GAME_MODE_PLAYING)
5062 SetPanelBackground();
5063 SetDrawBackgroundMask(REDRAW_DOOR_1);
5067 SetDrawBackgroundMask(REDRAW_FIELD);
5073 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5075 // ---------- handle request buttons ----------
5076 result = RequestHandleEvents(req_state, draw_buffer_last);
5080 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5084 if (game_status == GAME_MODE_PLAYING)
5086 SetPanelBackground();
5087 SetDrawBackgroundMask(REDRAW_DOOR_1);
5091 SetDrawBackgroundMask(REDRAW_FIELD);
5094 // continue network game after request
5095 if (network.enabled &&
5096 game_status == GAME_MODE_PLAYING &&
5097 !game.all_players_gone &&
5098 req_state & REQUEST_WAIT_FOR_INPUT)
5099 SendToServer_ContinuePlaying();
5101 // restore deactivated drawing when quick-loading level tape recording
5102 if (tape.playing && tape.deactivate_display)
5103 TapeDeactivateDisplayOn();
5108 boolean Request(char *text, unsigned int req_state)
5110 boolean overlay_enabled = GetOverlayEnabled();
5113 game.request_active_or_moving = TRUE;
5115 SetOverlayEnabled(FALSE);
5117 if (global.use_envelope_request)
5118 result = RequestEnvelope(text, req_state);
5120 result = RequestDoor(text, req_state);
5122 SetOverlayEnabled(overlay_enabled);
5124 game.request_active_or_moving = FALSE;
5129 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5131 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5132 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5135 if (dpo1->sort_priority != dpo2->sort_priority)
5136 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5138 compare_result = dpo1->nr - dpo2->nr;
5140 return compare_result;
5143 void InitGraphicCompatibilityInfo_Doors(void)
5149 struct DoorInfo *door;
5153 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5154 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5156 { -1, -1, -1, NULL }
5158 struct Rect door_rect_list[] =
5160 { DX, DY, DXSIZE, DYSIZE },
5161 { VX, VY, VXSIZE, VYSIZE }
5165 for (i = 0; doors[i].door_token != -1; i++)
5167 int door_token = doors[i].door_token;
5168 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5169 int part_1 = doors[i].part_1;
5170 int part_8 = doors[i].part_8;
5171 int part_2 = part_1 + 1;
5172 int part_3 = part_1 + 2;
5173 struct DoorInfo *door = doors[i].door;
5174 struct Rect *door_rect = &door_rect_list[door_index];
5175 boolean door_gfx_redefined = FALSE;
5177 // check if any door part graphic definitions have been redefined
5179 for (j = 0; door_part_controls[j].door_token != -1; j++)
5181 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5182 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5184 if (dpc->door_token == door_token && fi->redefined)
5185 door_gfx_redefined = TRUE;
5188 // check for old-style door graphic/animation modifications
5190 if (!door_gfx_redefined)
5192 if (door->anim_mode & ANIM_STATIC_PANEL)
5194 door->panel.step_xoffset = 0;
5195 door->panel.step_yoffset = 0;
5198 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5200 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5201 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5202 int num_door_steps, num_panel_steps;
5204 // remove door part graphics other than the two default wings
5206 for (j = 0; door_part_controls[j].door_token != -1; j++)
5208 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5209 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5211 if (dpc->graphic >= part_3 &&
5212 dpc->graphic <= part_8)
5216 // set graphics and screen positions of the default wings
5218 g_part_1->width = door_rect->width;
5219 g_part_1->height = door_rect->height;
5220 g_part_2->width = door_rect->width;
5221 g_part_2->height = door_rect->height;
5222 g_part_2->src_x = door_rect->width;
5223 g_part_2->src_y = g_part_1->src_y;
5225 door->part_2.x = door->part_1.x;
5226 door->part_2.y = door->part_1.y;
5228 if (door->width != -1)
5230 g_part_1->width = door->width;
5231 g_part_2->width = door->width;
5233 // special treatment for graphics and screen position of right wing
5234 g_part_2->src_x += door_rect->width - door->width;
5235 door->part_2.x += door_rect->width - door->width;
5238 if (door->height != -1)
5240 g_part_1->height = door->height;
5241 g_part_2->height = door->height;
5243 // special treatment for graphics and screen position of bottom wing
5244 g_part_2->src_y += door_rect->height - door->height;
5245 door->part_2.y += door_rect->height - door->height;
5248 // set animation delays for the default wings and panels
5250 door->part_1.step_delay = door->step_delay;
5251 door->part_2.step_delay = door->step_delay;
5252 door->panel.step_delay = door->step_delay;
5254 // set animation draw order for the default wings
5256 door->part_1.sort_priority = 2; // draw left wing over ...
5257 door->part_2.sort_priority = 1; // ... right wing
5259 // set animation draw offset for the default wings
5261 if (door->anim_mode & ANIM_HORIZONTAL)
5263 door->part_1.step_xoffset = door->step_offset;
5264 door->part_1.step_yoffset = 0;
5265 door->part_2.step_xoffset = door->step_offset * -1;
5266 door->part_2.step_yoffset = 0;
5268 num_door_steps = g_part_1->width / door->step_offset;
5270 else // ANIM_VERTICAL
5272 door->part_1.step_xoffset = 0;
5273 door->part_1.step_yoffset = door->step_offset;
5274 door->part_2.step_xoffset = 0;
5275 door->part_2.step_yoffset = door->step_offset * -1;
5277 num_door_steps = g_part_1->height / door->step_offset;
5280 // set animation draw offset for the default panels
5282 if (door->step_offset > 1)
5284 num_panel_steps = 2 * door_rect->height / door->step_offset;
5285 door->panel.start_step = num_panel_steps - num_door_steps;
5286 door->panel.start_step_closing = door->panel.start_step;
5290 num_panel_steps = door_rect->height / door->step_offset;
5291 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5292 door->panel.start_step_closing = door->panel.start_step;
5293 door->panel.step_delay *= 2;
5300 void InitDoors(void)
5304 for (i = 0; door_part_controls[i].door_token != -1; i++)
5306 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5307 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5309 // initialize "start_step_opening" and "start_step_closing", if needed
5310 if (dpc->pos->start_step_opening == 0 &&
5311 dpc->pos->start_step_closing == 0)
5313 // dpc->pos->start_step_opening = dpc->pos->start_step;
5314 dpc->pos->start_step_closing = dpc->pos->start_step;
5317 // fill structure for door part draw order (sorted below)
5319 dpo->sort_priority = dpc->pos->sort_priority;
5322 // sort door part controls according to sort_priority and graphic number
5323 qsort(door_part_order, MAX_DOOR_PARTS,
5324 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5327 unsigned int OpenDoor(unsigned int door_state)
5329 if (door_state & DOOR_COPY_BACK)
5331 if (door_state & DOOR_OPEN_1)
5332 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5333 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5335 if (door_state & DOOR_OPEN_2)
5336 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5337 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5339 door_state &= ~DOOR_COPY_BACK;
5342 return MoveDoor(door_state);
5345 unsigned int CloseDoor(unsigned int door_state)
5347 unsigned int old_door_state = GetDoorState();
5349 if (!(door_state & DOOR_NO_COPY_BACK))
5351 if (old_door_state & DOOR_OPEN_1)
5352 BlitBitmap(backbuffer, bitmap_db_door_1,
5353 DX, DY, DXSIZE, DYSIZE, 0, 0);
5355 if (old_door_state & DOOR_OPEN_2)
5356 BlitBitmap(backbuffer, bitmap_db_door_2,
5357 VX, VY, VXSIZE, VYSIZE, 0, 0);
5359 door_state &= ~DOOR_NO_COPY_BACK;
5362 return MoveDoor(door_state);
5365 unsigned int GetDoorState(void)
5367 return MoveDoor(DOOR_GET_STATE);
5370 unsigned int SetDoorState(unsigned int door_state)
5372 return MoveDoor(door_state | DOOR_SET_STATE);
5375 static int euclid(int a, int b)
5377 return (b ? euclid(b, a % b) : a);
5380 unsigned int MoveDoor(unsigned int door_state)
5382 struct Rect door_rect_list[] =
5384 { DX, DY, DXSIZE, DYSIZE },
5385 { VX, VY, VXSIZE, VYSIZE }
5387 static int door1 = DOOR_CLOSE_1;
5388 static int door2 = DOOR_CLOSE_2;
5389 DelayCounter door_delay = { 0 };
5392 if (door_state == DOOR_GET_STATE)
5393 return (door1 | door2);
5395 if (door_state & DOOR_SET_STATE)
5397 if (door_state & DOOR_ACTION_1)
5398 door1 = door_state & DOOR_ACTION_1;
5399 if (door_state & DOOR_ACTION_2)
5400 door2 = door_state & DOOR_ACTION_2;
5402 return (door1 | door2);
5405 if (!(door_state & DOOR_FORCE_REDRAW))
5407 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5408 door_state &= ~DOOR_OPEN_1;
5409 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5410 door_state &= ~DOOR_CLOSE_1;
5411 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5412 door_state &= ~DOOR_OPEN_2;
5413 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5414 door_state &= ~DOOR_CLOSE_2;
5417 if (global.autoplay_leveldir)
5419 door_state |= DOOR_NO_DELAY;
5420 door_state &= ~DOOR_CLOSE_ALL;
5423 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5424 door_state |= DOOR_NO_DELAY;
5426 if (door_state & DOOR_ACTION)
5428 boolean door_panel_drawn[NUM_DOORS];
5429 boolean panel_has_doors[NUM_DOORS];
5430 boolean door_part_skip[MAX_DOOR_PARTS];
5431 boolean door_part_done[MAX_DOOR_PARTS];
5432 boolean door_part_done_all;
5433 int num_steps[MAX_DOOR_PARTS];
5434 int max_move_delay = 0; // delay for complete animations of all doors
5435 int max_step_delay = 0; // delay (ms) between two animation frames
5436 int num_move_steps = 0; // number of animation steps for all doors
5437 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5438 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5442 for (i = 0; i < NUM_DOORS; i++)
5443 panel_has_doors[i] = FALSE;
5445 for (i = 0; i < MAX_DOOR_PARTS; i++)
5447 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5448 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5449 int door_token = dpc->door_token;
5451 door_part_done[i] = FALSE;
5452 door_part_skip[i] = (!(door_state & door_token) ||
5456 for (i = 0; i < MAX_DOOR_PARTS; i++)
5458 int nr = door_part_order[i].nr;
5459 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5460 struct DoorPartPosInfo *pos = dpc->pos;
5461 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5462 int door_token = dpc->door_token;
5463 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5464 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5465 int step_xoffset = ABS(pos->step_xoffset);
5466 int step_yoffset = ABS(pos->step_yoffset);
5467 int step_delay = pos->step_delay;
5468 int current_door_state = door_state & door_token;
5469 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5470 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5471 boolean part_opening = (is_panel ? door_closing : door_opening);
5472 int start_step = (part_opening ? pos->start_step_opening :
5473 pos->start_step_closing);
5474 float move_xsize = (step_xoffset ? g->width : 0);
5475 float move_ysize = (step_yoffset ? g->height : 0);
5476 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5477 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5478 int move_steps = (move_xsteps && move_ysteps ?
5479 MIN(move_xsteps, move_ysteps) :
5480 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5481 int move_delay = move_steps * step_delay;
5483 if (door_part_skip[nr])
5486 max_move_delay = MAX(max_move_delay, move_delay);
5487 max_step_delay = (max_step_delay == 0 ? step_delay :
5488 euclid(max_step_delay, step_delay));
5489 num_steps[nr] = move_steps;
5493 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5495 panel_has_doors[door_index] = TRUE;
5499 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5501 num_move_steps = max_move_delay / max_step_delay;
5502 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5504 door_delay.value = max_step_delay;
5506 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5508 start = num_move_steps - 1;
5512 // opening door sound has priority over simultaneously closing door
5513 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5515 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5517 if (door_state & DOOR_OPEN_1)
5518 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5519 if (door_state & DOOR_OPEN_2)
5520 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5522 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5524 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5526 if (door_state & DOOR_CLOSE_1)
5527 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5528 if (door_state & DOOR_CLOSE_2)
5529 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5533 for (k = start; k < num_move_steps; k++)
5535 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5537 door_part_done_all = TRUE;
5539 for (i = 0; i < NUM_DOORS; i++)
5540 door_panel_drawn[i] = FALSE;
5542 for (i = 0; i < MAX_DOOR_PARTS; i++)
5544 int nr = door_part_order[i].nr;
5545 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5546 struct DoorPartPosInfo *pos = dpc->pos;
5547 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5548 int door_token = dpc->door_token;
5549 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5550 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5551 boolean is_panel_and_door_has_closed = FALSE;
5552 struct Rect *door_rect = &door_rect_list[door_index];
5553 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5555 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5556 int current_door_state = door_state & door_token;
5557 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5558 boolean door_closing = !door_opening;
5559 boolean part_opening = (is_panel ? door_closing : door_opening);
5560 boolean part_closing = !part_opening;
5561 int start_step = (part_opening ? pos->start_step_opening :
5562 pos->start_step_closing);
5563 int step_delay = pos->step_delay;
5564 int step_factor = step_delay / max_step_delay;
5565 int k1 = (step_factor ? k / step_factor + 1 : k);
5566 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5567 int kk = MAX(0, k2);
5570 int src_x, src_y, src_xx, src_yy;
5571 int dst_x, dst_y, dst_xx, dst_yy;
5574 if (door_part_skip[nr])
5577 if (!(door_state & door_token))
5585 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5586 int kk_door = MAX(0, k2_door);
5587 int sync_frame = kk_door * door_delay.value;
5588 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5590 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5591 &g_src_x, &g_src_y);
5596 if (!door_panel_drawn[door_index])
5598 ClearRectangle(drawto, door_rect->x, door_rect->y,
5599 door_rect->width, door_rect->height);
5601 door_panel_drawn[door_index] = TRUE;
5604 // draw opening or closing door parts
5606 if (pos->step_xoffset < 0) // door part on right side
5609 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5612 if (dst_xx + width > door_rect->width)
5613 width = door_rect->width - dst_xx;
5615 else // door part on left side
5618 dst_xx = pos->x - kk * pos->step_xoffset;
5622 src_xx = ABS(dst_xx);
5626 width = g->width - src_xx;
5628 if (width > door_rect->width)
5629 width = door_rect->width;
5631 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5634 if (pos->step_yoffset < 0) // door part on bottom side
5637 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5640 if (dst_yy + height > door_rect->height)
5641 height = door_rect->height - dst_yy;
5643 else // door part on top side
5646 dst_yy = pos->y - kk * pos->step_yoffset;
5650 src_yy = ABS(dst_yy);
5654 height = g->height - src_yy;
5657 src_x = g_src_x + src_xx;
5658 src_y = g_src_y + src_yy;
5660 dst_x = door_rect->x + dst_xx;
5661 dst_y = door_rect->y + dst_yy;
5663 is_panel_and_door_has_closed =
5666 panel_has_doors[door_index] &&
5667 k >= num_move_steps_doors_only - 1);
5669 if (width >= 0 && width <= g->width &&
5670 height >= 0 && height <= g->height &&
5671 !is_panel_and_door_has_closed)
5673 if (is_panel || !pos->draw_masked)
5674 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5677 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5681 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5683 if ((part_opening && (width < 0 || height < 0)) ||
5684 (part_closing && (width >= g->width && height >= g->height)))
5685 door_part_done[nr] = TRUE;
5687 // continue door part animations, but not panel after door has closed
5688 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5689 door_part_done_all = FALSE;
5692 if (!(door_state & DOOR_NO_DELAY))
5696 SkipUntilDelayReached(&door_delay, &k, last_frame);
5698 // prevent OS (Windows) from complaining about program not responding
5702 if (door_part_done_all)
5706 if (!(door_state & DOOR_NO_DELAY))
5708 // wait for specified door action post delay
5709 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5710 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5711 else if (door_state & DOOR_ACTION_1)
5712 door_delay.value = door_1.post_delay;
5713 else if (door_state & DOOR_ACTION_2)
5714 door_delay.value = door_2.post_delay;
5716 while (!DelayReached(&door_delay))
5721 if (door_state & DOOR_ACTION_1)
5722 door1 = door_state & DOOR_ACTION_1;
5723 if (door_state & DOOR_ACTION_2)
5724 door2 = door_state & DOOR_ACTION_2;
5726 // draw masked border over door area
5727 DrawMaskedBorder(REDRAW_DOOR_1);
5728 DrawMaskedBorder(REDRAW_DOOR_2);
5730 ClearAutoRepeatKeyEvents();
5732 return (door1 | door2);
5735 static boolean useSpecialEditorDoor(void)
5737 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5738 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5740 // do not draw special editor door if editor border defined or redefined
5741 if (graphic_info[graphic].bitmap != NULL || redefined)
5744 // do not draw special editor door if global border defined to be empty
5745 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5748 // do not draw special editor door if viewport definitions do not match
5752 EY + EYSIZE != VY + VYSIZE)
5758 void DrawSpecialEditorDoor(void)
5760 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5761 int top_border_width = gfx1->width;
5762 int top_border_height = gfx1->height;
5763 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5764 int ex = EX - outer_border;
5765 int ey = EY - outer_border;
5766 int vy = VY - outer_border;
5767 int exsize = EXSIZE + 2 * outer_border;
5769 if (!useSpecialEditorDoor())
5772 // draw bigger level editor toolbox window
5773 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5774 top_border_width, top_border_height, ex, ey - top_border_height);
5775 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5776 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5778 redraw_mask |= REDRAW_ALL;
5781 void UndrawSpecialEditorDoor(void)
5783 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5784 int top_border_width = gfx1->width;
5785 int top_border_height = gfx1->height;
5786 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5787 int ex = EX - outer_border;
5788 int ey = EY - outer_border;
5789 int ey_top = ey - top_border_height;
5790 int exsize = EXSIZE + 2 * outer_border;
5791 int eysize = EYSIZE + 2 * outer_border;
5793 if (!useSpecialEditorDoor())
5796 // draw normal tape recorder window
5797 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5799 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5800 ex, ey_top, top_border_width, top_border_height,
5802 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5803 ex, ey, exsize, eysize, ex, ey);
5807 // if screen background is set to "[NONE]", clear editor toolbox window
5808 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5809 ClearRectangle(drawto, ex, ey, exsize, eysize);
5812 redraw_mask |= REDRAW_ALL;
5816 // ---------- new tool button stuff -------------------------------------------
5821 struct TextPosInfo *pos;
5823 boolean is_touch_button;
5825 } toolbutton_info[NUM_TOOL_BUTTONS] =
5828 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5829 TOOL_CTRL_ID_YES, FALSE, "yes"
5832 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5833 TOOL_CTRL_ID_NO, FALSE, "no"
5836 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5837 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5840 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5841 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5844 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5845 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5848 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5849 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5852 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5853 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5856 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5857 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5860 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5861 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5864 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5865 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5869 void CreateToolButtons(void)
5873 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5875 int graphic = toolbutton_info[i].graphic;
5876 struct GraphicInfo *gfx = &graphic_info[graphic];
5877 struct TextPosInfo *pos = toolbutton_info[i].pos;
5878 struct GadgetInfo *gi;
5879 Bitmap *deco_bitmap = None;
5880 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5881 unsigned int event_mask = GD_EVENT_RELEASED;
5882 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5883 int base_x = (is_touch_button ? 0 : DX);
5884 int base_y = (is_touch_button ? 0 : DY);
5885 int gd_x = gfx->src_x;
5886 int gd_y = gfx->src_y;
5887 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5888 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5893 // do not use touch buttons if overlay touch buttons are disabled
5894 if (is_touch_button && !setup.touch.overlay_buttons)
5897 if (global.use_envelope_request && !is_touch_button)
5899 setRequestPosition(&base_x, &base_y, TRUE);
5901 // check if request buttons are outside of envelope and fix, if needed
5902 if (x < 0 || x + gfx->width > request.width ||
5903 y < 0 || y + gfx->height > request.height)
5905 if (id == TOOL_CTRL_ID_YES)
5908 y = request.height - 2 * request.border_size - gfx->height;
5910 else if (id == TOOL_CTRL_ID_NO)
5912 x = request.width - 2 * request.border_size - gfx->width;
5913 y = request.height - 2 * request.border_size - gfx->height;
5915 else if (id == TOOL_CTRL_ID_CONFIRM)
5917 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5918 y = request.height - 2 * request.border_size - gfx->height;
5920 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5922 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5924 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5925 y = request.height - 2 * request.border_size - gfx->height * 2;
5927 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5928 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5933 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5936 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5938 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5939 pos->size, &deco_bitmap, &deco_x, &deco_y);
5940 deco_xpos = (gfx->width - pos->size) / 2;
5941 deco_ypos = (gfx->height - pos->size) / 2;
5944 gi = CreateGadget(GDI_CUSTOM_ID, id,
5945 GDI_IMAGE_ID, graphic,
5946 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5949 GDI_WIDTH, gfx->width,
5950 GDI_HEIGHT, gfx->height,
5951 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5952 GDI_STATE, GD_BUTTON_UNPRESSED,
5953 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5954 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5955 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5956 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5957 GDI_DECORATION_SIZE, pos->size, pos->size,
5958 GDI_DECORATION_SHIFTING, 1, 1,
5959 GDI_DIRECT_DRAW, FALSE,
5960 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5961 GDI_EVENT_MASK, event_mask,
5962 GDI_CALLBACK_ACTION, HandleToolButtons,
5966 Fail("cannot create gadget");
5968 tool_gadget[id] = gi;
5972 void FreeToolButtons(void)
5976 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5977 FreeGadget(tool_gadget[i]);
5980 static void UnmapToolButtons(void)
5984 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5985 UnmapGadget(tool_gadget[i]);
5988 static void HandleToolButtons(struct GadgetInfo *gi)
5990 request_gadget_id = gi->custom_id;
5993 static struct Mapping_EM_to_RND_object
5996 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5997 boolean is_backside; // backside of moving element
6003 em_object_mapping_list[GAME_TILE_MAX + 1] =
6006 Zborder, FALSE, FALSE,
6010 Zplayer, FALSE, FALSE,
6019 Ztank, FALSE, FALSE,
6023 Zeater, FALSE, FALSE,
6027 Zdynamite, FALSE, FALSE,
6031 Zboom, FALSE, FALSE,
6036 Xchain, FALSE, FALSE,
6037 EL_DEFAULT, ACTION_EXPLODING, -1
6040 Xboom_bug, FALSE, FALSE,
6041 EL_BUG, ACTION_EXPLODING, -1
6044 Xboom_tank, FALSE, FALSE,
6045 EL_SPACESHIP, ACTION_EXPLODING, -1
6048 Xboom_android, FALSE, FALSE,
6049 EL_EMC_ANDROID, ACTION_OTHER, -1
6052 Xboom_1, FALSE, FALSE,
6053 EL_DEFAULT, ACTION_EXPLODING, -1
6056 Xboom_2, FALSE, FALSE,
6057 EL_DEFAULT, ACTION_EXPLODING, -1
6061 Xblank, TRUE, FALSE,
6066 Xsplash_e, FALSE, FALSE,
6067 EL_ACID_SPLASH_RIGHT, -1, -1
6070 Xsplash_w, FALSE, FALSE,
6071 EL_ACID_SPLASH_LEFT, -1, -1
6075 Xplant, TRUE, FALSE,
6076 EL_EMC_PLANT, -1, -1
6079 Yplant, FALSE, FALSE,
6080 EL_EMC_PLANT, -1, -1
6084 Xacid_1, TRUE, FALSE,
6088 Xacid_2, FALSE, FALSE,
6092 Xacid_3, FALSE, FALSE,
6096 Xacid_4, FALSE, FALSE,
6100 Xacid_5, FALSE, FALSE,
6104 Xacid_6, FALSE, FALSE,
6108 Xacid_7, FALSE, FALSE,
6112 Xacid_8, FALSE, FALSE,
6117 Xfake_acid_1, TRUE, FALSE,
6118 EL_EMC_FAKE_ACID, -1, -1
6121 Xfake_acid_2, FALSE, FALSE,
6122 EL_EMC_FAKE_ACID, -1, -1
6125 Xfake_acid_3, FALSE, FALSE,
6126 EL_EMC_FAKE_ACID, -1, -1
6129 Xfake_acid_4, FALSE, FALSE,
6130 EL_EMC_FAKE_ACID, -1, -1
6133 Xfake_acid_5, FALSE, FALSE,
6134 EL_EMC_FAKE_ACID, -1, -1
6137 Xfake_acid_6, FALSE, FALSE,
6138 EL_EMC_FAKE_ACID, -1, -1
6141 Xfake_acid_7, FALSE, FALSE,
6142 EL_EMC_FAKE_ACID, -1, -1
6145 Xfake_acid_8, FALSE, FALSE,
6146 EL_EMC_FAKE_ACID, -1, -1
6150 Xfake_acid_1_player, FALSE, FALSE,
6151 EL_EMC_FAKE_ACID, -1, -1
6154 Xfake_acid_2_player, FALSE, FALSE,
6155 EL_EMC_FAKE_ACID, -1, -1
6158 Xfake_acid_3_player, FALSE, FALSE,
6159 EL_EMC_FAKE_ACID, -1, -1
6162 Xfake_acid_4_player, FALSE, FALSE,
6163 EL_EMC_FAKE_ACID, -1, -1
6166 Xfake_acid_5_player, FALSE, FALSE,
6167 EL_EMC_FAKE_ACID, -1, -1
6170 Xfake_acid_6_player, FALSE, FALSE,
6171 EL_EMC_FAKE_ACID, -1, -1
6174 Xfake_acid_7_player, FALSE, FALSE,
6175 EL_EMC_FAKE_ACID, -1, -1
6178 Xfake_acid_8_player, FALSE, FALSE,
6179 EL_EMC_FAKE_ACID, -1, -1
6183 Xgrass, TRUE, FALSE,
6184 EL_EMC_GRASS, -1, -1
6187 Ygrass_nB, FALSE, FALSE,
6188 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6191 Ygrass_eB, FALSE, FALSE,
6192 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6195 Ygrass_sB, FALSE, FALSE,
6196 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6199 Ygrass_wB, FALSE, FALSE,
6200 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6208 Ydirt_nB, FALSE, FALSE,
6209 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6212 Ydirt_eB, FALSE, FALSE,
6213 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6216 Ydirt_sB, FALSE, FALSE,
6217 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6220 Ydirt_wB, FALSE, FALSE,
6221 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6225 Xandroid, TRUE, FALSE,
6226 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6229 Xandroid_1_n, FALSE, FALSE,
6230 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6233 Xandroid_2_n, FALSE, FALSE,
6234 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6237 Xandroid_1_e, FALSE, FALSE,
6238 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6241 Xandroid_2_e, FALSE, FALSE,
6242 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6245 Xandroid_1_w, FALSE, FALSE,
6246 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6249 Xandroid_2_w, FALSE, FALSE,
6250 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6253 Xandroid_1_s, FALSE, FALSE,
6254 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6257 Xandroid_2_s, FALSE, FALSE,
6258 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6261 Yandroid_n, FALSE, FALSE,
6262 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6265 Yandroid_nB, FALSE, TRUE,
6266 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6269 Yandroid_ne, FALSE, FALSE,
6270 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6273 Yandroid_neB, FALSE, TRUE,
6274 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6277 Yandroid_e, FALSE, FALSE,
6278 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6281 Yandroid_eB, FALSE, TRUE,
6282 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6285 Yandroid_se, FALSE, FALSE,
6286 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6289 Yandroid_seB, FALSE, TRUE,
6290 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6293 Yandroid_s, FALSE, FALSE,
6294 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6297 Yandroid_sB, FALSE, TRUE,
6298 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6301 Yandroid_sw, FALSE, FALSE,
6302 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6305 Yandroid_swB, FALSE, TRUE,
6306 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6309 Yandroid_w, FALSE, FALSE,
6310 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6313 Yandroid_wB, FALSE, TRUE,
6314 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6317 Yandroid_nw, FALSE, FALSE,
6318 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6321 Yandroid_nwB, FALSE, TRUE,
6322 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6326 Xeater_n, TRUE, FALSE,
6327 EL_YAMYAM_UP, -1, -1
6330 Xeater_e, TRUE, FALSE,
6331 EL_YAMYAM_RIGHT, -1, -1
6334 Xeater_w, TRUE, FALSE,
6335 EL_YAMYAM_LEFT, -1, -1
6338 Xeater_s, TRUE, FALSE,
6339 EL_YAMYAM_DOWN, -1, -1
6342 Yeater_n, FALSE, FALSE,
6343 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6346 Yeater_nB, FALSE, TRUE,
6347 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6350 Yeater_e, FALSE, FALSE,
6351 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6354 Yeater_eB, FALSE, TRUE,
6355 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6358 Yeater_s, FALSE, FALSE,
6359 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6362 Yeater_sB, FALSE, TRUE,
6363 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6366 Yeater_w, FALSE, FALSE,
6367 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6370 Yeater_wB, FALSE, TRUE,
6371 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6374 Yeater_stone, FALSE, FALSE,
6375 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6378 Yeater_spring, FALSE, FALSE,
6379 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6383 Xalien, TRUE, FALSE,
6387 Xalien_pause, FALSE, FALSE,
6391 Yalien_n, FALSE, FALSE,
6392 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6395 Yalien_nB, FALSE, TRUE,
6396 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6399 Yalien_e, FALSE, FALSE,
6400 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6403 Yalien_eB, FALSE, TRUE,
6404 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6407 Yalien_s, FALSE, FALSE,
6408 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6411 Yalien_sB, FALSE, TRUE,
6412 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6415 Yalien_w, FALSE, FALSE,
6416 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6419 Yalien_wB, FALSE, TRUE,
6420 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6423 Yalien_stone, FALSE, FALSE,
6424 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6427 Yalien_spring, FALSE, FALSE,
6428 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6432 Xbug_1_n, TRUE, FALSE,
6436 Xbug_1_e, TRUE, FALSE,
6437 EL_BUG_RIGHT, -1, -1
6440 Xbug_1_s, TRUE, FALSE,
6444 Xbug_1_w, TRUE, FALSE,
6448 Xbug_2_n, FALSE, FALSE,
6452 Xbug_2_e, FALSE, FALSE,
6453 EL_BUG_RIGHT, -1, -1
6456 Xbug_2_s, FALSE, FALSE,
6460 Xbug_2_w, FALSE, FALSE,
6464 Ybug_n, FALSE, FALSE,
6465 EL_BUG, ACTION_MOVING, MV_BIT_UP
6468 Ybug_nB, FALSE, TRUE,
6469 EL_BUG, ACTION_MOVING, MV_BIT_UP
6472 Ybug_e, FALSE, FALSE,
6473 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6476 Ybug_eB, FALSE, TRUE,
6477 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6480 Ybug_s, FALSE, FALSE,
6481 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6484 Ybug_sB, FALSE, TRUE,
6485 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6488 Ybug_w, FALSE, FALSE,
6489 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6492 Ybug_wB, FALSE, TRUE,
6493 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6496 Ybug_w_n, FALSE, FALSE,
6497 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6500 Ybug_n_e, FALSE, FALSE,
6501 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6504 Ybug_e_s, FALSE, FALSE,
6505 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6508 Ybug_s_w, FALSE, FALSE,
6509 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6512 Ybug_e_n, FALSE, FALSE,
6513 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6516 Ybug_s_e, FALSE, FALSE,
6517 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6520 Ybug_w_s, FALSE, FALSE,
6521 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6524 Ybug_n_w, FALSE, FALSE,
6525 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6528 Ybug_stone, FALSE, FALSE,
6529 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6532 Ybug_spring, FALSE, FALSE,
6533 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6537 Xtank_1_n, TRUE, FALSE,
6538 EL_SPACESHIP_UP, -1, -1
6541 Xtank_1_e, TRUE, FALSE,
6542 EL_SPACESHIP_RIGHT, -1, -1
6545 Xtank_1_s, TRUE, FALSE,
6546 EL_SPACESHIP_DOWN, -1, -1
6549 Xtank_1_w, TRUE, FALSE,
6550 EL_SPACESHIP_LEFT, -1, -1
6553 Xtank_2_n, FALSE, FALSE,
6554 EL_SPACESHIP_UP, -1, -1
6557 Xtank_2_e, FALSE, FALSE,
6558 EL_SPACESHIP_RIGHT, -1, -1
6561 Xtank_2_s, FALSE, FALSE,
6562 EL_SPACESHIP_DOWN, -1, -1
6565 Xtank_2_w, FALSE, FALSE,
6566 EL_SPACESHIP_LEFT, -1, -1
6569 Ytank_n, FALSE, FALSE,
6570 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6573 Ytank_nB, FALSE, TRUE,
6574 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6577 Ytank_e, FALSE, FALSE,
6578 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6581 Ytank_eB, FALSE, TRUE,
6582 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6585 Ytank_s, FALSE, FALSE,
6586 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6589 Ytank_sB, FALSE, TRUE,
6590 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6593 Ytank_w, FALSE, FALSE,
6594 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6597 Ytank_wB, FALSE, TRUE,
6598 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6601 Ytank_w_n, FALSE, FALSE,
6602 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6605 Ytank_n_e, FALSE, FALSE,
6606 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6609 Ytank_e_s, FALSE, FALSE,
6610 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6613 Ytank_s_w, FALSE, FALSE,
6614 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6617 Ytank_e_n, FALSE, FALSE,
6618 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6621 Ytank_s_e, FALSE, FALSE,
6622 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6625 Ytank_w_s, FALSE, FALSE,
6626 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6629 Ytank_n_w, FALSE, FALSE,
6630 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6633 Ytank_stone, FALSE, FALSE,
6634 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6637 Ytank_spring, FALSE, FALSE,
6638 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6642 Xemerald, TRUE, FALSE,
6646 Xemerald_pause, FALSE, FALSE,
6650 Xemerald_fall, FALSE, FALSE,
6654 Xemerald_shine, FALSE, FALSE,
6655 EL_EMERALD, ACTION_TWINKLING, -1
6658 Yemerald_s, FALSE, FALSE,
6659 EL_EMERALD, ACTION_FALLING, -1
6662 Yemerald_sB, FALSE, TRUE,
6663 EL_EMERALD, ACTION_FALLING, -1
6666 Yemerald_e, FALSE, FALSE,
6667 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6670 Yemerald_eB, FALSE, TRUE,
6671 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6674 Yemerald_w, FALSE, FALSE,
6675 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6678 Yemerald_wB, FALSE, TRUE,
6679 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6682 Yemerald_blank, FALSE, FALSE,
6683 EL_EMERALD, ACTION_COLLECTING, -1
6687 Xdiamond, TRUE, FALSE,
6691 Xdiamond_pause, FALSE, FALSE,
6695 Xdiamond_fall, FALSE, FALSE,
6699 Xdiamond_shine, FALSE, FALSE,
6700 EL_DIAMOND, ACTION_TWINKLING, -1
6703 Ydiamond_s, FALSE, FALSE,
6704 EL_DIAMOND, ACTION_FALLING, -1
6707 Ydiamond_sB, FALSE, TRUE,
6708 EL_DIAMOND, ACTION_FALLING, -1
6711 Ydiamond_e, FALSE, FALSE,
6712 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6715 Ydiamond_eB, FALSE, TRUE,
6716 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6719 Ydiamond_w, FALSE, FALSE,
6720 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6723 Ydiamond_wB, FALSE, TRUE,
6724 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6727 Ydiamond_blank, FALSE, FALSE,
6728 EL_DIAMOND, ACTION_COLLECTING, -1
6731 Ydiamond_stone, FALSE, FALSE,
6732 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6736 Xstone, TRUE, FALSE,
6740 Xstone_pause, FALSE, FALSE,
6744 Xstone_fall, FALSE, FALSE,
6748 Ystone_s, FALSE, FALSE,
6749 EL_ROCK, ACTION_FALLING, -1
6752 Ystone_sB, FALSE, TRUE,
6753 EL_ROCK, ACTION_FALLING, -1
6756 Ystone_e, FALSE, FALSE,
6757 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6760 Ystone_eB, FALSE, TRUE,
6761 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6764 Ystone_w, FALSE, FALSE,
6765 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6768 Ystone_wB, FALSE, TRUE,
6769 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6777 Xbomb_pause, FALSE, FALSE,
6781 Xbomb_fall, FALSE, FALSE,
6785 Ybomb_s, FALSE, FALSE,
6786 EL_BOMB, ACTION_FALLING, -1
6789 Ybomb_sB, FALSE, TRUE,
6790 EL_BOMB, ACTION_FALLING, -1
6793 Ybomb_e, FALSE, FALSE,
6794 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6797 Ybomb_eB, FALSE, TRUE,
6798 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6801 Ybomb_w, FALSE, FALSE,
6802 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6805 Ybomb_wB, FALSE, TRUE,
6806 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6809 Ybomb_blank, FALSE, FALSE,
6810 EL_BOMB, ACTION_ACTIVATING, -1
6818 Xnut_pause, FALSE, FALSE,
6822 Xnut_fall, FALSE, FALSE,
6826 Ynut_s, FALSE, FALSE,
6827 EL_NUT, ACTION_FALLING, -1
6830 Ynut_sB, FALSE, TRUE,
6831 EL_NUT, ACTION_FALLING, -1
6834 Ynut_e, FALSE, FALSE,
6835 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6838 Ynut_eB, FALSE, TRUE,
6839 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6842 Ynut_w, FALSE, FALSE,
6843 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6846 Ynut_wB, FALSE, TRUE,
6847 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6850 Ynut_stone, FALSE, FALSE,
6851 EL_NUT, ACTION_BREAKING, -1
6855 Xspring, TRUE, FALSE,
6859 Xspring_pause, FALSE, FALSE,
6863 Xspring_e, TRUE, FALSE,
6864 EL_SPRING_RIGHT, -1, -1
6867 Xspring_w, TRUE, FALSE,
6868 EL_SPRING_LEFT, -1, -1
6871 Xspring_fall, FALSE, FALSE,
6875 Yspring_s, FALSE, FALSE,
6876 EL_SPRING, ACTION_FALLING, -1
6879 Yspring_sB, FALSE, TRUE,
6880 EL_SPRING, ACTION_FALLING, -1
6883 Yspring_e, FALSE, FALSE,
6884 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6887 Yspring_eB, FALSE, TRUE,
6888 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6891 Yspring_w, FALSE, FALSE,
6892 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6895 Yspring_wB, FALSE, TRUE,
6896 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6899 Yspring_alien_e, FALSE, FALSE,
6900 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6903 Yspring_alien_eB, FALSE, TRUE,
6904 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6907 Yspring_alien_w, FALSE, FALSE,
6908 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6911 Yspring_alien_wB, FALSE, TRUE,
6912 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6916 Xpush_emerald_e, FALSE, FALSE,
6917 EL_EMERALD, -1, MV_BIT_RIGHT
6920 Xpush_emerald_w, FALSE, FALSE,
6921 EL_EMERALD, -1, MV_BIT_LEFT
6924 Xpush_diamond_e, FALSE, FALSE,
6925 EL_DIAMOND, -1, MV_BIT_RIGHT
6928 Xpush_diamond_w, FALSE, FALSE,
6929 EL_DIAMOND, -1, MV_BIT_LEFT
6932 Xpush_stone_e, FALSE, FALSE,
6933 EL_ROCK, -1, MV_BIT_RIGHT
6936 Xpush_stone_w, FALSE, FALSE,
6937 EL_ROCK, -1, MV_BIT_LEFT
6940 Xpush_bomb_e, FALSE, FALSE,
6941 EL_BOMB, -1, MV_BIT_RIGHT
6944 Xpush_bomb_w, FALSE, FALSE,
6945 EL_BOMB, -1, MV_BIT_LEFT
6948 Xpush_nut_e, FALSE, FALSE,
6949 EL_NUT, -1, MV_BIT_RIGHT
6952 Xpush_nut_w, FALSE, FALSE,
6953 EL_NUT, -1, MV_BIT_LEFT
6956 Xpush_spring_e, FALSE, FALSE,
6957 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6960 Xpush_spring_w, FALSE, FALSE,
6961 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6965 Xdynamite, TRUE, FALSE,
6966 EL_EM_DYNAMITE, -1, -1
6969 Ydynamite_blank, FALSE, FALSE,
6970 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6973 Xdynamite_1, TRUE, FALSE,
6974 EL_EM_DYNAMITE_ACTIVE, -1, -1
6977 Xdynamite_2, FALSE, FALSE,
6978 EL_EM_DYNAMITE_ACTIVE, -1, -1
6981 Xdynamite_3, FALSE, FALSE,
6982 EL_EM_DYNAMITE_ACTIVE, -1, -1
6985 Xdynamite_4, FALSE, FALSE,
6986 EL_EM_DYNAMITE_ACTIVE, -1, -1
6990 Xkey_1, TRUE, FALSE,
6994 Xkey_2, TRUE, FALSE,
6998 Xkey_3, TRUE, FALSE,
7002 Xkey_4, TRUE, FALSE,
7006 Xkey_5, TRUE, FALSE,
7007 EL_EMC_KEY_5, -1, -1
7010 Xkey_6, TRUE, FALSE,
7011 EL_EMC_KEY_6, -1, -1
7014 Xkey_7, TRUE, FALSE,
7015 EL_EMC_KEY_7, -1, -1
7018 Xkey_8, TRUE, FALSE,
7019 EL_EMC_KEY_8, -1, -1
7023 Xdoor_1, TRUE, FALSE,
7024 EL_EM_GATE_1, -1, -1
7027 Xdoor_2, TRUE, FALSE,
7028 EL_EM_GATE_2, -1, -1
7031 Xdoor_3, TRUE, FALSE,
7032 EL_EM_GATE_3, -1, -1
7035 Xdoor_4, TRUE, FALSE,
7036 EL_EM_GATE_4, -1, -1
7039 Xdoor_5, TRUE, FALSE,
7040 EL_EMC_GATE_5, -1, -1
7043 Xdoor_6, TRUE, FALSE,
7044 EL_EMC_GATE_6, -1, -1
7047 Xdoor_7, TRUE, FALSE,
7048 EL_EMC_GATE_7, -1, -1
7051 Xdoor_8, TRUE, FALSE,
7052 EL_EMC_GATE_8, -1, -1
7056 Xfake_door_1, TRUE, FALSE,
7057 EL_EM_GATE_1_GRAY, -1, -1
7060 Xfake_door_2, TRUE, FALSE,
7061 EL_EM_GATE_2_GRAY, -1, -1
7064 Xfake_door_3, TRUE, FALSE,
7065 EL_EM_GATE_3_GRAY, -1, -1
7068 Xfake_door_4, TRUE, FALSE,
7069 EL_EM_GATE_4_GRAY, -1, -1
7072 Xfake_door_5, TRUE, FALSE,
7073 EL_EMC_GATE_5_GRAY, -1, -1
7076 Xfake_door_6, TRUE, FALSE,
7077 EL_EMC_GATE_6_GRAY, -1, -1
7080 Xfake_door_7, TRUE, FALSE,
7081 EL_EMC_GATE_7_GRAY, -1, -1
7084 Xfake_door_8, TRUE, FALSE,
7085 EL_EMC_GATE_8_GRAY, -1, -1
7089 Xballoon, TRUE, FALSE,
7093 Yballoon_n, FALSE, FALSE,
7094 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7097 Yballoon_nB, FALSE, TRUE,
7098 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7101 Yballoon_e, FALSE, FALSE,
7102 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7105 Yballoon_eB, FALSE, TRUE,
7106 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7109 Yballoon_s, FALSE, FALSE,
7110 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7113 Yballoon_sB, FALSE, TRUE,
7114 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7117 Yballoon_w, FALSE, FALSE,
7118 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7121 Yballoon_wB, FALSE, TRUE,
7122 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7126 Xball_1, TRUE, FALSE,
7127 EL_EMC_MAGIC_BALL, -1, -1
7130 Yball_1, FALSE, FALSE,
7131 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7134 Xball_2, FALSE, FALSE,
7135 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7138 Yball_2, FALSE, FALSE,
7139 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7142 Yball_blank, FALSE, FALSE,
7143 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7147 Xamoeba_1, TRUE, FALSE,
7148 EL_AMOEBA_DRY, ACTION_OTHER, -1
7151 Xamoeba_2, FALSE, FALSE,
7152 EL_AMOEBA_DRY, ACTION_OTHER, -1
7155 Xamoeba_3, FALSE, FALSE,
7156 EL_AMOEBA_DRY, ACTION_OTHER, -1
7159 Xamoeba_4, FALSE, FALSE,
7160 EL_AMOEBA_DRY, ACTION_OTHER, -1
7163 Xamoeba_5, TRUE, FALSE,
7164 EL_AMOEBA_WET, ACTION_OTHER, -1
7167 Xamoeba_6, FALSE, FALSE,
7168 EL_AMOEBA_WET, ACTION_OTHER, -1
7171 Xamoeba_7, FALSE, FALSE,
7172 EL_AMOEBA_WET, ACTION_OTHER, -1
7175 Xamoeba_8, FALSE, FALSE,
7176 EL_AMOEBA_WET, ACTION_OTHER, -1
7181 EL_AMOEBA_DROP, ACTION_GROWING, -1
7184 Xdrip_fall, FALSE, FALSE,
7185 EL_AMOEBA_DROP, -1, -1
7188 Xdrip_stretch, FALSE, FALSE,
7189 EL_AMOEBA_DROP, ACTION_FALLING, -1
7192 Xdrip_stretchB, FALSE, TRUE,
7193 EL_AMOEBA_DROP, ACTION_FALLING, -1
7196 Ydrip_1_s, FALSE, FALSE,
7197 EL_AMOEBA_DROP, ACTION_FALLING, -1
7200 Ydrip_1_sB, FALSE, TRUE,
7201 EL_AMOEBA_DROP, ACTION_FALLING, -1
7204 Ydrip_2_s, FALSE, FALSE,
7205 EL_AMOEBA_DROP, ACTION_FALLING, -1
7208 Ydrip_2_sB, FALSE, TRUE,
7209 EL_AMOEBA_DROP, ACTION_FALLING, -1
7213 Xwonderwall, TRUE, FALSE,
7214 EL_MAGIC_WALL, -1, -1
7217 Ywonderwall, FALSE, FALSE,
7218 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7222 Xwheel, TRUE, FALSE,
7223 EL_ROBOT_WHEEL, -1, -1
7226 Ywheel, FALSE, FALSE,
7227 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7231 Xswitch, TRUE, FALSE,
7232 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7235 Yswitch, FALSE, FALSE,
7236 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7240 Xbumper, TRUE, FALSE,
7241 EL_EMC_SPRING_BUMPER, -1, -1
7244 Ybumper, FALSE, FALSE,
7245 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7249 Xacid_nw, TRUE, FALSE,
7250 EL_ACID_POOL_TOPLEFT, -1, -1
7253 Xacid_ne, TRUE, FALSE,
7254 EL_ACID_POOL_TOPRIGHT, -1, -1
7257 Xacid_sw, TRUE, FALSE,
7258 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7261 Xacid_s, TRUE, FALSE,
7262 EL_ACID_POOL_BOTTOM, -1, -1
7265 Xacid_se, TRUE, FALSE,
7266 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7270 Xfake_blank, TRUE, FALSE,
7271 EL_INVISIBLE_WALL, -1, -1
7274 Yfake_blank, FALSE, FALSE,
7275 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7279 Xfake_grass, TRUE, FALSE,
7280 EL_EMC_FAKE_GRASS, -1, -1
7283 Yfake_grass, FALSE, FALSE,
7284 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7288 Xfake_amoeba, TRUE, FALSE,
7289 EL_EMC_DRIPPER, -1, -1
7292 Yfake_amoeba, FALSE, FALSE,
7293 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7297 Xlenses, TRUE, FALSE,
7298 EL_EMC_LENSES, -1, -1
7302 Xmagnify, TRUE, FALSE,
7303 EL_EMC_MAGNIFIER, -1, -1
7308 EL_QUICKSAND_EMPTY, -1, -1
7311 Xsand_stone, TRUE, FALSE,
7312 EL_QUICKSAND_FULL, -1, -1
7315 Xsand_stonein_1, FALSE, TRUE,
7316 EL_ROCK, ACTION_FILLING, -1
7319 Xsand_stonein_2, FALSE, TRUE,
7320 EL_ROCK, ACTION_FILLING, -1
7323 Xsand_stonein_3, FALSE, TRUE,
7324 EL_ROCK, ACTION_FILLING, -1
7327 Xsand_stonein_4, FALSE, TRUE,
7328 EL_ROCK, ACTION_FILLING, -1
7331 Xsand_sandstone_1, FALSE, FALSE,
7332 EL_QUICKSAND_FILLING, -1, -1
7335 Xsand_sandstone_2, FALSE, FALSE,
7336 EL_QUICKSAND_FILLING, -1, -1
7339 Xsand_sandstone_3, FALSE, FALSE,
7340 EL_QUICKSAND_FILLING, -1, -1
7343 Xsand_sandstone_4, FALSE, FALSE,
7344 EL_QUICKSAND_FILLING, -1, -1
7347 Xsand_stonesand_1, FALSE, FALSE,
7348 EL_QUICKSAND_EMPTYING, -1, -1
7351 Xsand_stonesand_2, FALSE, FALSE,
7352 EL_QUICKSAND_EMPTYING, -1, -1
7355 Xsand_stonesand_3, FALSE, FALSE,
7356 EL_QUICKSAND_EMPTYING, -1, -1
7359 Xsand_stonesand_4, FALSE, FALSE,
7360 EL_QUICKSAND_EMPTYING, -1, -1
7363 Xsand_stoneout_1, FALSE, FALSE,
7364 EL_ROCK, ACTION_EMPTYING, -1
7367 Xsand_stoneout_2, FALSE, FALSE,
7368 EL_ROCK, ACTION_EMPTYING, -1
7371 Xsand_stonesand_quickout_1, FALSE, FALSE,
7372 EL_QUICKSAND_EMPTYING, -1, -1
7375 Xsand_stonesand_quickout_2, FALSE, FALSE,
7376 EL_QUICKSAND_EMPTYING, -1, -1
7380 Xslide_ns, TRUE, FALSE,
7381 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7384 Yslide_ns_blank, FALSE, FALSE,
7385 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7388 Xslide_ew, TRUE, FALSE,
7389 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7392 Yslide_ew_blank, FALSE, FALSE,
7393 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7397 Xwind_n, TRUE, FALSE,
7398 EL_BALLOON_SWITCH_UP, -1, -1
7401 Xwind_e, TRUE, FALSE,
7402 EL_BALLOON_SWITCH_RIGHT, -1, -1
7405 Xwind_s, TRUE, FALSE,
7406 EL_BALLOON_SWITCH_DOWN, -1, -1
7409 Xwind_w, TRUE, FALSE,
7410 EL_BALLOON_SWITCH_LEFT, -1, -1
7413 Xwind_any, TRUE, FALSE,
7414 EL_BALLOON_SWITCH_ANY, -1, -1
7417 Xwind_stop, TRUE, FALSE,
7418 EL_BALLOON_SWITCH_NONE, -1, -1
7423 EL_EM_EXIT_CLOSED, -1, -1
7426 Xexit_1, TRUE, FALSE,
7427 EL_EM_EXIT_OPEN, -1, -1
7430 Xexit_2, FALSE, FALSE,
7431 EL_EM_EXIT_OPEN, -1, -1
7434 Xexit_3, FALSE, FALSE,
7435 EL_EM_EXIT_OPEN, -1, -1
7439 Xpause, FALSE, FALSE,
7444 Xwall_1, TRUE, FALSE,
7448 Xwall_2, TRUE, FALSE,
7449 EL_EMC_WALL_14, -1, -1
7452 Xwall_3, TRUE, FALSE,
7453 EL_EMC_WALL_15, -1, -1
7456 Xwall_4, TRUE, FALSE,
7457 EL_EMC_WALL_16, -1, -1
7461 Xroundwall_1, TRUE, FALSE,
7462 EL_WALL_SLIPPERY, -1, -1
7465 Xroundwall_2, TRUE, FALSE,
7466 EL_EMC_WALL_SLIPPERY_2, -1, -1
7469 Xroundwall_3, TRUE, FALSE,
7470 EL_EMC_WALL_SLIPPERY_3, -1, -1
7473 Xroundwall_4, TRUE, FALSE,
7474 EL_EMC_WALL_SLIPPERY_4, -1, -1
7478 Xsteel_1, TRUE, FALSE,
7479 EL_STEELWALL, -1, -1
7482 Xsteel_2, TRUE, FALSE,
7483 EL_EMC_STEELWALL_2, -1, -1
7486 Xsteel_3, TRUE, FALSE,
7487 EL_EMC_STEELWALL_3, -1, -1
7490 Xsteel_4, TRUE, FALSE,
7491 EL_EMC_STEELWALL_4, -1, -1
7495 Xdecor_1, TRUE, FALSE,
7496 EL_EMC_WALL_8, -1, -1
7499 Xdecor_2, TRUE, FALSE,
7500 EL_EMC_WALL_6, -1, -1
7503 Xdecor_3, TRUE, FALSE,
7504 EL_EMC_WALL_4, -1, -1
7507 Xdecor_4, TRUE, FALSE,
7508 EL_EMC_WALL_7, -1, -1
7511 Xdecor_5, TRUE, FALSE,
7512 EL_EMC_WALL_5, -1, -1
7515 Xdecor_6, TRUE, FALSE,
7516 EL_EMC_WALL_9, -1, -1
7519 Xdecor_7, TRUE, FALSE,
7520 EL_EMC_WALL_10, -1, -1
7523 Xdecor_8, TRUE, FALSE,
7524 EL_EMC_WALL_1, -1, -1
7527 Xdecor_9, TRUE, FALSE,
7528 EL_EMC_WALL_2, -1, -1
7531 Xdecor_10, TRUE, FALSE,
7532 EL_EMC_WALL_3, -1, -1
7535 Xdecor_11, TRUE, FALSE,
7536 EL_EMC_WALL_11, -1, -1
7539 Xdecor_12, TRUE, FALSE,
7540 EL_EMC_WALL_12, -1, -1
7544 Xalpha_0, TRUE, FALSE,
7545 EL_CHAR('0'), -1, -1
7548 Xalpha_1, TRUE, FALSE,
7549 EL_CHAR('1'), -1, -1
7552 Xalpha_2, TRUE, FALSE,
7553 EL_CHAR('2'), -1, -1
7556 Xalpha_3, TRUE, FALSE,
7557 EL_CHAR('3'), -1, -1
7560 Xalpha_4, TRUE, FALSE,
7561 EL_CHAR('4'), -1, -1
7564 Xalpha_5, TRUE, FALSE,
7565 EL_CHAR('5'), -1, -1
7568 Xalpha_6, TRUE, FALSE,
7569 EL_CHAR('6'), -1, -1
7572 Xalpha_7, TRUE, FALSE,
7573 EL_CHAR('7'), -1, -1
7576 Xalpha_8, TRUE, FALSE,
7577 EL_CHAR('8'), -1, -1
7580 Xalpha_9, TRUE, FALSE,
7581 EL_CHAR('9'), -1, -1
7584 Xalpha_excla, TRUE, FALSE,
7585 EL_CHAR('!'), -1, -1
7588 Xalpha_apost, TRUE, FALSE,
7589 EL_CHAR('\''), -1, -1
7592 Xalpha_comma, TRUE, FALSE,
7593 EL_CHAR(','), -1, -1
7596 Xalpha_minus, TRUE, FALSE,
7597 EL_CHAR('-'), -1, -1
7600 Xalpha_perio, TRUE, FALSE,
7601 EL_CHAR('.'), -1, -1
7604 Xalpha_colon, TRUE, FALSE,
7605 EL_CHAR(':'), -1, -1
7608 Xalpha_quest, TRUE, FALSE,
7609 EL_CHAR('?'), -1, -1
7612 Xalpha_a, TRUE, FALSE,
7613 EL_CHAR('A'), -1, -1
7616 Xalpha_b, TRUE, FALSE,
7617 EL_CHAR('B'), -1, -1
7620 Xalpha_c, TRUE, FALSE,
7621 EL_CHAR('C'), -1, -1
7624 Xalpha_d, TRUE, FALSE,
7625 EL_CHAR('D'), -1, -1
7628 Xalpha_e, TRUE, FALSE,
7629 EL_CHAR('E'), -1, -1
7632 Xalpha_f, TRUE, FALSE,
7633 EL_CHAR('F'), -1, -1
7636 Xalpha_g, TRUE, FALSE,
7637 EL_CHAR('G'), -1, -1
7640 Xalpha_h, TRUE, FALSE,
7641 EL_CHAR('H'), -1, -1
7644 Xalpha_i, TRUE, FALSE,
7645 EL_CHAR('I'), -1, -1
7648 Xalpha_j, TRUE, FALSE,
7649 EL_CHAR('J'), -1, -1
7652 Xalpha_k, TRUE, FALSE,
7653 EL_CHAR('K'), -1, -1
7656 Xalpha_l, TRUE, FALSE,
7657 EL_CHAR('L'), -1, -1
7660 Xalpha_m, TRUE, FALSE,
7661 EL_CHAR('M'), -1, -1
7664 Xalpha_n, TRUE, FALSE,
7665 EL_CHAR('N'), -1, -1
7668 Xalpha_o, TRUE, FALSE,
7669 EL_CHAR('O'), -1, -1
7672 Xalpha_p, TRUE, FALSE,
7673 EL_CHAR('P'), -1, -1
7676 Xalpha_q, TRUE, FALSE,
7677 EL_CHAR('Q'), -1, -1
7680 Xalpha_r, TRUE, FALSE,
7681 EL_CHAR('R'), -1, -1
7684 Xalpha_s, TRUE, FALSE,
7685 EL_CHAR('S'), -1, -1
7688 Xalpha_t, TRUE, FALSE,
7689 EL_CHAR('T'), -1, -1
7692 Xalpha_u, TRUE, FALSE,
7693 EL_CHAR('U'), -1, -1
7696 Xalpha_v, TRUE, FALSE,
7697 EL_CHAR('V'), -1, -1
7700 Xalpha_w, TRUE, FALSE,
7701 EL_CHAR('W'), -1, -1
7704 Xalpha_x, TRUE, FALSE,
7705 EL_CHAR('X'), -1, -1
7708 Xalpha_y, TRUE, FALSE,
7709 EL_CHAR('Y'), -1, -1
7712 Xalpha_z, TRUE, FALSE,
7713 EL_CHAR('Z'), -1, -1
7716 Xalpha_arrow_e, TRUE, FALSE,
7717 EL_CHAR('>'), -1, -1
7720 Xalpha_arrow_w, TRUE, FALSE,
7721 EL_CHAR('<'), -1, -1
7724 Xalpha_copyr, TRUE, FALSE,
7725 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7729 Ykey_1_blank, FALSE, FALSE,
7730 EL_EM_KEY_1, ACTION_COLLECTING, -1
7733 Ykey_2_blank, FALSE, FALSE,
7734 EL_EM_KEY_2, ACTION_COLLECTING, -1
7737 Ykey_3_blank, FALSE, FALSE,
7738 EL_EM_KEY_3, ACTION_COLLECTING, -1
7741 Ykey_4_blank, FALSE, FALSE,
7742 EL_EM_KEY_4, ACTION_COLLECTING, -1
7745 Ykey_5_blank, FALSE, FALSE,
7746 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7749 Ykey_6_blank, FALSE, FALSE,
7750 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7753 Ykey_7_blank, FALSE, FALSE,
7754 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7757 Ykey_8_blank, FALSE, FALSE,
7758 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7761 Ylenses_blank, FALSE, FALSE,
7762 EL_EMC_LENSES, ACTION_COLLECTING, -1
7765 Ymagnify_blank, FALSE, FALSE,
7766 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7769 Ygrass_blank, FALSE, FALSE,
7770 EL_EMC_GRASS, ACTION_SNAPPING, -1
7773 Ydirt_blank, FALSE, FALSE,
7774 EL_SAND, ACTION_SNAPPING, -1
7783 static struct Mapping_EM_to_RND_player
7792 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7796 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7800 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7804 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7808 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7812 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7816 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7820 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7824 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7828 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7832 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7836 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7840 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7844 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7848 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7852 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7856 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7860 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7864 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7868 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7872 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7876 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7880 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7884 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7888 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7892 EL_PLAYER_1, ACTION_DEFAULT, -1,
7896 EL_PLAYER_2, ACTION_DEFAULT, -1,
7900 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7904 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7908 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7912 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7916 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7920 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7924 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7928 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7932 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7936 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7940 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7944 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7948 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7952 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7956 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7960 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7964 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7968 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7972 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7976 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7980 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7984 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7988 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7992 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7996 EL_PLAYER_3, ACTION_DEFAULT, -1,
8000 EL_PLAYER_4, ACTION_DEFAULT, -1,
8009 int map_element_RND_to_EM_cave(int element_rnd)
8011 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
8012 static boolean mapping_initialized = FALSE;
8014 if (!mapping_initialized)
8018 // return "Xalpha_quest" for all undefined elements in mapping array
8019 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8020 mapping_RND_to_EM[i] = Xalpha_quest;
8022 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8023 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
8024 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
8025 em_object_mapping_list[i].element_em;
8027 mapping_initialized = TRUE;
8030 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
8032 Warn("invalid RND level element %d", element_rnd);
8037 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
8040 int map_element_EM_to_RND_cave(int element_em_cave)
8042 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8043 static boolean mapping_initialized = FALSE;
8045 if (!mapping_initialized)
8049 // return "EL_UNKNOWN" for all undefined elements in mapping array
8050 for (i = 0; i < GAME_TILE_MAX; i++)
8051 mapping_EM_to_RND[i] = EL_UNKNOWN;
8053 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8054 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8055 em_object_mapping_list[i].element_rnd;
8057 mapping_initialized = TRUE;
8060 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8062 Warn("invalid EM cave element %d", element_em_cave);
8067 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8070 int map_element_EM_to_RND_game(int element_em_game)
8072 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8073 static boolean mapping_initialized = FALSE;
8075 if (!mapping_initialized)
8079 // return "EL_UNKNOWN" for all undefined elements in mapping array
8080 for (i = 0; i < GAME_TILE_MAX; i++)
8081 mapping_EM_to_RND[i] = EL_UNKNOWN;
8083 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8084 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8085 em_object_mapping_list[i].element_rnd;
8087 mapping_initialized = TRUE;
8090 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8092 Warn("invalid EM game element %d", element_em_game);
8097 return mapping_EM_to_RND[element_em_game];
8100 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8102 struct LevelInfo_EM *level_em = level->native_em_level;
8103 struct CAVE *cav = level_em->cav;
8106 for (i = 0; i < GAME_TILE_MAX; i++)
8107 cav->android_array[i] = Cblank;
8109 for (i = 0; i < level->num_android_clone_elements; i++)
8111 int element_rnd = level->android_clone_element[i];
8112 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8114 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8115 if (em_object_mapping_list[j].element_rnd == element_rnd)
8116 cav->android_array[em_object_mapping_list[j].element_em] =
8121 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8123 struct LevelInfo_EM *level_em = level->native_em_level;
8124 struct CAVE *cav = level_em->cav;
8127 level->num_android_clone_elements = 0;
8129 for (i = 0; i < GAME_TILE_MAX; i++)
8131 int element_em_cave = cav->android_array[i];
8133 boolean element_found = FALSE;
8135 if (element_em_cave == Cblank)
8138 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8140 for (j = 0; j < level->num_android_clone_elements; j++)
8141 if (level->android_clone_element[j] == element_rnd)
8142 element_found = TRUE;
8146 level->android_clone_element[level->num_android_clone_elements++] =
8149 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8154 if (level->num_android_clone_elements == 0)
8156 level->num_android_clone_elements = 1;
8157 level->android_clone_element[0] = EL_EMPTY;
8161 int map_direction_RND_to_EM(int direction)
8163 return (direction == MV_UP ? 0 :
8164 direction == MV_RIGHT ? 1 :
8165 direction == MV_DOWN ? 2 :
8166 direction == MV_LEFT ? 3 :
8170 int map_direction_EM_to_RND(int direction)
8172 return (direction == 0 ? MV_UP :
8173 direction == 1 ? MV_RIGHT :
8174 direction == 2 ? MV_DOWN :
8175 direction == 3 ? MV_LEFT :
8179 int map_element_RND_to_SP(int element_rnd)
8181 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8183 if (element_rnd >= EL_SP_START &&
8184 element_rnd <= EL_SP_END)
8185 element_sp = element_rnd - EL_SP_START;
8186 else if (element_rnd == EL_EMPTY_SPACE)
8188 else if (element_rnd == EL_INVISIBLE_WALL)
8194 int map_element_SP_to_RND(int element_sp)
8196 int element_rnd = EL_UNKNOWN;
8198 if (element_sp >= 0x00 &&
8200 element_rnd = EL_SP_START + element_sp;
8201 else if (element_sp == 0x28)
8202 element_rnd = EL_INVISIBLE_WALL;
8207 int map_action_SP_to_RND(int action_sp)
8211 case actActive: return ACTION_ACTIVE;
8212 case actImpact: return ACTION_IMPACT;
8213 case actExploding: return ACTION_EXPLODING;
8214 case actDigging: return ACTION_DIGGING;
8215 case actSnapping: return ACTION_SNAPPING;
8216 case actCollecting: return ACTION_COLLECTING;
8217 case actPassing: return ACTION_PASSING;
8218 case actPushing: return ACTION_PUSHING;
8219 case actDropping: return ACTION_DROPPING;
8221 default: return ACTION_DEFAULT;
8225 int map_element_RND_to_MM(int element_rnd)
8227 return (element_rnd >= EL_MM_START_1 &&
8228 element_rnd <= EL_MM_END_1 ?
8229 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8231 element_rnd >= EL_MM_START_2 &&
8232 element_rnd <= EL_MM_END_2 ?
8233 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8235 element_rnd >= EL_MM_START_3 &&
8236 element_rnd <= EL_MM_END_3 ?
8237 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
8239 element_rnd >= EL_CHAR_START &&
8240 element_rnd <= EL_CHAR_END ?
8241 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8243 element_rnd >= EL_MM_RUNTIME_START &&
8244 element_rnd <= EL_MM_RUNTIME_END ?
8245 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8247 EL_MM_EMPTY_NATIVE);
8250 int map_element_MM_to_RND(int element_mm)
8252 return (element_mm == EL_MM_EMPTY_NATIVE ||
8253 element_mm == EL_DF_EMPTY_NATIVE ?
8256 element_mm >= EL_MM_START_1_NATIVE &&
8257 element_mm <= EL_MM_END_1_NATIVE ?
8258 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8260 element_mm >= EL_MM_START_2_NATIVE &&
8261 element_mm <= EL_MM_END_2_NATIVE ?
8262 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8264 element_mm >= EL_MM_START_3_NATIVE &&
8265 element_mm <= EL_MM_END_3_NATIVE ?
8266 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
8268 element_mm >= EL_MM_CHAR_START_NATIVE &&
8269 element_mm <= EL_MM_CHAR_END_NATIVE ?
8270 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8272 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8273 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8274 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8279 int map_action_MM_to_RND(int action_mm)
8281 // all MM actions are defined to exactly match their RND counterparts
8285 int map_sound_MM_to_RND(int sound_mm)
8289 case SND_MM_GAME_LEVELTIME_CHARGING:
8290 return SND_GAME_LEVELTIME_CHARGING;
8292 case SND_MM_GAME_HEALTH_CHARGING:
8293 return SND_GAME_HEALTH_CHARGING;
8296 return SND_UNDEFINED;
8300 int map_mm_wall_element(int element)
8302 return (element >= EL_MM_STEEL_WALL_START &&
8303 element <= EL_MM_STEEL_WALL_END ?
8306 element >= EL_MM_WOODEN_WALL_START &&
8307 element <= EL_MM_WOODEN_WALL_END ?
8310 element >= EL_MM_ICE_WALL_START &&
8311 element <= EL_MM_ICE_WALL_END ?
8314 element >= EL_MM_AMOEBA_WALL_START &&
8315 element <= EL_MM_AMOEBA_WALL_END ?
8318 element >= EL_DF_STEEL_WALL_START &&
8319 element <= EL_DF_STEEL_WALL_END ?
8322 element >= EL_DF_WOODEN_WALL_START &&
8323 element <= EL_DF_WOODEN_WALL_END ?
8329 int map_mm_wall_element_editor(int element)
8333 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8334 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8335 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8336 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8337 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8338 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8340 default: return element;
8344 int get_next_element(int element)
8348 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8349 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8350 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8351 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8352 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8353 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8354 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8355 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8356 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8357 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8358 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8360 default: return element;
8364 int el2img_mm(int element_mm)
8366 return el2img(map_element_MM_to_RND(element_mm));
8369 int el_act2img_mm(int element_mm, int action)
8371 return el_act2img(map_element_MM_to_RND(element_mm), action);
8374 int el_act_dir2img(int element, int action, int direction)
8376 element = GFX_ELEMENT(element);
8377 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8379 // direction_graphic[][] == graphic[] for undefined direction graphics
8380 return element_info[element].direction_graphic[action][direction];
8383 static int el_act_dir2crm(int element, int action, int direction)
8385 element = GFX_ELEMENT(element);
8386 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8388 // direction_graphic[][] == graphic[] for undefined direction graphics
8389 return element_info[element].direction_crumbled[action][direction];
8392 int el_act2img(int element, int action)
8394 element = GFX_ELEMENT(element);
8396 return element_info[element].graphic[action];
8399 int el_act2crm(int element, int action)
8401 element = GFX_ELEMENT(element);
8403 return element_info[element].crumbled[action];
8406 int el_dir2img(int element, int direction)
8408 element = GFX_ELEMENT(element);
8410 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8413 int el2baseimg(int element)
8415 return element_info[element].graphic[ACTION_DEFAULT];
8418 int el2img(int element)
8420 element = GFX_ELEMENT(element);
8422 return element_info[element].graphic[ACTION_DEFAULT];
8425 int el2edimg(int element)
8427 element = GFX_ELEMENT(element);
8429 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8432 int el2preimg(int element)
8434 element = GFX_ELEMENT(element);
8436 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8439 int el2panelimg(int element)
8441 element = GFX_ELEMENT(element);
8443 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8446 int font2baseimg(int font_nr)
8448 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8451 int getBeltNrFromBeltElement(int element)
8453 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8454 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8455 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8458 int getBeltNrFromBeltActiveElement(int element)
8460 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8461 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8462 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8465 int getBeltNrFromBeltSwitchElement(int element)
8467 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8468 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8469 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8472 int getBeltDirNrFromBeltElement(int element)
8474 static int belt_base_element[4] =
8476 EL_CONVEYOR_BELT_1_LEFT,
8477 EL_CONVEYOR_BELT_2_LEFT,
8478 EL_CONVEYOR_BELT_3_LEFT,
8479 EL_CONVEYOR_BELT_4_LEFT
8482 int belt_nr = getBeltNrFromBeltElement(element);
8483 int belt_dir_nr = element - belt_base_element[belt_nr];
8485 return (belt_dir_nr % 3);
8488 int getBeltDirNrFromBeltSwitchElement(int element)
8490 static int belt_base_element[4] =
8492 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8493 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8494 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8495 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8498 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8499 int belt_dir_nr = element - belt_base_element[belt_nr];
8501 return (belt_dir_nr % 3);
8504 int getBeltDirFromBeltElement(int element)
8506 static int belt_move_dir[3] =
8513 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8515 return belt_move_dir[belt_dir_nr];
8518 int getBeltDirFromBeltSwitchElement(int element)
8520 static int belt_move_dir[3] =
8527 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8529 return belt_move_dir[belt_dir_nr];
8532 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8534 static int belt_base_element[4] =
8536 EL_CONVEYOR_BELT_1_LEFT,
8537 EL_CONVEYOR_BELT_2_LEFT,
8538 EL_CONVEYOR_BELT_3_LEFT,
8539 EL_CONVEYOR_BELT_4_LEFT
8542 return belt_base_element[belt_nr] + belt_dir_nr;
8545 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8547 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8549 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8552 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8554 static int belt_base_element[4] =
8556 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8557 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8558 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8559 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8562 return belt_base_element[belt_nr] + belt_dir_nr;
8565 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8567 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8569 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8572 boolean swapTiles_EM(boolean is_pre_emc_cave)
8574 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8577 boolean getTeamMode_EM(void)
8579 return game.team_mode || network_playing;
8582 boolean isActivePlayer_EM(int player_nr)
8584 return stored_player[player_nr].active;
8587 unsigned int InitRND(int seed)
8589 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8590 return InitEngineRandom_EM(seed);
8591 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8592 return InitEngineRandom_SP(seed);
8593 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8594 return InitEngineRandom_MM(seed);
8596 return InitEngineRandom_RND(seed);
8599 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8600 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8602 static int get_effective_element_EM(int tile, int frame_em)
8604 int element = object_mapping[tile].element_rnd;
8605 int action = object_mapping[tile].action;
8606 boolean is_backside = object_mapping[tile].is_backside;
8607 boolean action_removing = (action == ACTION_DIGGING ||
8608 action == ACTION_SNAPPING ||
8609 action == ACTION_COLLECTING);
8617 return (frame_em > 5 ? EL_EMPTY : element);
8623 else // frame_em == 7
8634 case Ydiamond_stone:
8638 case Xdrip_stretchB:
8654 case Ymagnify_blank:
8657 case Xsand_stonein_1:
8658 case Xsand_stonein_2:
8659 case Xsand_stonein_3:
8660 case Xsand_stonein_4:
8664 return (is_backside || action_removing ? EL_EMPTY : element);
8669 static boolean check_linear_animation_EM(int tile)
8673 case Xsand_stonesand_1:
8674 case Xsand_stonesand_quickout_1:
8675 case Xsand_sandstone_1:
8676 case Xsand_stonein_1:
8677 case Xsand_stoneout_1:
8705 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8706 boolean has_crumbled_graphics,
8707 int crumbled, int sync_frame)
8709 // if element can be crumbled, but certain action graphics are just empty
8710 // space (like instantly snapping sand to empty space in 1 frame), do not
8711 // treat these empty space graphics as crumbled graphics in EMC engine
8712 if (crumbled == IMG_EMPTY_SPACE)
8713 has_crumbled_graphics = FALSE;
8715 if (has_crumbled_graphics)
8717 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8718 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8719 g_crumbled->anim_delay,
8720 g_crumbled->anim_mode,
8721 g_crumbled->anim_start_frame,
8724 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8725 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8727 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8728 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8730 g_em->has_crumbled_graphics = TRUE;
8734 g_em->crumbled_bitmap = NULL;
8735 g_em->crumbled_src_x = 0;
8736 g_em->crumbled_src_y = 0;
8737 g_em->crumbled_border_size = 0;
8738 g_em->crumbled_tile_size = 0;
8740 g_em->has_crumbled_graphics = FALSE;
8745 void ResetGfxAnimation_EM(int x, int y, int tile)
8751 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8752 int tile, int frame_em, int x, int y)
8754 int action = object_mapping[tile].action;
8755 int direction = object_mapping[tile].direction;
8756 int effective_element = get_effective_element_EM(tile, frame_em);
8757 int graphic = (direction == MV_NONE ?
8758 el_act2img(effective_element, action) :
8759 el_act_dir2img(effective_element, action, direction));
8760 struct GraphicInfo *g = &graphic_info[graphic];
8762 boolean action_removing = (action == ACTION_DIGGING ||
8763 action == ACTION_SNAPPING ||
8764 action == ACTION_COLLECTING);
8765 boolean action_moving = (action == ACTION_FALLING ||
8766 action == ACTION_MOVING ||
8767 action == ACTION_PUSHING ||
8768 action == ACTION_EATING ||
8769 action == ACTION_FILLING ||
8770 action == ACTION_EMPTYING);
8771 boolean action_falling = (action == ACTION_FALLING ||
8772 action == ACTION_FILLING ||
8773 action == ACTION_EMPTYING);
8775 // special case: graphic uses "2nd movement tile" and has defined
8776 // 7 frames for movement animation (or less) => use default graphic
8777 // for last (8th) frame which ends the movement animation
8778 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8780 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8781 graphic = (direction == MV_NONE ?
8782 el_act2img(effective_element, action) :
8783 el_act_dir2img(effective_element, action, direction));
8785 g = &graphic_info[graphic];
8788 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8792 else if (action_moving)
8794 boolean is_backside = object_mapping[tile].is_backside;
8798 int direction = object_mapping[tile].direction;
8799 int move_dir = (action_falling ? MV_DOWN : direction);
8804 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8805 if (g->double_movement && frame_em == 0)
8809 if (move_dir == MV_LEFT)
8810 GfxFrame[x - 1][y] = GfxFrame[x][y];
8811 else if (move_dir == MV_RIGHT)
8812 GfxFrame[x + 1][y] = GfxFrame[x][y];
8813 else if (move_dir == MV_UP)
8814 GfxFrame[x][y - 1] = GfxFrame[x][y];
8815 else if (move_dir == MV_DOWN)
8816 GfxFrame[x][y + 1] = GfxFrame[x][y];
8823 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8824 if (tile == Xsand_stonesand_quickout_1 ||
8825 tile == Xsand_stonesand_quickout_2)
8829 if (graphic_info[graphic].anim_global_sync)
8830 sync_frame = FrameCounter;
8831 else if (graphic_info[graphic].anim_global_anim_sync)
8832 sync_frame = getGlobalAnimSyncFrame();
8833 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8834 sync_frame = GfxFrame[x][y];
8836 sync_frame = 0; // playfield border (pseudo steel)
8838 SetRandomAnimationValue(x, y);
8840 int frame = getAnimationFrame(g->anim_frames,
8843 g->anim_start_frame,
8846 g_em->unique_identifier =
8847 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8850 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8851 int tile, int frame_em, int x, int y)
8853 int action = object_mapping[tile].action;
8854 int direction = object_mapping[tile].direction;
8855 boolean is_backside = object_mapping[tile].is_backside;
8856 int effective_element = get_effective_element_EM(tile, frame_em);
8857 int effective_action = action;
8858 int graphic = (direction == MV_NONE ?
8859 el_act2img(effective_element, effective_action) :
8860 el_act_dir2img(effective_element, effective_action,
8862 int crumbled = (direction == MV_NONE ?
8863 el_act2crm(effective_element, effective_action) :
8864 el_act_dir2crm(effective_element, effective_action,
8866 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8867 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8868 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8869 struct GraphicInfo *g = &graphic_info[graphic];
8872 // special case: graphic uses "2nd movement tile" and has defined
8873 // 7 frames for movement animation (or less) => use default graphic
8874 // for last (8th) frame which ends the movement animation
8875 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8877 effective_action = ACTION_DEFAULT;
8878 graphic = (direction == MV_NONE ?
8879 el_act2img(effective_element, effective_action) :
8880 el_act_dir2img(effective_element, effective_action,
8882 crumbled = (direction == MV_NONE ?
8883 el_act2crm(effective_element, effective_action) :
8884 el_act_dir2crm(effective_element, effective_action,
8887 g = &graphic_info[graphic];
8890 if (graphic_info[graphic].anim_global_sync)
8891 sync_frame = FrameCounter;
8892 else if (graphic_info[graphic].anim_global_anim_sync)
8893 sync_frame = getGlobalAnimSyncFrame();
8894 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8895 sync_frame = GfxFrame[x][y];
8897 sync_frame = 0; // playfield border (pseudo steel)
8899 SetRandomAnimationValue(x, y);
8901 int frame = getAnimationFrame(g->anim_frames,
8904 g->anim_start_frame,
8907 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8908 g->double_movement && is_backside);
8910 // (updating the "crumbled" graphic definitions is probably not really needed,
8911 // as animations for crumbled graphics can't be longer than one EMC cycle)
8912 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8916 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8917 int player_nr, int anim, int frame_em)
8919 int element = player_mapping[player_nr][anim].element_rnd;
8920 int action = player_mapping[player_nr][anim].action;
8921 int direction = player_mapping[player_nr][anim].direction;
8922 int graphic = (direction == MV_NONE ?
8923 el_act2img(element, action) :
8924 el_act_dir2img(element, action, direction));
8925 struct GraphicInfo *g = &graphic_info[graphic];
8928 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8930 stored_player[player_nr].StepFrame = frame_em;
8932 sync_frame = stored_player[player_nr].Frame;
8934 int frame = getAnimationFrame(g->anim_frames,
8937 g->anim_start_frame,
8940 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8941 &g_em->src_x, &g_em->src_y, FALSE);
8944 void InitGraphicInfo_EM(void)
8948 // always start with reliable default values
8949 for (i = 0; i < GAME_TILE_MAX; i++)
8951 object_mapping[i].element_rnd = EL_UNKNOWN;
8952 object_mapping[i].is_backside = FALSE;
8953 object_mapping[i].action = ACTION_DEFAULT;
8954 object_mapping[i].direction = MV_NONE;
8957 // always start with reliable default values
8958 for (p = 0; p < MAX_PLAYERS; p++)
8960 for (i = 0; i < PLY_MAX; i++)
8962 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8963 player_mapping[p][i].action = ACTION_DEFAULT;
8964 player_mapping[p][i].direction = MV_NONE;
8968 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8970 int e = em_object_mapping_list[i].element_em;
8972 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8973 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8975 if (em_object_mapping_list[i].action != -1)
8976 object_mapping[e].action = em_object_mapping_list[i].action;
8978 if (em_object_mapping_list[i].direction != -1)
8979 object_mapping[e].direction =
8980 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8983 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8985 int a = em_player_mapping_list[i].action_em;
8986 int p = em_player_mapping_list[i].player_nr;
8988 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8990 if (em_player_mapping_list[i].action != -1)
8991 player_mapping[p][a].action = em_player_mapping_list[i].action;
8993 if (em_player_mapping_list[i].direction != -1)
8994 player_mapping[p][a].direction =
8995 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8998 for (i = 0; i < GAME_TILE_MAX; i++)
9000 int element = object_mapping[i].element_rnd;
9001 int action = object_mapping[i].action;
9002 int direction = object_mapping[i].direction;
9003 boolean is_backside = object_mapping[i].is_backside;
9004 boolean action_exploding = ((action == ACTION_EXPLODING ||
9005 action == ACTION_SMASHED_BY_ROCK ||
9006 action == ACTION_SMASHED_BY_SPRING) &&
9007 element != EL_DIAMOND);
9008 boolean action_active = (action == ACTION_ACTIVE);
9009 boolean action_other = (action == ACTION_OTHER);
9011 for (j = 0; j < 8; j++)
9013 int effective_element = get_effective_element_EM(i, j);
9014 int effective_action = (j < 7 ? action :
9015 i == Xdrip_stretch ? action :
9016 i == Xdrip_stretchB ? action :
9017 i == Ydrip_1_s ? action :
9018 i == Ydrip_1_sB ? action :
9019 i == Yball_1 ? action :
9020 i == Xball_2 ? action :
9021 i == Yball_2 ? action :
9022 i == Yball_blank ? action :
9023 i == Ykey_1_blank ? action :
9024 i == Ykey_2_blank ? action :
9025 i == Ykey_3_blank ? action :
9026 i == Ykey_4_blank ? action :
9027 i == Ykey_5_blank ? action :
9028 i == Ykey_6_blank ? action :
9029 i == Ykey_7_blank ? action :
9030 i == Ykey_8_blank ? action :
9031 i == Ylenses_blank ? action :
9032 i == Ymagnify_blank ? action :
9033 i == Ygrass_blank ? action :
9034 i == Ydirt_blank ? action :
9035 i == Xsand_stonein_1 ? action :
9036 i == Xsand_stonein_2 ? action :
9037 i == Xsand_stonein_3 ? action :
9038 i == Xsand_stonein_4 ? action :
9039 i == Xsand_stoneout_1 ? action :
9040 i == Xsand_stoneout_2 ? action :
9041 i == Xboom_android ? ACTION_EXPLODING :
9042 action_exploding ? ACTION_EXPLODING :
9043 action_active ? action :
9044 action_other ? action :
9046 int graphic = (el_act_dir2img(effective_element, effective_action,
9048 int crumbled = (el_act_dir2crm(effective_element, effective_action,
9050 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
9051 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
9052 boolean has_action_graphics = (graphic != base_graphic);
9053 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9054 struct GraphicInfo *g = &graphic_info[graphic];
9055 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9058 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9059 boolean special_animation = (action != ACTION_DEFAULT &&
9060 g->anim_frames == 3 &&
9061 g->anim_delay == 2 &&
9062 g->anim_mode & ANIM_LINEAR);
9063 int sync_frame = (i == Xdrip_stretch ? 7 :
9064 i == Xdrip_stretchB ? 7 :
9065 i == Ydrip_2_s ? j + 8 :
9066 i == Ydrip_2_sB ? j + 8 :
9075 i == Xfake_acid_1 ? 0 :
9076 i == Xfake_acid_2 ? 10 :
9077 i == Xfake_acid_3 ? 20 :
9078 i == Xfake_acid_4 ? 30 :
9079 i == Xfake_acid_5 ? 40 :
9080 i == Xfake_acid_6 ? 50 :
9081 i == Xfake_acid_7 ? 60 :
9082 i == Xfake_acid_8 ? 70 :
9083 i == Xfake_acid_1_player ? 0 :
9084 i == Xfake_acid_2_player ? 10 :
9085 i == Xfake_acid_3_player ? 20 :
9086 i == Xfake_acid_4_player ? 30 :
9087 i == Xfake_acid_5_player ? 40 :
9088 i == Xfake_acid_6_player ? 50 :
9089 i == Xfake_acid_7_player ? 60 :
9090 i == Xfake_acid_8_player ? 70 :
9092 i == Yball_2 ? j + 8 :
9093 i == Yball_blank ? j + 1 :
9094 i == Ykey_1_blank ? j + 1 :
9095 i == Ykey_2_blank ? j + 1 :
9096 i == Ykey_3_blank ? j + 1 :
9097 i == Ykey_4_blank ? j + 1 :
9098 i == Ykey_5_blank ? j + 1 :
9099 i == Ykey_6_blank ? j + 1 :
9100 i == Ykey_7_blank ? j + 1 :
9101 i == Ykey_8_blank ? j + 1 :
9102 i == Ylenses_blank ? j + 1 :
9103 i == Ymagnify_blank ? j + 1 :
9104 i == Ygrass_blank ? j + 1 :
9105 i == Ydirt_blank ? j + 1 :
9106 i == Xamoeba_1 ? 0 :
9107 i == Xamoeba_2 ? 1 :
9108 i == Xamoeba_3 ? 2 :
9109 i == Xamoeba_4 ? 3 :
9110 i == Xamoeba_5 ? 0 :
9111 i == Xamoeba_6 ? 1 :
9112 i == Xamoeba_7 ? 2 :
9113 i == Xamoeba_8 ? 3 :
9114 i == Xexit_2 ? j + 8 :
9115 i == Xexit_3 ? j + 16 :
9116 i == Xdynamite_1 ? 0 :
9117 i == Xdynamite_2 ? 8 :
9118 i == Xdynamite_3 ? 16 :
9119 i == Xdynamite_4 ? 24 :
9120 i == Xsand_stonein_1 ? j + 1 :
9121 i == Xsand_stonein_2 ? j + 9 :
9122 i == Xsand_stonein_3 ? j + 17 :
9123 i == Xsand_stonein_4 ? j + 25 :
9124 i == Xsand_stoneout_1 && j == 0 ? 0 :
9125 i == Xsand_stoneout_1 && j == 1 ? 0 :
9126 i == Xsand_stoneout_1 && j == 2 ? 1 :
9127 i == Xsand_stoneout_1 && j == 3 ? 2 :
9128 i == Xsand_stoneout_1 && j == 4 ? 2 :
9129 i == Xsand_stoneout_1 && j == 5 ? 3 :
9130 i == Xsand_stoneout_1 && j == 6 ? 4 :
9131 i == Xsand_stoneout_1 && j == 7 ? 4 :
9132 i == Xsand_stoneout_2 && j == 0 ? 5 :
9133 i == Xsand_stoneout_2 && j == 1 ? 6 :
9134 i == Xsand_stoneout_2 && j == 2 ? 7 :
9135 i == Xsand_stoneout_2 && j == 3 ? 8 :
9136 i == Xsand_stoneout_2 && j == 4 ? 9 :
9137 i == Xsand_stoneout_2 && j == 5 ? 11 :
9138 i == Xsand_stoneout_2 && j == 6 ? 13 :
9139 i == Xsand_stoneout_2 && j == 7 ? 15 :
9140 i == Xboom_bug && j == 1 ? 2 :
9141 i == Xboom_bug && j == 2 ? 2 :
9142 i == Xboom_bug && j == 3 ? 4 :
9143 i == Xboom_bug && j == 4 ? 4 :
9144 i == Xboom_bug && j == 5 ? 2 :
9145 i == Xboom_bug && j == 6 ? 2 :
9146 i == Xboom_bug && j == 7 ? 0 :
9147 i == Xboom_tank && j == 1 ? 2 :
9148 i == Xboom_tank && j == 2 ? 2 :
9149 i == Xboom_tank && j == 3 ? 4 :
9150 i == Xboom_tank && j == 4 ? 4 :
9151 i == Xboom_tank && j == 5 ? 2 :
9152 i == Xboom_tank && j == 6 ? 2 :
9153 i == Xboom_tank && j == 7 ? 0 :
9154 i == Xboom_android && j == 7 ? 6 :
9155 i == Xboom_1 && j == 1 ? 2 :
9156 i == Xboom_1 && j == 2 ? 2 :
9157 i == Xboom_1 && j == 3 ? 4 :
9158 i == Xboom_1 && j == 4 ? 4 :
9159 i == Xboom_1 && j == 5 ? 6 :
9160 i == Xboom_1 && j == 6 ? 6 :
9161 i == Xboom_1 && j == 7 ? 8 :
9162 i == Xboom_2 && j == 0 ? 8 :
9163 i == Xboom_2 && j == 1 ? 8 :
9164 i == Xboom_2 && j == 2 ? 10 :
9165 i == Xboom_2 && j == 3 ? 10 :
9166 i == Xboom_2 && j == 4 ? 10 :
9167 i == Xboom_2 && j == 5 ? 12 :
9168 i == Xboom_2 && j == 6 ? 12 :
9169 i == Xboom_2 && j == 7 ? 12 :
9170 special_animation && j == 4 ? 3 :
9171 effective_action != action ? 0 :
9173 int frame = getAnimationFrame(g->anim_frames,
9176 g->anim_start_frame,
9179 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9180 g->double_movement && is_backside);
9182 g_em->bitmap = src_bitmap;
9183 g_em->src_x = src_x;
9184 g_em->src_y = src_y;
9185 g_em->src_offset_x = 0;
9186 g_em->src_offset_y = 0;
9187 g_em->dst_offset_x = 0;
9188 g_em->dst_offset_y = 0;
9189 g_em->width = TILEX;
9190 g_em->height = TILEY;
9192 g_em->preserve_background = FALSE;
9194 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9197 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9198 effective_action == ACTION_MOVING ||
9199 effective_action == ACTION_PUSHING ||
9200 effective_action == ACTION_EATING)) ||
9201 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9202 effective_action == ACTION_EMPTYING)))
9205 (effective_action == ACTION_FALLING ||
9206 effective_action == ACTION_FILLING ||
9207 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9208 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9209 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9210 int num_steps = (i == Ydrip_1_s ? 16 :
9211 i == Ydrip_1_sB ? 16 :
9212 i == Ydrip_2_s ? 16 :
9213 i == Ydrip_2_sB ? 16 :
9214 i == Xsand_stonein_1 ? 32 :
9215 i == Xsand_stonein_2 ? 32 :
9216 i == Xsand_stonein_3 ? 32 :
9217 i == Xsand_stonein_4 ? 32 :
9218 i == Xsand_stoneout_1 ? 16 :
9219 i == Xsand_stoneout_2 ? 16 : 8);
9220 int cx = ABS(dx) * (TILEX / num_steps);
9221 int cy = ABS(dy) * (TILEY / num_steps);
9222 int step_frame = (i == Ydrip_2_s ? j + 8 :
9223 i == Ydrip_2_sB ? j + 8 :
9224 i == Xsand_stonein_2 ? j + 8 :
9225 i == Xsand_stonein_3 ? j + 16 :
9226 i == Xsand_stonein_4 ? j + 24 :
9227 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9228 int step = (is_backside ? step_frame : num_steps - step_frame);
9230 if (is_backside) // tile where movement starts
9232 if (dx < 0 || dy < 0)
9234 g_em->src_offset_x = cx * step;
9235 g_em->src_offset_y = cy * step;
9239 g_em->dst_offset_x = cx * step;
9240 g_em->dst_offset_y = cy * step;
9243 else // tile where movement ends
9245 if (dx < 0 || dy < 0)
9247 g_em->dst_offset_x = cx * step;
9248 g_em->dst_offset_y = cy * step;
9252 g_em->src_offset_x = cx * step;
9253 g_em->src_offset_y = cy * step;
9257 g_em->width = TILEX - cx * step;
9258 g_em->height = TILEY - cy * step;
9261 // create unique graphic identifier to decide if tile must be redrawn
9262 /* bit 31 - 16 (16 bit): EM style graphic
9263 bit 15 - 12 ( 4 bit): EM style frame
9264 bit 11 - 6 ( 6 bit): graphic width
9265 bit 5 - 0 ( 6 bit): graphic height */
9266 g_em->unique_identifier =
9267 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9271 for (i = 0; i < GAME_TILE_MAX; i++)
9273 for (j = 0; j < 8; j++)
9275 int element = object_mapping[i].element_rnd;
9276 int action = object_mapping[i].action;
9277 int direction = object_mapping[i].direction;
9278 boolean is_backside = object_mapping[i].is_backside;
9279 int graphic_action = el_act_dir2img(element, action, direction);
9280 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9282 if ((action == ACTION_SMASHED_BY_ROCK ||
9283 action == ACTION_SMASHED_BY_SPRING ||
9284 action == ACTION_EATING) &&
9285 graphic_action == graphic_default)
9287 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9288 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9289 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9290 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9293 // no separate animation for "smashed by rock" -- use rock instead
9294 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9295 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9297 g_em->bitmap = g_xx->bitmap;
9298 g_em->src_x = g_xx->src_x;
9299 g_em->src_y = g_xx->src_y;
9300 g_em->src_offset_x = g_xx->src_offset_x;
9301 g_em->src_offset_y = g_xx->src_offset_y;
9302 g_em->dst_offset_x = g_xx->dst_offset_x;
9303 g_em->dst_offset_y = g_xx->dst_offset_y;
9304 g_em->width = g_xx->width;
9305 g_em->height = g_xx->height;
9306 g_em->unique_identifier = g_xx->unique_identifier;
9309 g_em->preserve_background = TRUE;
9314 for (p = 0; p < MAX_PLAYERS; p++)
9316 for (i = 0; i < PLY_MAX; i++)
9318 int element = player_mapping[p][i].element_rnd;
9319 int action = player_mapping[p][i].action;
9320 int direction = player_mapping[p][i].direction;
9322 for (j = 0; j < 8; j++)
9324 int effective_element = element;
9325 int effective_action = action;
9326 int graphic = (direction == MV_NONE ?
9327 el_act2img(effective_element, effective_action) :
9328 el_act_dir2img(effective_element, effective_action,
9330 struct GraphicInfo *g = &graphic_info[graphic];
9331 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9335 int frame = getAnimationFrame(g->anim_frames,
9338 g->anim_start_frame,
9341 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9343 g_em->bitmap = src_bitmap;
9344 g_em->src_x = src_x;
9345 g_em->src_y = src_y;
9346 g_em->src_offset_x = 0;
9347 g_em->src_offset_y = 0;
9348 g_em->dst_offset_x = 0;
9349 g_em->dst_offset_y = 0;
9350 g_em->width = TILEX;
9351 g_em->height = TILEY;
9357 static void CheckSaveEngineSnapshot_EM(int frame,
9358 boolean any_player_moving,
9359 boolean any_player_snapping,
9360 boolean any_player_dropping)
9362 if (frame == 7 && !any_player_dropping)
9364 if (!local_player->was_waiting)
9366 if (!CheckSaveEngineSnapshotToList())
9369 local_player->was_waiting = TRUE;
9372 else if (any_player_moving || any_player_snapping || any_player_dropping)
9374 local_player->was_waiting = FALSE;
9378 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9379 boolean murphy_is_dropping)
9381 if (murphy_is_waiting)
9383 if (!local_player->was_waiting)
9385 if (!CheckSaveEngineSnapshotToList())
9388 local_player->was_waiting = TRUE;
9393 local_player->was_waiting = FALSE;
9397 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9398 boolean button_released)
9400 if (button_released)
9402 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9403 CheckSaveEngineSnapshotToList();
9405 else if (element_clicked)
9407 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9408 CheckSaveEngineSnapshotToList();
9410 game.snapshot.changed_action = TRUE;
9414 boolean CheckSingleStepMode_EM(int frame,
9415 boolean any_player_moving,
9416 boolean any_player_snapping,
9417 boolean any_player_dropping)
9419 if (tape.single_step && tape.recording && !tape.pausing)
9420 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9421 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9423 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9424 any_player_snapping, any_player_dropping);
9426 return tape.pausing;
9429 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9430 boolean murphy_is_dropping)
9432 boolean murphy_starts_dropping = FALSE;
9435 for (i = 0; i < MAX_PLAYERS; i++)
9436 if (stored_player[i].force_dropping)
9437 murphy_starts_dropping = TRUE;
9439 if (tape.single_step && tape.recording && !tape.pausing)
9440 if (murphy_is_waiting && !murphy_starts_dropping)
9441 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9443 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9446 void CheckSingleStepMode_MM(boolean element_clicked,
9447 boolean button_released)
9449 if (tape.single_step && tape.recording && !tape.pausing)
9450 if (button_released)
9451 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9453 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9456 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9457 int graphic, int sync_frame)
9459 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9461 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9464 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9466 return (IS_NEXT_FRAME(sync_frame, graphic));
9469 int getGraphicInfo_Delay(int graphic)
9471 return graphic_info[graphic].anim_delay;
9474 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9476 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9479 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9485 void PlayMenuSoundExt(int sound)
9487 if (sound == SND_UNDEFINED)
9490 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9491 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9494 if (IS_LOOP_SOUND(sound))
9495 PlaySoundLoop(sound);
9500 void PlayMenuSound(void)
9502 PlayMenuSoundExt(menu.sound[game_status]);
9505 void PlayMenuSoundStereo(int sound, int stereo_position)
9507 if (sound == SND_UNDEFINED)
9510 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9511 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9514 if (IS_LOOP_SOUND(sound))
9515 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9517 PlaySoundStereo(sound, stereo_position);
9520 void PlayMenuSoundIfLoopExt(int sound)
9522 if (sound == SND_UNDEFINED)
9525 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9526 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9529 if (IS_LOOP_SOUND(sound))
9530 PlaySoundLoop(sound);
9533 void PlayMenuSoundIfLoop(void)
9535 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9538 void PlayMenuMusicExt(int music)
9540 if (music == MUS_UNDEFINED)
9543 if (!setup.sound_music)
9546 if (IS_LOOP_MUSIC(music))
9547 PlayMusicLoop(music);
9552 void PlayMenuMusic(void)
9554 char *curr_music = getCurrentlyPlayingMusicFilename();
9555 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9557 if (!strEqual(curr_music, next_music))
9558 PlayMenuMusicExt(menu.music[game_status]);
9561 void PlayMenuSoundsAndMusic(void)
9567 static void FadeMenuSounds(void)
9572 static void FadeMenuMusic(void)
9574 char *curr_music = getCurrentlyPlayingMusicFilename();
9575 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9577 if (!strEqual(curr_music, next_music))
9581 void FadeMenuSoundsAndMusic(void)
9587 void PlaySoundActivating(void)
9590 PlaySound(SND_MENU_ITEM_ACTIVATING);
9594 void PlaySoundSelecting(void)
9597 PlaySound(SND_MENU_ITEM_SELECTING);
9601 void ToggleFullscreenIfNeeded(void)
9603 // if setup and video fullscreen state are already matching, nothing do do
9604 if (setup.fullscreen == video.fullscreen_enabled ||
9605 !video.fullscreen_available)
9608 SDLSetWindowFullscreen(setup.fullscreen);
9610 // set setup value according to successfully changed fullscreen mode
9611 setup.fullscreen = video.fullscreen_enabled;
9614 void ChangeWindowScalingIfNeeded(void)
9616 // if setup and video window scaling are already matching, nothing do do
9617 if (setup.window_scaling_percent == video.window_scaling_percent ||
9618 video.fullscreen_enabled)
9621 SDLSetWindowScaling(setup.window_scaling_percent);
9623 // set setup value according to successfully changed window scaling
9624 setup.window_scaling_percent = video.window_scaling_percent;
9627 void ChangeVsyncModeIfNeeded(void)
9629 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9630 int video_vsync_mode = video.vsync_mode;
9632 // if setup and video vsync mode are already matching, nothing do do
9633 if (setup_vsync_mode == video_vsync_mode)
9636 // if renderer is using OpenGL, vsync mode can directly be changed
9637 SDLSetScreenVsyncMode(setup.vsync_mode);
9639 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9640 if (video.vsync_mode == video_vsync_mode)
9642 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9644 // save backbuffer content which gets lost when re-creating screen
9645 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9647 // force re-creating screen and renderer to set new vsync mode
9648 video.fullscreen_enabled = !setup.fullscreen;
9650 // when creating new renderer, destroy textures linked to old renderer
9651 FreeAllImageTextures(); // needs old renderer to free the textures
9653 // re-create screen and renderer (including change of vsync mode)
9654 ChangeVideoModeIfNeeded(setup.fullscreen);
9656 // set setup value according to successfully changed fullscreen mode
9657 setup.fullscreen = video.fullscreen_enabled;
9659 // restore backbuffer content from temporary backbuffer backup bitmap
9660 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9661 FreeBitmap(tmp_backbuffer);
9663 // update visible window/screen
9664 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9666 // when changing vsync mode, re-create textures for new renderer
9667 InitImageTextures();
9670 // set setup value according to successfully changed vsync mode
9671 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9674 static void JoinRectangles(int *x, int *y, int *width, int *height,
9675 int x2, int y2, int width2, int height2)
9677 // do not join with "off-screen" rectangle
9678 if (x2 == -1 || y2 == -1)
9683 *width = MAX(*width, width2);
9684 *height = MAX(*height, height2);
9687 void SetAnimStatus(int anim_status_new)
9689 if (anim_status_new == GAME_MODE_MAIN)
9690 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9691 else if (anim_status_new == GAME_MODE_NAMES)
9692 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9693 else if (anim_status_new == GAME_MODE_SCORES)
9694 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9696 global.anim_status_next = anim_status_new;
9698 // directly set screen modes that are entered without fading
9699 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9700 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9701 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9702 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9703 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9704 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9705 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9706 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9707 global.anim_status = global.anim_status_next;
9710 void SetGameStatus(int game_status_new)
9712 if (game_status_new != game_status)
9713 game_status_last_screen = game_status;
9715 game_status = game_status_new;
9717 SetAnimStatus(game_status_new);
9720 void SetFontStatus(int game_status_new)
9722 static int last_game_status = -1;
9724 if (game_status_new != -1)
9726 // set game status for font use after storing last game status
9727 last_game_status = game_status;
9728 game_status = game_status_new;
9732 // reset game status after font use from last stored game status
9733 game_status = last_game_status;
9737 void ResetFontStatus(void)
9742 void SetLevelSetInfo(char *identifier, int level_nr)
9744 setString(&levelset.identifier, identifier);
9746 levelset.level_nr = level_nr;
9749 boolean CheckIfAllViewportsHaveChanged(void)
9751 // if game status has not changed, viewports have not changed either
9752 if (game_status == game_status_last)
9755 // check if all viewports have changed with current game status
9757 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9758 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9759 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9760 int new_real_sx = vp_playfield->x;
9761 int new_real_sy = vp_playfield->y;
9762 int new_full_sxsize = vp_playfield->width;
9763 int new_full_sysize = vp_playfield->height;
9764 int new_dx = vp_door_1->x;
9765 int new_dy = vp_door_1->y;
9766 int new_dxsize = vp_door_1->width;
9767 int new_dysize = vp_door_1->height;
9768 int new_vx = vp_door_2->x;
9769 int new_vy = vp_door_2->y;
9770 int new_vxsize = vp_door_2->width;
9771 int new_vysize = vp_door_2->height;
9773 boolean playfield_viewport_has_changed =
9774 (new_real_sx != REAL_SX ||
9775 new_real_sy != REAL_SY ||
9776 new_full_sxsize != FULL_SXSIZE ||
9777 new_full_sysize != FULL_SYSIZE);
9779 boolean door_1_viewport_has_changed =
9782 new_dxsize != DXSIZE ||
9783 new_dysize != DYSIZE);
9785 boolean door_2_viewport_has_changed =
9788 new_vxsize != VXSIZE ||
9789 new_vysize != VYSIZE ||
9790 game_status_last == GAME_MODE_EDITOR);
9792 return (playfield_viewport_has_changed &&
9793 door_1_viewport_has_changed &&
9794 door_2_viewport_has_changed);
9797 boolean CheckFadeAll(void)
9799 return (CheckIfGlobalBorderHasChanged() ||
9800 CheckIfAllViewportsHaveChanged());
9803 void ChangeViewportPropertiesIfNeeded(void)
9805 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9806 FALSE : setup.small_game_graphics);
9807 int gfx_game_mode = getGlobalGameStatus(game_status);
9808 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9810 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9811 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9812 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9813 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9814 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9815 int new_win_xsize = vp_window->width;
9816 int new_win_ysize = vp_window->height;
9817 int border_left = vp_playfield->border_left;
9818 int border_right = vp_playfield->border_right;
9819 int border_top = vp_playfield->border_top;
9820 int border_bottom = vp_playfield->border_bottom;
9821 int new_sx = vp_playfield->x + border_left;
9822 int new_sy = vp_playfield->y + border_top;
9823 int new_sxsize = vp_playfield->width - border_left - border_right;
9824 int new_sysize = vp_playfield->height - border_top - border_bottom;
9825 int new_real_sx = vp_playfield->x;
9826 int new_real_sy = vp_playfield->y;
9827 int new_full_sxsize = vp_playfield->width;
9828 int new_full_sysize = vp_playfield->height;
9829 int new_dx = vp_door_1->x;
9830 int new_dy = vp_door_1->y;
9831 int new_dxsize = vp_door_1->width;
9832 int new_dysize = vp_door_1->height;
9833 int new_vx = vp_door_2->x;
9834 int new_vy = vp_door_2->y;
9835 int new_vxsize = vp_door_2->width;
9836 int new_vysize = vp_door_2->height;
9837 int new_ex = vp_door_3->x;
9838 int new_ey = vp_door_3->y;
9839 int new_exsize = vp_door_3->width;
9840 int new_eysize = vp_door_3->height;
9841 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9842 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9843 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9844 int new_scr_fieldx = new_sxsize / tilesize;
9845 int new_scr_fieldy = new_sysize / tilesize;
9846 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9847 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9848 boolean init_gfx_buffers = FALSE;
9849 boolean init_video_buffer = FALSE;
9850 boolean init_gadgets_and_anims = FALSE;
9851 boolean init_em_graphics = FALSE;
9853 if (new_win_xsize != WIN_XSIZE ||
9854 new_win_ysize != WIN_YSIZE)
9856 WIN_XSIZE = new_win_xsize;
9857 WIN_YSIZE = new_win_ysize;
9859 init_video_buffer = TRUE;
9860 init_gfx_buffers = TRUE;
9861 init_gadgets_and_anims = TRUE;
9863 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9866 if (new_scr_fieldx != SCR_FIELDX ||
9867 new_scr_fieldy != SCR_FIELDY)
9869 // this always toggles between MAIN and GAME when using small tile size
9871 SCR_FIELDX = new_scr_fieldx;
9872 SCR_FIELDY = new_scr_fieldy;
9874 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9885 new_sxsize != SXSIZE ||
9886 new_sysize != SYSIZE ||
9887 new_dxsize != DXSIZE ||
9888 new_dysize != DYSIZE ||
9889 new_vxsize != VXSIZE ||
9890 new_vysize != VYSIZE ||
9891 new_exsize != EXSIZE ||
9892 new_eysize != EYSIZE ||
9893 new_real_sx != REAL_SX ||
9894 new_real_sy != REAL_SY ||
9895 new_full_sxsize != FULL_SXSIZE ||
9896 new_full_sysize != FULL_SYSIZE ||
9897 new_tilesize_var != TILESIZE_VAR
9900 // ------------------------------------------------------------------------
9901 // determine next fading area for changed viewport definitions
9902 // ------------------------------------------------------------------------
9904 // start with current playfield area (default fading area)
9907 FADE_SXSIZE = FULL_SXSIZE;
9908 FADE_SYSIZE = FULL_SYSIZE;
9910 // add new playfield area if position or size has changed
9911 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9912 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9914 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9915 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9918 // add current and new door 1 area if position or size has changed
9919 if (new_dx != DX || new_dy != DY ||
9920 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9922 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9923 DX, DY, DXSIZE, DYSIZE);
9924 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9925 new_dx, new_dy, new_dxsize, new_dysize);
9928 // add current and new door 2 area if position or size has changed
9929 if (new_vx != VX || new_vy != VY ||
9930 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9932 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9933 VX, VY, VXSIZE, VYSIZE);
9934 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9935 new_vx, new_vy, new_vxsize, new_vysize);
9938 // ------------------------------------------------------------------------
9939 // handle changed tile size
9940 // ------------------------------------------------------------------------
9942 if (new_tilesize_var != TILESIZE_VAR)
9944 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9946 // changing tile size invalidates scroll values of engine snapshots
9947 FreeEngineSnapshotSingle();
9949 // changing tile size requires update of graphic mapping for EM engine
9950 init_em_graphics = TRUE;
9961 SXSIZE = new_sxsize;
9962 SYSIZE = new_sysize;
9963 DXSIZE = new_dxsize;
9964 DYSIZE = new_dysize;
9965 VXSIZE = new_vxsize;
9966 VYSIZE = new_vysize;
9967 EXSIZE = new_exsize;
9968 EYSIZE = new_eysize;
9969 REAL_SX = new_real_sx;
9970 REAL_SY = new_real_sy;
9971 FULL_SXSIZE = new_full_sxsize;
9972 FULL_SYSIZE = new_full_sysize;
9973 TILESIZE_VAR = new_tilesize_var;
9975 init_gfx_buffers = TRUE;
9976 init_gadgets_and_anims = TRUE;
9978 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9979 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9982 if (init_gfx_buffers)
9984 // Debug("tools:viewport", "init_gfx_buffers");
9986 SCR_FIELDX = new_scr_fieldx_buffers;
9987 SCR_FIELDY = new_scr_fieldy_buffers;
9991 SCR_FIELDX = new_scr_fieldx;
9992 SCR_FIELDY = new_scr_fieldy;
9994 SetDrawDeactivationMask(REDRAW_NONE);
9995 SetDrawBackgroundMask(REDRAW_FIELD);
9998 if (init_video_buffer)
10000 // Debug("tools:viewport", "init_video_buffer");
10002 FreeAllImageTextures(); // needs old renderer to free the textures
10004 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
10005 InitImageTextures();
10008 if (init_gadgets_and_anims)
10010 // Debug("tools:viewport", "init_gadgets_and_anims");
10013 InitGlobalAnimations();
10016 if (init_em_graphics)
10018 InitGraphicInfo_EM();
10022 void OpenURL(char *url)
10024 #if SDL_VERSION_ATLEAST(2,0,14)
10027 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
10028 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
10029 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
10033 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
10035 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
10039 // ============================================================================
10041 // ============================================================================
10043 #if defined(PLATFORM_WINDOWS)
10044 /* FILETIME of Jan 1 1970 00:00:00. */
10045 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
10048 * timezone information is stored outside the kernel so tzp isn't used anymore.
10050 * Note: this function is not for Win32 high precision timing purpose. See
10053 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
10055 FILETIME file_time;
10056 SYSTEMTIME system_time;
10057 ULARGE_INTEGER ularge;
10059 GetSystemTime(&system_time);
10060 SystemTimeToFileTime(&system_time, &file_time);
10061 ularge.LowPart = file_time.dwLowDateTime;
10062 ularge.HighPart = file_time.dwHighDateTime;
10064 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10065 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10071 static char *test_init_uuid_random_function_simple(void)
10073 static char seed_text[100];
10074 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10076 sprintf(seed_text, "%d", seed);
10081 static char *test_init_uuid_random_function_better(void)
10083 static char seed_text[100];
10084 struct timeval current_time;
10086 gettimeofday(¤t_time, NULL);
10088 prng_seed_bytes(¤t_time, sizeof(current_time));
10090 sprintf(seed_text, "%ld.%ld",
10091 (long)current_time.tv_sec,
10092 (long)current_time.tv_usec);
10097 #if defined(PLATFORM_WINDOWS)
10098 static char *test_init_uuid_random_function_better_windows(void)
10100 static char seed_text[100];
10101 struct timeval current_time;
10103 gettimeofday_windows(¤t_time, NULL);
10105 prng_seed_bytes(¤t_time, sizeof(current_time));
10107 sprintf(seed_text, "%ld.%ld",
10108 (long)current_time.tv_sec,
10109 (long)current_time.tv_usec);
10115 static unsigned int test_uuid_random_function_simple(int max)
10117 return GetSimpleRandom(max);
10120 static unsigned int test_uuid_random_function_better(int max)
10122 return (max > 0 ? prng_get_uint() % max : 0);
10125 #if defined(PLATFORM_WINDOWS)
10126 #define NUM_UUID_TESTS 3
10128 #define NUM_UUID_TESTS 2
10131 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10133 struct hashtable *hash_seeds =
10134 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10135 struct hashtable *hash_uuids =
10136 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10137 static char message[100];
10140 char *random_name = (nr == 0 ? "simple" : "better");
10141 char *random_type = (always_seed ? "always" : "only once");
10142 char *(*init_random_function)(void) =
10144 test_init_uuid_random_function_simple :
10145 test_init_uuid_random_function_better);
10146 unsigned int (*random_function)(int) =
10148 test_uuid_random_function_simple :
10149 test_uuid_random_function_better);
10152 #if defined(PLATFORM_WINDOWS)
10155 random_name = "windows";
10156 init_random_function = test_init_uuid_random_function_better_windows;
10162 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10163 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10165 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10166 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10167 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10169 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10173 // always initialize random number generator at least once
10174 init_random_function();
10176 unsigned int time_start = SDL_GetTicks();
10178 for (i = 0; i < num_uuids; i++)
10182 char *seed = getStringCopy(init_random_function());
10184 hashtable_remove(hash_seeds, seed);
10185 hashtable_insert(hash_seeds, seed, "1");
10188 char *uuid = getStringCopy(getUUIDExt(random_function));
10190 hashtable_remove(hash_uuids, uuid);
10191 hashtable_insert(hash_uuids, uuid, "1");
10194 int num_unique_seeds = hashtable_count(hash_seeds);
10195 int num_unique_uuids = hashtable_count(hash_uuids);
10197 unsigned int time_needed = SDL_GetTicks() - time_start;
10199 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10201 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10204 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10206 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10207 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10209 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10211 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10213 Request(message, REQ_CONFIRM);
10215 hashtable_destroy(hash_seeds, 0);
10216 hashtable_destroy(hash_uuids, 0);
10219 void TestGeneratingUUIDs(void)
10221 int num_uuids = 1000000;
10224 for (i = 0; i < NUM_UUID_TESTS; i++)
10225 for (j = 0; j < 2; j++)
10226 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10228 CloseAllAndExit(0);