1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 #define DEBUG_FRAME_TIME FALSE
28 // tool button identifiers
29 #define TOOL_CTRL_ID_YES 0
30 #define TOOL_CTRL_ID_NO 1
31 #define TOOL_CTRL_ID_CONFIRM 2
32 #define TOOL_CTRL_ID_PLAYER_1 3
33 #define TOOL_CTRL_ID_PLAYER_2 4
34 #define TOOL_CTRL_ID_PLAYER_3 5
35 #define TOOL_CTRL_ID_PLAYER_4 6
36 #define TOOL_CTRL_ID_TOUCH_YES 7
37 #define TOOL_CTRL_ID_TOUCH_NO 8
38 #define TOOL_CTRL_ID_TOUCH_CONFIRM 9
40 #define NUM_TOOL_BUTTONS 10
42 // constants for number of doors and door parts
44 #define NUM_PANELS NUM_DOORS
45 // #define NUM_PANELS 0
46 #define MAX_PARTS_PER_DOOR 8
47 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
48 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
51 struct DoorPartOrderInfo
57 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
59 struct DoorPartControlInfo
63 struct DoorPartPosInfo *pos;
66 static struct DoorPartControlInfo door_part_controls[] =
70 IMG_GFX_DOOR_1_PART_1,
75 IMG_GFX_DOOR_1_PART_2,
80 IMG_GFX_DOOR_1_PART_3,
85 IMG_GFX_DOOR_1_PART_4,
90 IMG_GFX_DOOR_1_PART_5,
95 IMG_GFX_DOOR_1_PART_6,
100 IMG_GFX_DOOR_1_PART_7,
105 IMG_GFX_DOOR_1_PART_8,
111 IMG_GFX_DOOR_2_PART_1,
116 IMG_GFX_DOOR_2_PART_2,
121 IMG_GFX_DOOR_2_PART_3,
126 IMG_GFX_DOOR_2_PART_4,
131 IMG_GFX_DOOR_2_PART_5,
136 IMG_GFX_DOOR_2_PART_6,
141 IMG_GFX_DOOR_2_PART_7,
146 IMG_GFX_DOOR_2_PART_8,
152 IMG_BACKGROUND_PANEL,
168 static struct XY xy_topdown[] =
177 // forward declaration for internal use
178 static void UnmapToolButtons(void);
179 static void HandleToolButtons(struct GadgetInfo *);
180 static int el_act_dir2crm(int, int, int);
181 static int el_act2crm(int, int);
183 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
184 static int request_gadget_id = -1;
186 static char *print_if_not_empty(int element)
188 static char *s = NULL;
189 char *token_name = element_info[element].token_name;
194 s = checked_malloc(strlen(token_name) + 10 + 1);
196 if (element != EL_EMPTY)
197 sprintf(s, "%d\t['%s']", element, token_name);
199 sprintf(s, "%d", element);
204 int getFieldbufferOffsetX_RND(int dir, int pos)
206 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
207 int dx = (dir & MV_HORIZONTAL ? pos : 0);
208 int dx_var = dx * TILESIZE_VAR / TILESIZE;
211 if (EVEN(SCR_FIELDX))
213 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
214 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
216 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
217 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
219 fx += (dx_var > 0 ? TILEX_VAR : 0);
226 if (full_lev_fieldx <= SCR_FIELDX)
228 if (EVEN(SCR_FIELDX))
229 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
231 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
237 int getFieldbufferOffsetY_RND(int dir, int pos)
239 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
240 int dy = (dir & MV_VERTICAL ? pos : 0);
241 int dy_var = dy * TILESIZE_VAR / TILESIZE;
244 if (EVEN(SCR_FIELDY))
246 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
247 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
249 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
250 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
252 fy += (dy_var > 0 ? TILEY_VAR : 0);
259 if (full_lev_fieldy <= SCR_FIELDY)
261 if (EVEN(SCR_FIELDY))
262 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
264 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
270 static int getLevelFromScreenX_RND(int sx)
272 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
275 int lx = LEVELX((px + dx) / TILESIZE_VAR);
280 static int getLevelFromScreenY_RND(int sy)
282 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
285 int ly = LEVELY((py + dy) / TILESIZE_VAR);
290 static int getLevelFromScreenX_EM(int sx)
292 int level_xsize = level.native_em_level->cav->width;
293 int full_xsize = level_xsize * TILESIZE_VAR;
295 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
297 int fx = getFieldbufferOffsetX_EM();
300 int lx = LEVELX((px + dx) / TILESIZE_VAR);
305 static int getLevelFromScreenY_EM(int sy)
307 int level_ysize = level.native_em_level->cav->height;
308 int full_ysize = level_ysize * TILESIZE_VAR;
310 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
312 int fy = getFieldbufferOffsetY_EM();
315 int ly = LEVELY((py + dy) / TILESIZE_VAR);
320 static int getLevelFromScreenX_SP(int sx)
322 int menBorder = setup.sp_show_border_elements;
323 int level_xsize = level.native_sp_level->width;
324 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
326 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
328 int fx = getFieldbufferOffsetX_SP();
331 int lx = LEVELX((px + dx) / TILESIZE_VAR);
336 static int getLevelFromScreenY_SP(int sy)
338 int menBorder = setup.sp_show_border_elements;
339 int level_ysize = level.native_sp_level->height;
340 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
342 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
344 int fy = getFieldbufferOffsetY_SP();
347 int ly = LEVELY((py + dy) / TILESIZE_VAR);
352 static int getLevelFromScreenX_MM(int sx)
354 int level_xsize = level.native_mm_level->fieldx;
355 int full_xsize = level_xsize * TILESIZE_VAR;
357 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
360 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
365 static int getLevelFromScreenY_MM(int sy)
367 int level_ysize = level.native_mm_level->fieldy;
368 int full_ysize = level_ysize * TILESIZE_VAR;
370 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
373 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
378 int getLevelFromScreenX(int x)
380 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
381 return getLevelFromScreenX_EM(x);
382 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
383 return getLevelFromScreenX_SP(x);
384 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
385 return getLevelFromScreenX_MM(x);
387 return getLevelFromScreenX_RND(x);
390 int getLevelFromScreenY(int y)
392 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
393 return getLevelFromScreenY_EM(y);
394 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
395 return getLevelFromScreenY_SP(y);
396 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
397 return getLevelFromScreenY_MM(y);
399 return getLevelFromScreenY_RND(y);
402 int getScreenFieldSizeX(void)
404 return (tape.playing ? tape.scr_fieldx : SCR_FIELDX);
407 int getScreenFieldSizeY(void)
409 return (tape.playing ? tape.scr_fieldy : SCR_FIELDY);
412 void DumpTile(int x, int y)
419 Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
422 if (!IN_LEV_FIELD(x, y))
424 Info("(not in level field)");
430 token_name = element_info[Tile[x][y]].token_name;
432 Info("Tile: %d\t['%s']", Tile[x][y], token_name);
433 Info("Back: %s", print_if_not_empty(Back[x][y]));
434 Info("Store: %s", print_if_not_empty(Store[x][y]));
435 Info("Store2: %s", print_if_not_empty(Store2[x][y]));
436 Info("StorePlayer: %s", print_if_not_empty(StorePlayer[x][y]));
437 Info("MovPos: %d", MovPos[x][y]);
438 Info("MovDir: %d", MovDir[x][y]);
439 Info("MovDelay: %d", MovDelay[x][y]);
440 Info("ChangeDelay: %d", ChangeDelay[x][y]);
441 Info("CustomValue: %d", CustomValue[x][y]);
442 Info("GfxElement: %d", GfxElement[x][y]);
443 Info("GfxAction: %d", GfxAction[x][y]);
444 Info("GfxFrame: %d [%d]", GfxFrame[x][y], FrameCounter);
445 Info("Player x/y: %d, %d", local_player->jx, local_player->jy);
449 void DumpTileFromScreen(int sx, int sy)
451 int lx = getLevelFromScreenX(sx);
452 int ly = getLevelFromScreenY(sy);
457 void SetDrawtoField(int mode)
459 if (mode == DRAW_TO_FIELDBUFFER)
465 BX2 = SCR_FIELDX + 1;
466 BY2 = SCR_FIELDY + 1;
468 drawto_field = fieldbuffer;
470 else // DRAW_TO_BACKBUFFER
476 BX2 = SCR_FIELDX - 1;
477 BY2 = SCR_FIELDY - 1;
479 drawto_field = backbuffer;
483 int GetDrawtoField(void)
485 return (drawto_field == fieldbuffer ? DRAW_TO_FIELDBUFFER : DRAW_TO_BACKBUFFER);
488 static void RedrawPlayfield_RND(void)
490 if (game.envelope_active)
493 DrawLevel(REDRAW_ALL);
497 void RedrawPlayfield(void)
499 if (game_status != GAME_MODE_PLAYING)
502 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
503 RedrawPlayfield_EM(TRUE);
504 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
505 RedrawPlayfield_SP(TRUE);
506 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
507 RedrawPlayfield_MM();
508 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
509 RedrawPlayfield_RND();
511 BlitScreenToBitmap(backbuffer);
513 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
517 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
520 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
521 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
523 // may happen for "border.draw_masked.*" with undefined "global.border.*"
524 if (src_bitmap == NULL)
527 if (x == -1 && y == -1)
530 if (draw_target == DRAW_TO_SCREEN)
531 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
533 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
536 static void DrawMaskedBorderExt_FIELD(int draw_target)
538 if (global.border_status >= GAME_MODE_MAIN &&
539 global.border_status <= GAME_MODE_PLAYING &&
540 border.draw_masked[global.border_status])
541 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
545 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
547 // when drawing to backbuffer, never draw border over open doors
548 if (draw_target == DRAW_TO_BACKBUFFER &&
549 (GetDoorState() & DOOR_OPEN_1))
552 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
553 (global.border_status != GAME_MODE_EDITOR ||
554 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
555 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
558 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
560 // when drawing to backbuffer, never draw border over open doors
561 if (draw_target == DRAW_TO_BACKBUFFER &&
562 (GetDoorState() & DOOR_OPEN_2))
565 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
566 global.border_status != GAME_MODE_EDITOR)
567 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
570 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
572 // currently not available
575 static void DrawMaskedBorderExt_ALL(int draw_target)
577 DrawMaskedBorderExt_FIELD(draw_target);
578 DrawMaskedBorderExt_DOOR_1(draw_target);
579 DrawMaskedBorderExt_DOOR_2(draw_target);
580 DrawMaskedBorderExt_DOOR_3(draw_target);
583 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
585 // never draw masked screen borders on borderless screens
586 if (global.border_status == GAME_MODE_LOADING ||
587 global.border_status == GAME_MODE_TITLE)
590 if (redraw_mask & REDRAW_ALL)
591 DrawMaskedBorderExt_ALL(draw_target);
594 if (redraw_mask & REDRAW_FIELD)
595 DrawMaskedBorderExt_FIELD(draw_target);
596 if (redraw_mask & REDRAW_DOOR_1)
597 DrawMaskedBorderExt_DOOR_1(draw_target);
598 if (redraw_mask & REDRAW_DOOR_2)
599 DrawMaskedBorderExt_DOOR_2(draw_target);
600 if (redraw_mask & REDRAW_DOOR_3)
601 DrawMaskedBorderExt_DOOR_3(draw_target);
605 void DrawMaskedBorder_FIELD(void)
607 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
610 void DrawMaskedBorder(int redraw_mask)
612 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
615 void DrawMaskedBorderToTarget(int draw_target)
617 if (draw_target == DRAW_TO_BACKBUFFER ||
618 draw_target == DRAW_TO_SCREEN)
620 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
624 int last_border_status = global.border_status;
626 if (draw_target == DRAW_TO_FADE_SOURCE)
628 global.border_status = gfx.fade_border_source_status;
629 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
631 else if (draw_target == DRAW_TO_FADE_TARGET)
633 global.border_status = gfx.fade_border_target_status;
634 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
637 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
639 global.border_status = last_border_status;
640 gfx.masked_border_bitmap_ptr = backbuffer;
644 void DrawTileCursor(int draw_target)
646 DrawTileCursor_MM(draw_target, game_status == GAME_MODE_PLAYING);
649 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
651 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
654 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
656 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
657 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
659 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
662 void BlitScreenToBitmap(Bitmap *target_bitmap)
664 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
665 BlitScreenToBitmap_EM(target_bitmap);
666 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
667 BlitScreenToBitmap_SP(target_bitmap);
668 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
669 BlitScreenToBitmap_MM(target_bitmap);
670 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
671 BlitScreenToBitmap_RND(target_bitmap);
673 redraw_mask |= REDRAW_FIELD;
676 static void DrawFramesPerSecond(void)
679 int font_nr = FONT_TEXT_2;
680 int font_width = getFontWidth(font_nr);
681 int draw_deactivation_mask = GetDrawDeactivationMask();
682 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
684 // draw FPS with leading space (needed if field buffer deactivated)
685 sprintf(text, " %04.1f fps", global.frames_per_second);
687 // override draw deactivation mask (required for invisible warp mode)
688 SetDrawDeactivationMask(REDRAW_NONE);
690 // draw opaque FPS if field buffer deactivated, else draw masked FPS
691 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
692 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
694 // set draw deactivation mask to previous value
695 SetDrawDeactivationMask(draw_deactivation_mask);
697 // force full-screen redraw in this frame
698 redraw_mask = REDRAW_ALL;
702 static void PrintFrameTimeDebugging(void)
704 static unsigned int last_counter = 0;
705 unsigned int counter = Counter();
706 int diff_1 = counter - last_counter;
707 int diff_2 = diff_1 - GAME_FRAME_DELAY;
709 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
710 char diff_bar[2 * diff_2_max + 5];
714 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
716 for (i = 0; i < diff_2_max; i++)
717 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
718 i >= diff_2_max - diff_2_cut ? '-' : ' ');
720 diff_bar[pos++] = '|';
722 for (i = 0; i < diff_2_max; i++)
723 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
725 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
727 diff_bar[pos++] = '\0';
729 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
732 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
735 last_counter = counter;
739 static int unifiedRedrawMask(int mask)
741 if (mask & REDRAW_ALL)
744 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
750 static boolean equalRedrawMasks(int mask_1, int mask_2)
752 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
755 void BackToFront(void)
757 static int last_redraw_mask = REDRAW_NONE;
759 // force screen redraw in every frame to continue drawing global animations
760 // (but always use the last redraw mask to prevent unwanted side effects)
761 if (redraw_mask == REDRAW_NONE)
762 redraw_mask = last_redraw_mask;
764 last_redraw_mask = redraw_mask;
767 // masked border now drawn immediately when blitting backbuffer to window
769 // draw masked border to all viewports, if defined
770 DrawMaskedBorder(redraw_mask);
773 // draw frames per second (only if debug mode is enabled)
774 if (redraw_mask & REDRAW_FPS)
775 DrawFramesPerSecond();
777 // remove playfield redraw before potentially merging with doors redraw
778 if (DrawingDeactivated(REAL_SX, REAL_SY))
779 redraw_mask &= ~REDRAW_FIELD;
781 // redraw complete window if both playfield and (some) doors need redraw
782 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
783 redraw_mask = REDRAW_ALL;
785 /* although redrawing the whole window would be fine for normal gameplay,
786 being able to only redraw the playfield is required for deactivating
787 certain drawing areas (mainly playfield) to work, which is needed for
788 warp-forward to be fast enough (by skipping redraw of most frames) */
790 if (redraw_mask & REDRAW_ALL)
792 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
794 else if (redraw_mask & REDRAW_FIELD)
796 BlitBitmap(backbuffer, window,
797 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
799 else if (redraw_mask & REDRAW_DOORS)
801 // merge door areas to prevent calling screen redraw more than once
807 if (redraw_mask & REDRAW_DOOR_1)
811 x2 = MAX(x2, DX + DXSIZE);
812 y2 = MAX(y2, DY + DYSIZE);
815 if (redraw_mask & REDRAW_DOOR_2)
819 x2 = MAX(x2, VX + VXSIZE);
820 y2 = MAX(y2, VY + VYSIZE);
823 if (redraw_mask & REDRAW_DOOR_3)
827 x2 = MAX(x2, EX + EXSIZE);
828 y2 = MAX(y2, EY + EYSIZE);
831 // make sure that at least one pixel is blitted, and inside the screen
832 // (else nothing is blitted, causing the animations not to be updated)
833 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
834 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
835 x2 = MIN(MAX(1, x2), WIN_XSIZE);
836 y2 = MIN(MAX(1, y2), WIN_YSIZE);
838 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
841 redraw_mask = REDRAW_NONE;
844 PrintFrameTimeDebugging();
848 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
850 unsigned int frame_delay_value_old = GetVideoFrameDelay();
852 SetVideoFrameDelay(frame_delay_value);
856 SetVideoFrameDelay(frame_delay_value_old);
859 static int fade_type_skip = FADE_TYPE_NONE;
861 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
863 void (*draw_border_function)(void) = NULL;
864 int x, y, width, height;
865 int fade_delay, post_delay;
867 if (fade_type == FADE_TYPE_FADE_OUT)
869 if (fade_type_skip != FADE_TYPE_NONE)
871 // skip all fade operations until specified fade operation
872 if (fade_type & fade_type_skip)
873 fade_type_skip = FADE_TYPE_NONE;
878 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
882 redraw_mask |= fade_mask;
884 if (fade_type == FADE_TYPE_SKIP)
886 fade_type_skip = fade_mode;
891 fade_delay = fading.fade_delay;
892 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
894 if (fade_type_skip != FADE_TYPE_NONE)
896 // skip all fade operations until specified fade operation
897 if (fade_type & fade_type_skip)
898 fade_type_skip = FADE_TYPE_NONE;
903 if (global.autoplay_leveldir)
908 if (fade_mask == REDRAW_FIELD)
913 height = FADE_SYSIZE;
915 if (border.draw_masked_when_fading)
916 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
918 DrawMaskedBorder_FIELD(); // draw once
928 // when switching screens without fading, set fade delay to zero
929 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
932 // do not display black frame when fading out without fade delay
933 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
936 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
937 draw_border_function);
939 redraw_mask &= ~fade_mask;
941 ClearAutoRepeatKeyEvents();
944 static void SetScreenStates_BeforeFadingIn(void)
946 // temporarily set screen mode for animations to screen after fading in
947 global.anim_status = global.anim_status_next;
949 // store backbuffer with all animations that will be started after fading in
950 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
952 // set screen mode for animations back to fading
953 global.anim_status = GAME_MODE_PSEUDO_FADING;
956 static void SetScreenStates_AfterFadingIn(void)
958 // store new source screen (to use correct masked border for fading)
959 gfx.fade_border_source_status = global.border_status;
961 global.anim_status = global.anim_status_next;
964 static void SetScreenStates_BeforeFadingOut(void)
966 // store new target screen (to use correct masked border for fading)
967 gfx.fade_border_target_status = game_status;
969 // set screen mode for animations to fading
970 global.anim_status = GAME_MODE_PSEUDO_FADING;
972 // store backbuffer with all animations that will be stopped for fading out
973 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
976 static void SetScreenStates_AfterFadingOut(void)
978 global.border_status = game_status;
981 void FadeIn(int fade_mask)
983 SetScreenStates_BeforeFadingIn();
986 DrawMaskedBorder(REDRAW_ALL);
989 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
990 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
992 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
996 FADE_SXSIZE = FULL_SXSIZE;
997 FADE_SYSIZE = FULL_SYSIZE;
999 // activate virtual buttons depending on upcoming game status
1000 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1001 game_status == GAME_MODE_PLAYING && !tape.playing)
1002 SetOverlayActive(TRUE);
1004 SetScreenStates_AfterFadingIn();
1006 // force update of global animation status in case of rapid screen changes
1007 redraw_mask = REDRAW_ALL;
1011 void FadeOut(int fade_mask)
1013 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1014 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1015 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1018 SetScreenStates_BeforeFadingOut();
1020 SetTileCursorActive(FALSE);
1021 SetOverlayActive(FALSE);
1024 DrawMaskedBorder(REDRAW_ALL);
1027 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1028 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1030 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1032 SetScreenStates_AfterFadingOut();
1035 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1037 static struct TitleFadingInfo fading_leave_stored;
1040 fading_leave_stored = fading_leave;
1042 fading = fading_leave_stored;
1045 void FadeSetEnterMenu(void)
1047 fading = menu.enter_menu;
1049 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1052 void FadeSetLeaveMenu(void)
1054 fading = menu.leave_menu;
1056 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1059 void FadeSetEnterScreen(void)
1061 fading = menu.enter_screen[game_status];
1063 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1066 void FadeSetNextScreen(void)
1068 fading = menu.next_screen[game_status];
1070 // (do not overwrite fade mode set by FadeSetEnterScreen)
1071 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1074 void FadeSetLeaveScreen(void)
1076 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1079 void FadeSetFromType(int type)
1081 if (type & TYPE_ENTER_SCREEN)
1082 FadeSetEnterScreen();
1083 else if (type & TYPE_ENTER)
1085 else if (type & TYPE_LEAVE)
1089 void FadeSetDisabled(void)
1091 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1093 fading = fading_none;
1096 void FadeSkipNextFadeIn(void)
1098 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1101 void FadeSkipNextFadeOut(void)
1103 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1106 static int getGlobalGameStatus(int status)
1108 return (status == GAME_MODE_PSEUDO_TYPENAME ? GAME_MODE_MAIN :
1109 status == GAME_MODE_SCOREINFO ? GAME_MODE_SCORES :
1113 int getImageFromGraphicOrDefault(int graphic, int default_graphic)
1115 if (graphic == IMG_UNDEFINED)
1116 return IMG_UNDEFINED;
1118 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1120 return (graphic_info[graphic].bitmap != NULL || redefined ?
1121 graphic : default_graphic);
1124 static int getBackgroundImage(int graphic)
1126 return getImageFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1129 static int getGlobalBorderImage(int graphic)
1131 return getImageFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1134 Bitmap *getGlobalBorderBitmapFromStatus(int status_raw)
1136 int status = getGlobalGameStatus(status_raw);
1138 (status == GAME_MODE_MAIN ? IMG_GLOBAL_BORDER_MAIN :
1139 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1140 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1141 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1143 int graphic_final = getGlobalBorderImage(graphic);
1145 return graphic_info[graphic_final].bitmap;
1148 void SetBackgroundImage(int graphic, int redraw_mask)
1150 struct GraphicInfo *g = &graphic_info[graphic];
1151 struct GraphicInfo g_undefined = { 0 };
1153 if (graphic == IMG_UNDEFINED)
1156 // always use original size bitmap for backgrounds, if existing
1157 Bitmap *bitmap = (g->bitmaps != NULL &&
1158 g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] != NULL ?
1159 g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] : g->bitmap);
1161 // remove every mask before setting mask for window, and
1162 // remove window area mask before setting mask for main or door area
1163 int remove_mask = (redraw_mask == REDRAW_ALL ? 0xffff : REDRAW_ALL);
1165 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
1166 SetBackgroundBitmap(NULL, remove_mask, 0, 0, 0, 0); // !!! FIX THIS !!!
1167 SetBackgroundBitmap(bitmap, redraw_mask,
1169 g->width, g->height);
1172 void SetWindowBackgroundImageIfDefined(int graphic)
1174 if (graphic_info[graphic].bitmap)
1175 SetBackgroundImage(graphic, REDRAW_ALL);
1178 void SetMainBackgroundImageIfDefined(int graphic)
1180 if (graphic_info[graphic].bitmap)
1181 SetBackgroundImage(graphic, REDRAW_FIELD);
1184 void SetDoorBackgroundImageIfDefined(int graphic)
1186 if (graphic_info[graphic].bitmap)
1187 SetBackgroundImage(graphic, REDRAW_DOOR_1);
1190 void SetWindowBackgroundImage(int graphic)
1192 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_ALL);
1195 void SetMainBackgroundImage(int graphic)
1197 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_FIELD);
1200 void SetDoorBackgroundImage(int graphic)
1202 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_DOOR_1);
1205 void SetPanelBackground(void)
1207 SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
1210 void DrawBackground(int x, int y, int width, int height)
1212 // "drawto" might still point to playfield buffer here (hall of fame)
1213 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1215 if (IN_GFX_FIELD_FULL(x, y))
1216 redraw_mask |= REDRAW_FIELD;
1217 else if (IN_GFX_DOOR_1(x, y))
1218 redraw_mask |= REDRAW_DOOR_1;
1219 else if (IN_GFX_DOOR_2(x, y))
1220 redraw_mask |= REDRAW_DOOR_2;
1221 else if (IN_GFX_DOOR_3(x, y))
1222 redraw_mask |= REDRAW_DOOR_3;
1225 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1227 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1229 if (font->bitmap == NULL)
1232 DrawBackground(x, y, width, height);
1235 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1237 struct GraphicInfo *g = &graphic_info[graphic];
1239 if (g->bitmap == NULL)
1242 DrawBackground(x, y, width, height);
1245 static int game_status_last = -1;
1246 static Bitmap *global_border_bitmap_last = NULL;
1247 static Bitmap *global_border_bitmap = NULL;
1248 static int real_sx_last = -1, real_sy_last = -1;
1249 static int full_sxsize_last = -1, full_sysize_last = -1;
1250 static int dx_last = -1, dy_last = -1;
1251 static int dxsize_last = -1, dysize_last = -1;
1252 static int vx_last = -1, vy_last = -1;
1253 static int vxsize_last = -1, vysize_last = -1;
1254 static int ex_last = -1, ey_last = -1;
1255 static int exsize_last = -1, eysize_last = -1;
1257 boolean CheckIfGlobalBorderHasChanged(void)
1259 // if game status has not changed, global border has not changed either
1260 if (game_status == game_status_last)
1263 // determine and store new global border bitmap for current game status
1264 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1266 return (global_border_bitmap_last != global_border_bitmap);
1269 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1271 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1272 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1274 // if game status has not changed, nothing has to be redrawn
1275 if (game_status == game_status_last)
1278 // redraw if last screen was title screen
1279 if (game_status_last == GAME_MODE_TITLE)
1282 // redraw if global screen border has changed
1283 if (CheckIfGlobalBorderHasChanged())
1286 // redraw if position or size of playfield area has changed
1287 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1288 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1291 // redraw if position or size of door area has changed
1292 if (dx_last != DX || dy_last != DY ||
1293 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1296 // redraw if position or size of tape area has changed
1297 if (vx_last != VX || vy_last != VY ||
1298 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1301 // redraw if position or size of editor area has changed
1302 if (ex_last != EX || ey_last != EY ||
1303 exsize_last != EXSIZE || eysize_last != EYSIZE)
1310 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1313 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1315 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1318 void RedrawGlobalBorder(void)
1320 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1322 RedrawGlobalBorderFromBitmap(bitmap);
1324 redraw_mask = REDRAW_ALL;
1327 static void RedrawGlobalBorderIfNeeded(void)
1329 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1330 if (game_status == game_status_last)
1334 // copy current draw buffer to later copy back areas that have not changed
1335 if (game_status_last != GAME_MODE_TITLE)
1336 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1338 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1339 if (CheckIfGlobalBorderRedrawIsNeeded())
1341 // determine and store new global border bitmap for current game status
1342 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1345 // redraw global screen border (or clear, if defined to be empty)
1346 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1348 if (game_status == GAME_MODE_EDITOR)
1349 DrawSpecialEditorDoor();
1351 // copy previous playfield and door areas, if they are defined on both
1352 // previous and current screen and if they still have the same size
1354 if (real_sx_last != -1 && real_sy_last != -1 &&
1355 REAL_SX != -1 && REAL_SY != -1 &&
1356 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1357 BlitBitmap(bitmap_db_store_1, backbuffer,
1358 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1361 if (dx_last != -1 && dy_last != -1 &&
1362 DX != -1 && DY != -1 &&
1363 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1364 BlitBitmap(bitmap_db_store_1, backbuffer,
1365 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1367 if (game_status != GAME_MODE_EDITOR)
1369 if (vx_last != -1 && vy_last != -1 &&
1370 VX != -1 && VY != -1 &&
1371 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1372 BlitBitmap(bitmap_db_store_1, backbuffer,
1373 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1377 if (ex_last != -1 && ey_last != -1 &&
1378 EX != -1 && EY != -1 &&
1379 exsize_last == EXSIZE && eysize_last == EYSIZE)
1380 BlitBitmap(bitmap_db_store_1, backbuffer,
1381 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1384 redraw_mask = REDRAW_ALL;
1387 game_status_last = game_status;
1389 global_border_bitmap_last = global_border_bitmap;
1391 real_sx_last = REAL_SX;
1392 real_sy_last = REAL_SY;
1393 full_sxsize_last = FULL_SXSIZE;
1394 full_sysize_last = FULL_SYSIZE;
1397 dxsize_last = DXSIZE;
1398 dysize_last = DYSIZE;
1401 vxsize_last = VXSIZE;
1402 vysize_last = VYSIZE;
1405 exsize_last = EXSIZE;
1406 eysize_last = EYSIZE;
1409 void ClearField(void)
1411 RedrawGlobalBorderIfNeeded();
1413 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1414 // (when entering hall of fame after playing)
1415 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1417 // !!! maybe this should be done before clearing the background !!!
1418 if (game_status == GAME_MODE_PLAYING)
1420 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1421 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1425 SetDrawtoField(DRAW_TO_BACKBUFFER);
1429 void MarkTileDirty(int x, int y)
1431 redraw_mask |= REDRAW_FIELD;
1434 void SetBorderElement(void)
1438 BorderElement = EL_EMPTY;
1440 // only the R'n'D game engine may use an additional steelwall border
1441 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1444 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1446 for (x = 0; x < lev_fieldx; x++)
1448 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1449 BorderElement = EL_STEELWALL;
1451 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1457 void FloodFillLevelExt(int start_x, int start_y, int fill_element,
1458 int max_array_fieldx, int max_array_fieldy,
1459 short field[max_array_fieldx][max_array_fieldy],
1460 int max_fieldx, int max_fieldy)
1462 static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY];
1463 struct XY *check = xy_topdown;
1464 int old_element = field[start_x][start_y];
1467 // do nothing if start field already has the desired content
1468 if (old_element == fill_element)
1471 stack_buffer[stack_pos++] = (struct XY){ start_x, start_y };
1473 while (stack_pos > 0)
1475 struct XY current = stack_buffer[--stack_pos];
1478 field[current.x][current.y] = fill_element;
1480 for (i = 0; i < 4; i++)
1482 int x = current.x + check[i].x;
1483 int y = current.y + check[i].y;
1485 // check for stack buffer overflow (should not happen)
1486 if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY)
1487 Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug.");
1489 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1490 stack_buffer[stack_pos++] = (struct XY){ x, y };
1495 void FloodFillLevel(int from_x, int from_y, int fill_element,
1496 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1497 int max_fieldx, int max_fieldy)
1499 FloodFillLevelExt(from_x, from_y, fill_element,
1500 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1501 max_fieldx, max_fieldy);
1504 void SetRandomAnimationValue(int x, int y)
1506 gfx.anim_random_frame = GfxRandom[x][y];
1509 int getGraphicAnimationFrame(int graphic, int sync_frame)
1511 // animation synchronized with global frame counter, not move position
1512 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1513 sync_frame = FrameCounter;
1514 else if (graphic_info[graphic].anim_global_anim_sync)
1515 sync_frame = getGlobalAnimSyncFrame();
1517 return getAnimationFrame(graphic_info[graphic].anim_frames,
1518 graphic_info[graphic].anim_delay,
1519 graphic_info[graphic].anim_mode,
1520 graphic_info[graphic].anim_start_frame,
1524 int getGraphicAnimationFrameXY(int graphic, int lx, int ly)
1526 if (graphic_info[graphic].anim_mode & ANIM_TILED)
1528 struct GraphicInfo *g = &graphic_info[graphic];
1529 int xsize = MAX(1, g->anim_frames_per_line);
1530 int ysize = MAX(1, g->anim_frames / xsize);
1531 int xoffset = g->anim_start_frame % xsize;
1532 int yoffset = g->anim_start_frame % ysize;
1533 // may be needed if screen field is significantly larger than playfield
1534 int x = (lx + xoffset + SCR_FIELDX * xsize) % xsize;
1535 int y = (ly + yoffset + SCR_FIELDY * ysize) % ysize;
1536 int sync_frame = y * xsize + x;
1538 return sync_frame % g->anim_frames;
1540 else if (graphic_info[graphic].anim_mode & ANIM_RANDOM_STATIC)
1542 struct GraphicInfo *g = &graphic_info[graphic];
1543 // may be needed if screen field is significantly larger than playfield
1544 int x = (lx + SCR_FIELDX * lev_fieldx) % lev_fieldx;
1545 int y = (ly + SCR_FIELDY * lev_fieldy) % lev_fieldy;
1546 int sync_frame = GfxRandomStatic[x][y];
1548 return sync_frame % g->anim_frames;
1552 int sync_frame = (IN_LEV_FIELD(lx, ly) ? GfxFrame[lx][ly] : -1);
1554 return getGraphicAnimationFrame(graphic, sync_frame);
1558 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1560 struct GraphicInfo *g = &graphic_info[graphic];
1561 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1563 if (tilesize == gfx.standard_tile_size)
1564 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1565 else if (tilesize == game.tile_size)
1566 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1568 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1571 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1572 boolean get_backside)
1574 struct GraphicInfo *g = &graphic_info[graphic];
1575 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1576 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1578 if (g->offset_y == 0) // frames are ordered horizontally
1580 int max_width = g->anim_frames_per_line * g->width;
1581 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1583 *x = pos % max_width;
1584 *y = src_y % g->height + pos / max_width * g->height;
1586 else if (g->offset_x == 0) // frames are ordered vertically
1588 int max_height = g->anim_frames_per_line * g->height;
1589 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1591 *x = src_x % g->width + pos / max_height * g->width;
1592 *y = pos % max_height;
1594 else // frames are ordered diagonally
1596 *x = src_x + frame * g->offset_x;
1597 *y = src_y + frame * g->offset_y;
1601 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1602 Bitmap **bitmap, int *x, int *y,
1603 boolean get_backside)
1605 struct GraphicInfo *g = &graphic_info[graphic];
1607 // if no graphics defined at all, use fallback graphics
1608 if (g->bitmaps == NULL)
1609 *g = graphic_info[IMG_CHAR_EXCLAM];
1611 // if no in-game graphics defined, always use standard graphic size
1612 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1613 tilesize = TILESIZE;
1615 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1616 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1618 *x = *x * tilesize / g->tile_size;
1619 *y = *y * tilesize / g->tile_size;
1622 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1623 Bitmap **bitmap, int *x, int *y)
1625 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1628 void getFixedGraphicSource(int graphic, int frame,
1629 Bitmap **bitmap, int *x, int *y)
1631 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1634 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1636 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1639 void getGlobalAnimGraphicSource(int graphic, int frame,
1640 Bitmap **bitmap, int *x, int *y)
1642 struct GraphicInfo *g = &graphic_info[graphic];
1644 // if no graphics defined at all, use fallback graphics
1645 if (g->bitmaps == NULL)
1646 *g = graphic_info[IMG_CHAR_EXCLAM];
1648 // use original size graphics, if existing, else use standard size graphics
1649 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1650 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1652 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1654 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1657 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1658 int *x, int *y, boolean get_backside)
1660 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1664 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1666 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1669 void DrawGraphic(int x, int y, int graphic, int frame)
1672 if (!IN_SCR_FIELD(x, y))
1674 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1675 Debug("draw:DrawGraphic", "This should never happen!");
1681 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1684 MarkTileDirty(x, y);
1687 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1690 if (!IN_SCR_FIELD(x, y))
1692 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1694 Debug("draw:DrawFixedGraphic", "This should never happen!");
1700 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1702 MarkTileDirty(x, y);
1705 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1711 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1713 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1716 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1722 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1723 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1726 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1729 if (!IN_SCR_FIELD(x, y))
1731 Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1733 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1739 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1742 MarkTileDirty(x, y);
1745 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1748 if (!IN_SCR_FIELD(x, y))
1750 Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1752 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1758 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1760 MarkTileDirty(x, y);
1763 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1769 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1771 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1775 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1776 int graphic, int frame)
1781 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1783 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1787 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1789 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1791 MarkTileDirty(x / tilesize, y / tilesize);
1794 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1797 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1798 graphic, frame, tilesize);
1799 MarkTileDirty(x / tilesize, y / tilesize);
1802 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1808 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1809 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1812 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1813 int frame, int tilesize)
1818 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1819 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1822 void DrawMiniGraphic(int x, int y, int graphic)
1824 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1825 MarkTileDirty(x / 2, y / 2);
1828 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1833 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1834 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1837 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1838 int graphic, int frame,
1839 int cut_mode, int mask_mode)
1844 int width = TILEX, height = TILEY;
1847 if (dx || dy) // shifted graphic
1849 if (x < BX1) // object enters playfield from the left
1856 else if (x > BX2) // object enters playfield from the right
1862 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1868 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1870 else if (dx) // general horizontal movement
1871 MarkTileDirty(x + SIGN(dx), y);
1873 if (y < BY1) // object enters playfield from the top
1875 if (cut_mode == CUT_BELOW) // object completely above top border
1883 else if (y > BY2) // object enters playfield from the bottom
1889 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1895 else if (dy > 0 && cut_mode == CUT_ABOVE)
1897 if (y == BY2) // object completely above bottom border
1903 MarkTileDirty(x, y + 1);
1904 } // object leaves playfield to the bottom
1905 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1907 else if (dy) // general vertical movement
1908 MarkTileDirty(x, y + SIGN(dy));
1912 if (!IN_SCR_FIELD(x, y))
1914 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1916 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1922 width = width * TILESIZE_VAR / TILESIZE;
1923 height = height * TILESIZE_VAR / TILESIZE;
1924 cx = cx * TILESIZE_VAR / TILESIZE;
1925 cy = cy * TILESIZE_VAR / TILESIZE;
1926 dx = dx * TILESIZE_VAR / TILESIZE;
1927 dy = dy * TILESIZE_VAR / TILESIZE;
1929 if (width > 0 && height > 0)
1931 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1936 dst_x = FX + x * TILEX_VAR + dx;
1937 dst_y = FY + y * TILEY_VAR + dy;
1939 if (mask_mode == USE_MASKING)
1940 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1943 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1946 MarkTileDirty(x, y);
1950 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1951 int graphic, int frame,
1952 int cut_mode, int mask_mode)
1957 int width = TILEX_VAR, height = TILEY_VAR;
1960 int x2 = x + SIGN(dx);
1961 int y2 = y + SIGN(dy);
1963 // movement with two-tile animations must be sync'ed with movement position,
1964 // not with current GfxFrame (which can be higher when using slow movement)
1965 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1966 int anim_frames = graphic_info[graphic].anim_frames;
1968 // (we also need anim_delay here for movement animations with less frames)
1969 int anim_delay = graphic_info[graphic].anim_delay;
1970 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1972 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1973 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1975 // re-calculate animation frame for two-tile movement animation
1976 frame = getGraphicAnimationFrame(graphic, sync_frame);
1978 // check if movement start graphic inside screen area and should be drawn
1979 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1981 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1983 dst_x = FX + x1 * TILEX_VAR;
1984 dst_y = FY + y1 * TILEY_VAR;
1986 if (mask_mode == USE_MASKING)
1987 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1990 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1993 MarkTileDirty(x1, y1);
1996 // check if movement end graphic inside screen area and should be drawn
1997 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1999 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
2001 dst_x = FX + x2 * TILEX_VAR;
2002 dst_y = FY + y2 * TILEY_VAR;
2004 if (mask_mode == USE_MASKING)
2005 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
2008 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
2011 MarkTileDirty(x2, y2);
2015 static void DrawGraphicShifted(int x, int y, int dx, int dy,
2016 int graphic, int frame,
2017 int cut_mode, int mask_mode)
2021 DrawGraphic(x, y, graphic, frame);
2026 if (graphic_info[graphic].double_movement) // EM style movement images
2027 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
2029 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
2032 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
2033 int graphic, int frame, int cut_mode)
2035 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
2038 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
2039 int cut_mode, int mask_mode)
2041 int lx = LEVELX(x), ly = LEVELY(y);
2045 if (IN_LEV_FIELD(lx, ly))
2047 if (element == EL_EMPTY)
2048 element = GfxElementEmpty[lx][ly];
2050 SetRandomAnimationValue(lx, ly);
2052 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2053 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2055 // do not use double (EM style) movement graphic when not moving
2056 if (graphic_info[graphic].double_movement && !dx && !dy)
2058 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2059 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2062 if (game.use_masked_elements && (dx || dy))
2063 mask_mode = USE_MASKING;
2065 else // border element
2067 graphic = el2img(element);
2068 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2071 if (element == EL_EXPANDABLE_WALL)
2073 boolean left_stopped = FALSE, right_stopped = FALSE;
2075 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2076 left_stopped = TRUE;
2077 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2078 right_stopped = TRUE;
2080 if (left_stopped && right_stopped)
2082 else if (left_stopped)
2084 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2085 frame = graphic_info[graphic].anim_frames - 1;
2087 else if (right_stopped)
2089 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2090 frame = graphic_info[graphic].anim_frames - 1;
2095 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2096 else if (mask_mode == USE_MASKING)
2097 DrawGraphicThruMask(x, y, graphic, frame);
2099 DrawGraphic(x, y, graphic, frame);
2102 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2103 int cut_mode, int mask_mode)
2105 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2106 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2107 cut_mode, mask_mode);
2110 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2113 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2116 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2119 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2122 void DrawLevelElementThruMask(int x, int y, int element)
2124 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2127 void DrawLevelFieldThruMask(int x, int y)
2129 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2132 // !!! implementation of quicksand is totally broken !!!
2133 #define IS_CRUMBLED_TILE(x, y, e) \
2134 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2135 !IS_MOVING(x, y) || \
2136 (e) == EL_QUICKSAND_EMPTYING || \
2137 (e) == EL_QUICKSAND_FAST_EMPTYING))
2139 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2144 int width, height, cx, cy;
2145 int sx = SCREENX(x), sy = SCREENY(y);
2146 int crumbled_border_size = graphic_info[graphic].border_size;
2147 int crumbled_tile_size = graphic_info[graphic].tile_size;
2148 int crumbled_border_size_var =
2149 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2152 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2154 for (i = 1; i < 4; i++)
2156 int dxx = (i & 1 ? dx : 0);
2157 int dyy = (i & 2 ? dy : 0);
2160 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2163 // check if neighbour field is of same crumble type
2164 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2165 graphic_info[graphic].class ==
2166 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2168 // return if check prevents inner corner
2169 if (same == (dxx == dx && dyy == dy))
2173 // if we reach this point, we have an inner corner
2175 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2177 width = crumbled_border_size_var;
2178 height = crumbled_border_size_var;
2179 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2180 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2182 if (game.use_masked_elements)
2184 int graphic0 = el2img(EL_EMPTY);
2185 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2186 Bitmap *src_bitmap0;
2189 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2191 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2193 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2195 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2197 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2200 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2202 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2205 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2210 int width, height, bx, by, cx, cy;
2211 int sx = SCREENX(x), sy = SCREENY(y);
2212 int crumbled_border_size = graphic_info[graphic].border_size;
2213 int crumbled_tile_size = graphic_info[graphic].tile_size;
2214 int crumbled_border_size_var =
2215 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2216 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2219 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2221 // only needed when using masked elements
2222 int graphic0 = el2img(EL_EMPTY);
2223 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2224 Bitmap *src_bitmap0;
2227 if (game.use_masked_elements)
2228 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2230 // draw simple, sloppy, non-corner-accurate crumbled border
2232 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2233 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2234 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2235 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2237 if (game.use_masked_elements)
2239 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2241 FX + sx * TILEX_VAR + cx,
2242 FY + sy * TILEY_VAR + cy);
2244 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2246 FX + sx * TILEX_VAR + cx,
2247 FY + sy * TILEY_VAR + cy);
2250 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2252 FX + sx * TILEX_VAR + cx,
2253 FY + sy * TILEY_VAR + cy);
2255 // (remaining middle border part must be at least as big as corner part)
2256 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2257 crumbled_border_size_var >= TILESIZE_VAR / 3)
2260 // correct corners of crumbled border, if needed
2262 for (i = -1; i <= 1; i += 2)
2264 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2265 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2266 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2269 // check if neighbour field is of same crumble type
2270 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2271 graphic_info[graphic].class ==
2272 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2274 // no crumbled corner, but continued crumbled border
2276 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2277 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2278 int b1 = (i == 1 ? crumbled_border_size_var :
2279 TILESIZE_VAR - 2 * crumbled_border_size_var);
2281 width = crumbled_border_size_var;
2282 height = crumbled_border_size_var;
2284 if (dir == 1 || dir == 2)
2299 if (game.use_masked_elements)
2301 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2303 FX + sx * TILEX_VAR + cx,
2304 FY + sy * TILEY_VAR + cy);
2306 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2308 FX + sx * TILEX_VAR + cx,
2309 FY + sy * TILEY_VAR + cy);
2312 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2314 FX + sx * TILEX_VAR + cx,
2315 FY + sy * TILEY_VAR + cy);
2320 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2322 int sx = SCREENX(x), sy = SCREENY(y);
2325 struct XY *xy = xy_topdown;
2327 if (!IN_LEV_FIELD(x, y))
2330 element = TILE_GFX_ELEMENT(x, y);
2332 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2334 if (!IN_SCR_FIELD(sx, sy))
2337 // crumble field borders towards direct neighbour fields
2338 for (i = 0; i < 4; i++)
2340 int xx = x + xy[i].x;
2341 int yy = y + xy[i].y;
2343 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2346 // check if neighbour field is of same crumble type
2347 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2348 graphic_info[graphic].class ==
2349 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2352 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2355 // crumble inner field corners towards corner neighbour fields
2356 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2357 graphic_info[graphic].anim_frames == 2)
2359 for (i = 0; i < 4; i++)
2361 int dx = (i & 1 ? +1 : -1);
2362 int dy = (i & 2 ? +1 : -1);
2364 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2368 MarkTileDirty(sx, sy);
2370 else // center field is not crumbled -- crumble neighbour fields
2372 // crumble field borders of direct neighbour fields
2373 for (i = 0; i < 4; i++)
2375 int xx = x + xy[i].x;
2376 int yy = y + xy[i].y;
2377 int sxx = sx + xy[i].x;
2378 int syy = sy + xy[i].y;
2380 if (!IN_LEV_FIELD(xx, yy) ||
2381 !IN_SCR_FIELD(sxx, syy))
2384 // do not crumble fields that are being digged or snapped
2385 if (Tile[xx][yy] == EL_EMPTY ||
2386 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2389 element = TILE_GFX_ELEMENT(xx, yy);
2391 if (!IS_CRUMBLED_TILE(xx, yy, element))
2394 graphic = el_act2crm(element, ACTION_DEFAULT);
2396 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2398 MarkTileDirty(sxx, syy);
2401 // crumble inner field corners of corner neighbour fields
2402 for (i = 0; i < 4; i++)
2404 int dx = (i & 1 ? +1 : -1);
2405 int dy = (i & 2 ? +1 : -1);
2411 if (!IN_LEV_FIELD(xx, yy) ||
2412 !IN_SCR_FIELD(sxx, syy))
2415 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2418 element = TILE_GFX_ELEMENT(xx, yy);
2420 if (!IS_CRUMBLED_TILE(xx, yy, element))
2423 graphic = el_act2crm(element, ACTION_DEFAULT);
2425 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2426 graphic_info[graphic].anim_frames == 2)
2427 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2429 MarkTileDirty(sxx, syy);
2434 void DrawLevelFieldCrumbled(int x, int y)
2438 if (!IN_LEV_FIELD(x, y))
2441 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2442 GfxElement[x][y] != EL_UNDEFINED &&
2443 GFX_CRUMBLED(GfxElement[x][y]))
2445 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2450 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2452 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2455 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2458 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2459 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2460 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2461 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2462 int sx = SCREENX(x), sy = SCREENY(y);
2464 DrawScreenGraphic(sx, sy, graphic1, frame1);
2465 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2468 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2470 int sx = SCREENX(x), sy = SCREENY(y);
2471 struct XY *xy = xy_topdown;
2474 // crumble direct neighbour fields (required for field borders)
2475 for (i = 0; i < 4; i++)
2477 int xx = x + xy[i].x;
2478 int yy = y + xy[i].y;
2479 int sxx = sx + xy[i].x;
2480 int syy = sy + xy[i].y;
2482 if (!IN_LEV_FIELD(xx, yy) ||
2483 !IN_SCR_FIELD(sxx, syy) ||
2484 !GFX_CRUMBLED(Tile[xx][yy]) ||
2488 DrawLevelField(xx, yy);
2491 // crumble corner neighbour fields (required for inner field corners)
2492 for (i = 0; i < 4; i++)
2494 int dx = (i & 1 ? +1 : -1);
2495 int dy = (i & 2 ? +1 : -1);
2501 if (!IN_LEV_FIELD(xx, yy) ||
2502 !IN_SCR_FIELD(sxx, syy) ||
2503 !GFX_CRUMBLED(Tile[xx][yy]) ||
2507 int element = TILE_GFX_ELEMENT(xx, yy);
2508 int graphic = el_act2crm(element, ACTION_DEFAULT);
2510 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2511 graphic_info[graphic].anim_frames == 2)
2512 DrawLevelField(xx, yy);
2516 static int getBorderElement(int x, int y)
2520 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2521 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2522 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2523 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2524 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2525 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2526 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2528 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2529 int steel_position = (x == -1 && y == -1 ? 0 :
2530 x == lev_fieldx && y == -1 ? 1 :
2531 x == -1 && y == lev_fieldy ? 2 :
2532 x == lev_fieldx && y == lev_fieldy ? 3 :
2533 x == -1 || x == lev_fieldx ? 4 :
2534 y == -1 || y == lev_fieldy ? 5 : 6);
2536 return border[steel_position][steel_type];
2539 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2541 if (game.use_masked_elements)
2543 if (graphic != el2img(EL_EMPTY))
2544 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2546 DrawGraphicThruMask(x, y, graphic, frame);
2550 DrawGraphic(x, y, graphic, frame);
2554 void DrawLevelGraphic(int x, int y, int graphic, int frame)
2556 DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2559 void DrawScreenElement(int x, int y, int element)
2561 int mask_mode = NO_MASKING;
2563 if (game.use_masked_elements)
2565 int lx = LEVELX(x), ly = LEVELY(y);
2567 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2569 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2571 mask_mode = USE_MASKING;
2575 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2576 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2579 void DrawLevelElement(int x, int y, int element)
2581 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2582 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2585 void DrawScreenField(int x, int y)
2587 int lx = LEVELX(x), ly = LEVELY(y);
2588 int element, content;
2590 if (!IN_LEV_FIELD(lx, ly))
2592 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2595 element = getBorderElement(lx, ly);
2597 DrawScreenElement(x, y, element);
2602 element = Tile[lx][ly];
2603 content = Store[lx][ly];
2605 if (IS_MOVING(lx, ly))
2607 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2608 boolean cut_mode = NO_CUTTING;
2610 if (element == EL_QUICKSAND_EMPTYING ||
2611 element == EL_QUICKSAND_FAST_EMPTYING ||
2612 element == EL_MAGIC_WALL_EMPTYING ||
2613 element == EL_BD_MAGIC_WALL_EMPTYING ||
2614 element == EL_DC_MAGIC_WALL_EMPTYING ||
2615 element == EL_AMOEBA_DROPPING)
2616 cut_mode = CUT_ABOVE;
2617 else if (element == EL_QUICKSAND_FILLING ||
2618 element == EL_QUICKSAND_FAST_FILLING ||
2619 element == EL_MAGIC_WALL_FILLING ||
2620 element == EL_BD_MAGIC_WALL_FILLING ||
2621 element == EL_DC_MAGIC_WALL_FILLING)
2622 cut_mode = CUT_BELOW;
2624 if (cut_mode == CUT_ABOVE)
2625 DrawScreenElement(x, y, element);
2627 DrawScreenElement(x, y, EL_EMPTY);
2629 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2631 int dir = MovDir[lx][ly];
2632 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2633 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2635 if (IN_SCR_FIELD(newx, newy))
2636 DrawScreenElement(newx, newy, EL_EMPTY);
2640 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2641 else if (cut_mode == NO_CUTTING)
2642 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2645 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2647 if (cut_mode == CUT_BELOW &&
2648 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2649 DrawLevelElement(lx, ly + 1, element);
2652 if (content == EL_ACID)
2654 int dir = MovDir[lx][ly];
2655 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2656 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2658 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2660 // prevent target field from being drawn again (but without masking)
2661 // (this would happen if target field is scanned after moving element)
2662 Stop[newlx][newly] = TRUE;
2665 else if (IS_BLOCKED(lx, ly))
2670 boolean cut_mode = NO_CUTTING;
2671 int element_old, content_old;
2673 Blocked2Moving(lx, ly, &oldx, &oldy);
2676 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2677 MovDir[oldx][oldy] == MV_RIGHT);
2679 element_old = Tile[oldx][oldy];
2680 content_old = Store[oldx][oldy];
2682 if (element_old == EL_QUICKSAND_EMPTYING ||
2683 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2684 element_old == EL_MAGIC_WALL_EMPTYING ||
2685 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2686 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2687 element_old == EL_AMOEBA_DROPPING)
2688 cut_mode = CUT_ABOVE;
2690 DrawScreenElement(x, y, EL_EMPTY);
2693 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2695 else if (cut_mode == NO_CUTTING)
2696 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2699 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2702 else if (IS_DRAWABLE(element))
2703 DrawScreenElement(x, y, element);
2705 DrawScreenElement(x, y, EL_EMPTY);
2708 void DrawLevelField(int x, int y)
2710 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2711 DrawScreenField(SCREENX(x), SCREENY(y));
2712 else if (IS_MOVING(x, y))
2716 Moving2Blocked(x, y, &newx, &newy);
2717 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2718 DrawScreenField(SCREENX(newx), SCREENY(newy));
2720 else if (IS_BLOCKED(x, y))
2724 Blocked2Moving(x, y, &oldx, &oldy);
2725 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2726 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2730 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2731 int (*el2img_function)(int), boolean masked,
2732 int element_bits_draw)
2734 int element_base = map_mm_wall_element(element);
2735 int element_bits = (IS_DF_WALL(element) ?
2736 element - EL_DF_WALL_START :
2737 IS_MM_WALL(element) ?
2738 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2739 int graphic = el2img_function(element_base);
2740 int tilesize_draw = tilesize / 2;
2745 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2747 for (i = 0; i < 4; i++)
2749 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2750 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2752 if (!(element_bits_draw & (1 << i)))
2755 if (element_bits & (1 << i))
2758 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2759 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2761 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2762 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2767 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2768 tilesize_draw, tilesize_draw);
2773 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2774 boolean masked, int element_bits_draw)
2776 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2777 element, tilesize, el2edimg, masked, element_bits_draw);
2780 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2781 int (*el2img_function)(int))
2783 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2787 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2790 if (IS_MM_WALL(element))
2792 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2793 element, tilesize, el2edimg, masked, 0x000f);
2797 int graphic = el2edimg(element);
2800 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2802 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2806 void DrawSizedElement(int x, int y, int element, int tilesize)
2808 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2811 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2813 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2816 void DrawMiniElement(int x, int y, int element)
2820 graphic = el2edimg(element);
2821 DrawMiniGraphic(x, y, graphic);
2824 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2827 int x = sx + scroll_x, y = sy + scroll_y;
2829 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2830 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2831 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2832 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2834 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2837 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2839 int x = sx + scroll_x, y = sy + scroll_y;
2841 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2842 DrawMiniElement(sx, sy, EL_EMPTY);
2843 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2844 DrawMiniElement(sx, sy, Tile[x][y]);
2846 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2849 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2850 int x, int y, int xsize, int ysize,
2851 int tile_width, int tile_height)
2855 int dst_x = startx + x * tile_width;
2856 int dst_y = starty + y * tile_height;
2857 int width = graphic_info[graphic].width;
2858 int height = graphic_info[graphic].height;
2859 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2860 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2861 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2862 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2863 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2864 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2865 boolean draw_masked = graphic_info[graphic].draw_masked;
2867 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2869 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2871 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2875 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2876 inner_sx + (x - 1) * tile_width % inner_width);
2877 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2878 inner_sy + (y - 1) * tile_height % inner_height);
2881 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2884 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2888 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2889 int x, int y, int xsize, int ysize,
2892 int font_width = getFontWidth(font_nr);
2893 int font_height = getFontHeight(font_nr);
2895 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2896 font_width, font_height);
2899 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2901 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2902 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2903 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2904 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2905 boolean no_delay = (tape.warp_forward);
2906 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2907 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2908 DelayCounter anim_delay = { anim_delay_value };
2909 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2910 int font_width = getFontWidth(font_nr);
2911 int font_height = getFontHeight(font_nr);
2912 int max_xsize = level.envelope[envelope_nr].xsize;
2913 int max_ysize = level.envelope[envelope_nr].ysize;
2914 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2915 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2916 int xend = max_xsize;
2917 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2918 int xstep = (xstart < xend ? 1 : 0);
2919 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2921 int end = MAX(xend - xstart, yend - ystart);
2924 for (i = start; i <= end; i++)
2926 int last_frame = end; // last frame of this "for" loop
2927 int x = xstart + i * xstep;
2928 int y = ystart + i * ystep;
2929 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2930 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2931 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2932 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2935 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2937 BlitScreenToBitmap(backbuffer);
2939 SetDrawtoField(DRAW_TO_BACKBUFFER);
2941 for (yy = 0; yy < ysize; yy++)
2942 for (xx = 0; xx < xsize; xx++)
2943 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2945 DrawTextArea(sx + font_width, sy + font_height,
2946 level.envelope[envelope_nr].text, font_nr, max_xsize,
2947 xsize - 2, ysize - 2, 0, mask_mode,
2948 level.envelope[envelope_nr].autowrap,
2949 level.envelope[envelope_nr].centered, FALSE);
2951 redraw_mask |= REDRAW_FIELD;
2954 SkipUntilDelayReached(&anim_delay, &i, last_frame);
2957 ClearAutoRepeatKeyEvents();
2960 void ShowEnvelope(int envelope_nr)
2962 int element = EL_ENVELOPE_1 + envelope_nr;
2963 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2964 int sound_opening = element_info[element].sound[ACTION_OPENING];
2965 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2966 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2967 boolean no_delay = (tape.warp_forward);
2968 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2969 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2970 int anim_mode = graphic_info[graphic].anim_mode;
2971 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2972 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2973 boolean overlay_enabled = GetOverlayEnabled();
2975 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2977 SetOverlayEnabled(FALSE);
2980 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2982 if (anim_mode == ANIM_DEFAULT)
2983 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2985 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2988 Delay_WithScreenUpdates(wait_delay_value);
2990 WaitForEventToContinue();
2993 SetOverlayEnabled(overlay_enabled);
2995 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2997 if (anim_mode != ANIM_NONE)
2998 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
3000 if (anim_mode == ANIM_DEFAULT)
3001 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
3003 game.envelope_active = FALSE;
3005 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3007 redraw_mask |= REDRAW_FIELD;
3011 void ShowEnvelope_MM(int envelope_nr)
3013 BlitBitmap(backbuffer, bitmap_db_field, REAL_SX, REAL_SY,
3014 FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
3016 ShowEnvelope(envelope_nr);
3018 SetDrawtoField(DRAW_TO_BACKBUFFER);
3020 BlitBitmap(bitmap_db_field, backbuffer, REAL_SX, REAL_SY,
3021 FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
3024 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
3025 int xsize, int ysize)
3027 if (!global.use_envelope_request ||
3028 request.sort_priority <= 0)
3031 if (request.bitmap == NULL ||
3032 xsize > request.xsize ||
3033 ysize > request.ysize)
3035 if (request.bitmap != NULL)
3036 FreeBitmap(request.bitmap);
3038 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
3040 SDL_Surface *surface = request.bitmap->surface;
3042 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
3043 Fail("SDLGetNativeSurface() failed");
3046 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3048 SDLFreeBitmapTextures(request.bitmap);
3049 SDLCreateBitmapTextures(request.bitmap);
3051 // set envelope request run-time values
3054 request.xsize = xsize;
3055 request.ysize = ysize;
3058 void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage)
3060 if (global.use_envelope_request &&
3061 game.request_active_or_moving &&
3062 request.sort_priority > 0 &&
3063 drawing_target == DRAW_TO_SCREEN &&
3064 drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
3066 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3067 request.sx, request.sy);
3071 static void setRequestBasePosition(int *x, int *y)
3073 int sx_base, sy_base;
3075 if (request.x != -1)
3076 sx_base = request.x;
3077 else if (request.align == ALIGN_LEFT)
3079 else if (request.align == ALIGN_RIGHT)
3080 sx_base = SX + SXSIZE;
3082 sx_base = SX + SXSIZE / 2;
3084 if (request.y != -1)
3085 sy_base = request.y;
3086 else if (request.valign == VALIGN_TOP)
3088 else if (request.valign == VALIGN_BOTTOM)
3089 sy_base = SY + SYSIZE;
3091 sy_base = SY + SYSIZE / 2;
3097 static void setRequestPositionExt(int *x, int *y, int width, int height,
3098 boolean add_border_size)
3100 int border_size = request.border_size;
3101 int sx_base, sy_base;
3104 setRequestBasePosition(&sx_base, &sy_base);
3106 if (request.align == ALIGN_LEFT)
3108 else if (request.align == ALIGN_RIGHT)
3109 sx = sx_base - width;
3111 sx = sx_base - width / 2;
3113 if (request.valign == VALIGN_TOP)
3115 else if (request.valign == VALIGN_BOTTOM)
3116 sy = sy_base - height;
3118 sy = sy_base - height / 2;
3120 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3121 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3123 if (add_border_size)
3133 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3135 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3138 static void DrawEnvelopeRequest(char *text)
3140 char *text_final = text;
3141 char *text_door_style = NULL;
3142 int graphic = IMG_BACKGROUND_REQUEST;
3143 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3144 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3145 int font_nr = FONT_REQUEST;
3146 int font_width = getFontWidth(font_nr);
3147 int font_height = getFontHeight(font_nr);
3148 int border_size = request.border_size;
3149 int line_spacing = request.line_spacing;
3150 int line_height = font_height + line_spacing;
3151 int max_text_width = request.width - 2 * border_size;
3152 int max_text_height = request.height - 2 * border_size;
3153 int line_length = max_text_width / font_width;
3154 int max_lines = max_text_height / line_height;
3155 int text_width = line_length * font_width;
3156 int width = request.width;
3157 int height = request.height;
3158 int tile_size = MAX(request.step_offset, 1);
3159 int x_steps = width / tile_size;
3160 int y_steps = height / tile_size;
3161 int sx_offset = border_size;
3162 int sy_offset = border_size;
3166 if (request.centered)
3167 sx_offset = (request.width - text_width) / 2;
3169 if (request.wrap_single_words && !request.autowrap)
3171 char *src_text_ptr, *dst_text_ptr;
3173 text_door_style = checked_malloc(2 * strlen(text) + 1);
3175 src_text_ptr = text;
3176 dst_text_ptr = text_door_style;
3178 while (*src_text_ptr)
3180 if (*src_text_ptr == ' ' ||
3181 *src_text_ptr == '?' ||
3182 *src_text_ptr == '!')
3183 *dst_text_ptr++ = '\n';
3185 if (*src_text_ptr != ' ')
3186 *dst_text_ptr++ = *src_text_ptr;
3191 *dst_text_ptr = '\0';
3193 text_final = text_door_style;
3196 setRequestPosition(&sx, &sy, FALSE);
3198 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3200 for (y = 0; y < y_steps; y++)
3201 for (x = 0; x < x_steps; x++)
3202 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3203 x, y, x_steps, y_steps,
3204 tile_size, tile_size);
3206 // force DOOR font inside door area
3207 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3209 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3210 line_length, -1, max_lines, line_spacing, mask_mode,
3211 request.autowrap, request.centered, FALSE);
3215 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3216 RedrawGadget(tool_gadget[i]);
3218 // store readily prepared envelope request for later use when animating
3219 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3221 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3223 if (text_door_style)
3224 free(text_door_style);
3227 static void AnimateEnvelopeRequest(int anim_mode, int action)
3229 int graphic = IMG_BACKGROUND_REQUEST;
3230 boolean draw_masked = graphic_info[graphic].draw_masked;
3231 int delay_value_normal = request.step_delay;
3232 int delay_value_fast = delay_value_normal / 2;
3233 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3234 boolean no_delay = (tape.warp_forward);
3235 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3236 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3237 DelayCounter anim_delay = { anim_delay_value };
3239 int tile_size = MAX(request.step_offset, 1);
3240 int max_xsize = request.width / tile_size;
3241 int max_ysize = request.height / tile_size;
3242 int max_xsize_inner = max_xsize - 2;
3243 int max_ysize_inner = max_ysize - 2;
3245 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3246 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3247 int xend = max_xsize_inner;
3248 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3249 int xstep = (xstart < xend ? 1 : 0);
3250 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3252 int end = MAX(xend - xstart, yend - ystart);
3255 if (setup.quick_doors)
3262 for (i = start; i <= end; i++)
3264 int last_frame = end; // last frame of this "for" loop
3265 int x = xstart + i * xstep;
3266 int y = ystart + i * ystep;
3267 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3268 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3269 int xsize_size_left = (xsize - 1) * tile_size;
3270 int ysize_size_top = (ysize - 1) * tile_size;
3271 int max_xsize_pos = (max_xsize - 1) * tile_size;
3272 int max_ysize_pos = (max_ysize - 1) * tile_size;
3273 int width = xsize * tile_size;
3274 int height = ysize * tile_size;
3279 setRequestPosition(&src_x, &src_y, FALSE);
3280 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3282 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3284 for (yy = 0; yy < 2; yy++)
3286 for (xx = 0; xx < 2; xx++)
3288 int src_xx = src_x + xx * max_xsize_pos;
3289 int src_yy = src_y + yy * max_ysize_pos;
3290 int dst_xx = dst_x + xx * xsize_size_left;
3291 int dst_yy = dst_y + yy * ysize_size_top;
3292 int xx_size = (xx ? tile_size : xsize_size_left);
3293 int yy_size = (yy ? tile_size : ysize_size_top);
3296 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3297 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3299 BlitBitmap(bitmap_db_store_2, backbuffer,
3300 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3304 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3306 redraw_mask |= REDRAW_FIELD;
3310 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3313 ClearAutoRepeatKeyEvents();
3316 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3318 int graphic = IMG_BACKGROUND_REQUEST;
3319 int sound_opening = SND_REQUEST_OPENING;
3320 int sound_closing = SND_REQUEST_CLOSING;
3321 int anim_mode_1 = request.anim_mode; // (higher priority)
3322 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3323 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3324 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3325 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3327 if (game_status == GAME_MODE_PLAYING)
3328 BlitScreenToBitmap(backbuffer);
3330 SetDrawtoField(DRAW_TO_BACKBUFFER);
3332 // SetDrawBackgroundMask(REDRAW_NONE);
3334 if (action == ACTION_OPENING)
3336 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3338 if (req_state & REQ_ASK)
3340 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3341 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3342 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3343 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3345 else if (req_state & REQ_CONFIRM)
3347 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3348 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3350 else if (req_state & REQ_PLAYER)
3352 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3353 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3354 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3355 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3358 DrawEnvelopeRequest(text);
3361 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3363 if (action == ACTION_OPENING)
3365 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3367 if (anim_mode == ANIM_DEFAULT)
3368 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3370 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3374 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3376 if (anim_mode != ANIM_NONE)
3377 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3379 if (anim_mode == ANIM_DEFAULT)
3380 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3383 game.envelope_active = FALSE;
3385 if (action == ACTION_CLOSING)
3386 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3388 // SetDrawBackgroundMask(last_draw_background_mask);
3390 redraw_mask |= REDRAW_FIELD;
3394 if (action == ACTION_CLOSING &&
3395 game_status == GAME_MODE_PLAYING &&
3396 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3397 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3400 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3402 if (IS_MM_WALL(element))
3404 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3410 int graphic = el2preimg(element);
3412 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3413 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3418 void DrawLevel(int draw_background_mask)
3422 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3423 SetDrawBackgroundMask(draw_background_mask);
3427 for (x = BX1; x <= BX2; x++)
3428 for (y = BY1; y <= BY2; y++)
3429 DrawScreenField(x, y);
3431 redraw_mask |= REDRAW_FIELD;
3434 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3439 for (x = 0; x < size_x; x++)
3440 for (y = 0; y < size_y; y++)
3441 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3443 redraw_mask |= REDRAW_FIELD;
3446 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3450 for (x = 0; x < size_x; x++)
3451 for (y = 0; y < size_y; y++)
3452 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3454 redraw_mask |= REDRAW_FIELD;
3457 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3459 boolean show_level_border = (BorderElement != EL_EMPTY);
3460 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3461 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3462 int tile_size = preview.tile_size;
3463 int preview_width = preview.xsize * tile_size;
3464 int preview_height = preview.ysize * tile_size;
3465 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3466 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3467 int real_preview_width = real_preview_xsize * tile_size;
3468 int real_preview_height = real_preview_ysize * tile_size;
3469 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3470 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3473 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3476 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3478 dst_x += (preview_width - real_preview_width) / 2;
3479 dst_y += (preview_height - real_preview_height) / 2;
3481 for (x = 0; x < real_preview_xsize; x++)
3483 for (y = 0; y < real_preview_ysize; y++)
3485 int lx = from_x + x + (show_level_border ? -1 : 0);
3486 int ly = from_y + y + (show_level_border ? -1 : 0);
3487 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3488 getBorderElement(lx, ly));
3490 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3491 element, tile_size);
3495 redraw_mask |= REDRAW_FIELD;
3498 #define MICROLABEL_EMPTY 0
3499 #define MICROLABEL_LEVEL_NAME 1
3500 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3501 #define MICROLABEL_LEVEL_AUTHOR 3
3502 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3503 #define MICROLABEL_IMPORTED_FROM 5
3504 #define MICROLABEL_IMPORTED_BY_HEAD 6
3505 #define MICROLABEL_IMPORTED_BY 7
3507 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3509 int max_text_width = SXSIZE;
3510 int font_width = getFontWidth(font_nr);
3512 if (pos->align == ALIGN_CENTER)
3513 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3514 else if (pos->align == ALIGN_RIGHT)
3515 max_text_width = pos->x;
3517 max_text_width = SXSIZE - pos->x;
3519 return max_text_width / font_width;
3522 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3524 char label_text[MAX_OUTPUT_LINESIZE + 1];
3525 int max_len_label_text;
3526 int font_nr = pos->font;
3529 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3532 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3533 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3534 mode == MICROLABEL_IMPORTED_BY_HEAD)
3535 font_nr = pos->font_alt;
3537 max_len_label_text = getMaxTextLength(pos, font_nr);
3539 if (pos->size != -1)
3540 max_len_label_text = pos->size;
3542 for (i = 0; i < max_len_label_text; i++)
3543 label_text[i] = ' ';
3544 label_text[max_len_label_text] = '\0';
3546 if (strlen(label_text) > 0)
3547 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3550 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3551 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3552 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3553 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3554 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3555 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3556 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3557 max_len_label_text);
3558 label_text[max_len_label_text] = '\0';
3560 if (strlen(label_text) > 0)
3561 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3563 redraw_mask |= REDRAW_FIELD;
3566 static void DrawPreviewLevelLabel(int mode)
3568 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3571 static void DrawPreviewLevelInfo(int mode)
3573 if (mode == MICROLABEL_LEVEL_NAME)
3574 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3575 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3576 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3579 static void DrawPreviewLevelExt(boolean restart)
3581 static DelayCounter scroll_delay = { 0 };
3582 static DelayCounter label_delay = { 0 };
3583 static int from_x, from_y, scroll_direction;
3584 static int label_state, label_counter;
3585 boolean show_level_border = (BorderElement != EL_EMPTY);
3586 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3587 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3589 scroll_delay.value = preview.step_delay;
3590 label_delay.value = MICROLEVEL_LABEL_DELAY;
3597 if (preview.anim_mode == ANIM_CENTERED)
3599 if (level_xsize > preview.xsize)
3600 from_x = (level_xsize - preview.xsize) / 2;
3601 if (level_ysize > preview.ysize)
3602 from_y = (level_ysize - preview.ysize) / 2;
3605 from_x += preview.xoffset;
3606 from_y += preview.yoffset;
3608 scroll_direction = MV_RIGHT;
3612 DrawPreviewLevelPlayfield(from_x, from_y);
3613 DrawPreviewLevelLabel(label_state);
3615 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3616 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3618 // initialize delay counters
3619 ResetDelayCounter(&scroll_delay);
3620 ResetDelayCounter(&label_delay);
3622 if (leveldir_current->name)
3624 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3625 char label_text[MAX_OUTPUT_LINESIZE + 1];
3626 int font_nr = pos->font;
3627 int max_len_label_text = getMaxTextLength(pos, font_nr);
3629 if (pos->size != -1)
3630 max_len_label_text = pos->size;
3632 strncpy(label_text, leveldir_current->name, max_len_label_text);
3633 label_text[max_len_label_text] = '\0';
3635 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3636 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3642 // scroll preview level, if needed
3643 if (preview.anim_mode != ANIM_NONE &&
3644 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3645 DelayReached(&scroll_delay))
3647 switch (scroll_direction)
3652 from_x -= preview.step_offset;
3653 from_x = (from_x < 0 ? 0 : from_x);
3656 scroll_direction = MV_UP;
3660 if (from_x < level_xsize - preview.xsize)
3662 from_x += preview.step_offset;
3663 from_x = (from_x > level_xsize - preview.xsize ?
3664 level_xsize - preview.xsize : from_x);
3667 scroll_direction = MV_DOWN;
3673 from_y -= preview.step_offset;
3674 from_y = (from_y < 0 ? 0 : from_y);
3677 scroll_direction = MV_RIGHT;
3681 if (from_y < level_ysize - preview.ysize)
3683 from_y += preview.step_offset;
3684 from_y = (from_y > level_ysize - preview.ysize ?
3685 level_ysize - preview.ysize : from_y);
3688 scroll_direction = MV_LEFT;
3695 DrawPreviewLevelPlayfield(from_x, from_y);
3698 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3699 // redraw micro level label, if needed
3700 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3701 !strEqual(level.author, ANONYMOUS_NAME) &&
3702 !strEqual(level.author, leveldir_current->name) &&
3703 DelayReached(&label_delay))
3705 int max_label_counter = 23;
3707 if (leveldir_current->imported_from != NULL &&
3708 strlen(leveldir_current->imported_from) > 0)
3709 max_label_counter += 14;
3710 if (leveldir_current->imported_by != NULL &&
3711 strlen(leveldir_current->imported_by) > 0)
3712 max_label_counter += 14;
3714 label_counter = (label_counter + 1) % max_label_counter;
3715 label_state = (label_counter >= 0 && label_counter <= 7 ?
3716 MICROLABEL_LEVEL_NAME :
3717 label_counter >= 9 && label_counter <= 12 ?
3718 MICROLABEL_LEVEL_AUTHOR_HEAD :
3719 label_counter >= 14 && label_counter <= 21 ?
3720 MICROLABEL_LEVEL_AUTHOR :
3721 label_counter >= 23 && label_counter <= 26 ?
3722 MICROLABEL_IMPORTED_FROM_HEAD :
3723 label_counter >= 28 && label_counter <= 35 ?
3724 MICROLABEL_IMPORTED_FROM :
3725 label_counter >= 37 && label_counter <= 40 ?
3726 MICROLABEL_IMPORTED_BY_HEAD :
3727 label_counter >= 42 && label_counter <= 49 ?
3728 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3730 if (leveldir_current->imported_from == NULL &&
3731 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3732 label_state == MICROLABEL_IMPORTED_FROM))
3733 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3734 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3736 DrawPreviewLevelLabel(label_state);
3740 void DrawPreviewPlayers(void)
3742 if (game_status != GAME_MODE_MAIN)
3745 // do not draw preview players if level preview redefined, but players aren't
3746 if (preview.redefined && !menu.main.preview_players.redefined)
3749 boolean player_found[MAX_PLAYERS];
3750 int num_players = 0;
3753 for (i = 0; i < MAX_PLAYERS; i++)
3754 player_found[i] = FALSE;
3756 // check which players can be found in the level (simple approach)
3757 for (x = 0; x < lev_fieldx; x++)
3759 for (y = 0; y < lev_fieldy; y++)
3761 int element = level.field[x][y];
3763 if (IS_PLAYER_ELEMENT(element))
3765 int player_nr = GET_PLAYER_NR(element);
3767 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3769 if (!player_found[player_nr])
3772 player_found[player_nr] = TRUE;
3777 struct TextPosInfo *pos = &menu.main.preview_players;
3778 int tile_size = pos->tile_size;
3779 int border_size = pos->border_size;
3780 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3781 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3782 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3783 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3784 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3785 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3786 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3787 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3788 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3789 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3790 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3791 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3793 // clear area in which the players will be drawn
3794 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3795 max_players_width, max_players_height);
3797 if (!network.enabled && !setup.team_mode)
3800 // only draw players if level is suited for team mode
3801 if (num_players < 2)
3804 // draw all players that were found in the level
3805 for (i = 0; i < MAX_PLAYERS; i++)
3807 if (player_found[i])
3809 int graphic = el2img(EL_PLAYER_1 + i);
3811 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3813 xpos += player_xoffset;
3814 ypos += player_yoffset;
3819 void DrawPreviewLevelInitial(void)
3821 DrawPreviewLevelExt(TRUE);
3822 DrawPreviewPlayers();
3825 void DrawPreviewLevelAnimation(void)
3827 DrawPreviewLevelExt(FALSE);
3830 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3831 int border_size, int font_nr)
3833 int graphic = el2img(EL_PLAYER_1 + player_nr);
3834 int font_height = getFontHeight(font_nr);
3835 int player_height = MAX(tile_size, font_height);
3836 int xoffset_text = tile_size + border_size;
3837 int yoffset_text = (player_height - font_height) / 2;
3838 int yoffset_graphic = (player_height - tile_size) / 2;
3839 char *player_name = getNetworkPlayerName(player_nr + 1);
3841 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3843 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3846 static void DrawNetworkPlayersExt(boolean force)
3848 if (game_status != GAME_MODE_MAIN)
3851 if (!network.connected && !force)
3854 // do not draw network players if level preview redefined, but players aren't
3855 if (preview.redefined && !menu.main.network_players.redefined)
3858 int num_players = 0;
3861 for (i = 0; i < MAX_PLAYERS; i++)
3862 if (stored_player[i].connected_network)
3865 struct TextPosInfo *pos = &menu.main.network_players;
3866 int tile_size = pos->tile_size;
3867 int border_size = pos->border_size;
3868 int xoffset_text = tile_size + border_size;
3869 int font_nr = pos->font;
3870 int font_width = getFontWidth(font_nr);
3871 int font_height = getFontHeight(font_nr);
3872 int player_height = MAX(tile_size, font_height);
3873 int player_yoffset = player_height + border_size;
3874 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3875 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3876 int all_players_height = num_players * player_yoffset - border_size;
3877 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3878 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3879 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3881 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3882 max_players_width, max_players_height);
3884 // first draw local network player ...
3885 for (i = 0; i < MAX_PLAYERS; i++)
3887 if (stored_player[i].connected_network &&
3888 stored_player[i].connected_locally)
3890 char *player_name = getNetworkPlayerName(i + 1);
3891 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3892 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3894 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3896 ypos += player_yoffset;
3900 // ... then draw all other network players
3901 for (i = 0; i < MAX_PLAYERS; i++)
3903 if (stored_player[i].connected_network &&
3904 !stored_player[i].connected_locally)
3906 char *player_name = getNetworkPlayerName(i + 1);
3907 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3908 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3910 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3912 ypos += player_yoffset;
3917 void DrawNetworkPlayers(void)
3919 DrawNetworkPlayersExt(FALSE);
3922 void ClearNetworkPlayers(void)
3924 DrawNetworkPlayersExt(TRUE);
3927 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3928 int graphic, int lx, int ly,
3931 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3933 if (mask_mode == USE_MASKING)
3934 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3936 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3939 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3940 int graphic, int sync_frame, int mask_mode)
3942 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3944 if (mask_mode == USE_MASKING)
3945 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3947 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3950 static void DrawGraphicAnimation(int x, int y, int graphic)
3952 int lx = LEVELX(x), ly = LEVELY(y);
3953 int mask_mode = NO_MASKING;
3955 if (!IN_SCR_FIELD(x, y))
3958 if (game.use_masked_elements)
3960 if (Tile[lx][ly] != EL_EMPTY)
3962 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3964 mask_mode = USE_MASKING;
3968 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3969 graphic, lx, ly, mask_mode);
3971 MarkTileDirty(x, y);
3974 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3976 int lx = LEVELX(x), ly = LEVELY(y);
3977 int mask_mode = NO_MASKING;
3979 if (!IN_SCR_FIELD(x, y))
3982 if (game.use_masked_elements)
3984 if (Tile[lx][ly] != EL_EMPTY)
3986 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3988 mask_mode = USE_MASKING;
3992 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3993 graphic, lx, ly, mask_mode);
3995 MarkTileDirty(x, y);
3998 void DrawLevelGraphicAnimation(int x, int y, int graphic)
4000 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4003 void DrawLevelElementAnimation(int x, int y, int element)
4005 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4007 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4010 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
4012 int sx = SCREENX(x), sy = SCREENY(y);
4014 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4017 if (Tile[x][y] == EL_EMPTY)
4018 graphic = el2img(GfxElementEmpty[x][y]);
4020 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4023 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4026 DrawGraphicAnimation(sx, sy, graphic);
4029 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4030 DrawLevelFieldCrumbled(x, y);
4032 if (GFX_CRUMBLED(Tile[x][y]))
4033 DrawLevelFieldCrumbled(x, y);
4037 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4039 int sx = SCREENX(x), sy = SCREENY(y);
4042 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4045 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4047 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4050 DrawGraphicAnimation(sx, sy, graphic);
4052 if (GFX_CRUMBLED(element))
4053 DrawLevelFieldCrumbled(x, y);
4056 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4058 if (player->use_murphy)
4060 // this works only because currently only one player can be "murphy" ...
4061 static int last_horizontal_dir = MV_LEFT;
4062 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4064 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4065 last_horizontal_dir = move_dir;
4067 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4069 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4071 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4077 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
4080 static boolean equalGraphics(int graphic1, int graphic2)
4082 struct GraphicInfo *g1 = &graphic_info[graphic1];
4083 struct GraphicInfo *g2 = &graphic_info[graphic2];
4085 return (g1->bitmap == g2->bitmap &&
4086 g1->src_x == g2->src_x &&
4087 g1->src_y == g2->src_y &&
4088 g1->anim_frames == g2->anim_frames &&
4089 g1->anim_delay == g2->anim_delay &&
4090 g1->anim_mode == g2->anim_mode);
4093 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4097 DRAW_PLAYER_STAGE_INIT = 0,
4098 DRAW_PLAYER_STAGE_LAST_FIELD,
4099 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4100 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4101 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4102 DRAW_PLAYER_STAGE_PLAYER,
4104 DRAW_PLAYER_STAGE_PLAYER,
4105 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4107 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4108 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4110 NUM_DRAW_PLAYER_STAGES
4113 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4115 static int static_last_player_graphic[MAX_PLAYERS];
4116 static int static_last_player_frame[MAX_PLAYERS];
4117 static boolean static_player_is_opaque[MAX_PLAYERS];
4118 static boolean draw_player[MAX_PLAYERS];
4119 int pnr = player->index_nr;
4121 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4123 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4124 static_last_player_frame[pnr] = player->Frame;
4125 static_player_is_opaque[pnr] = FALSE;
4127 draw_player[pnr] = TRUE;
4130 if (!draw_player[pnr])
4134 if (!IN_LEV_FIELD(player->jx, player->jy))
4136 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4137 Debug("draw:DrawPlayerExt", "This should never happen!");
4139 draw_player[pnr] = FALSE;
4145 int last_player_graphic = static_last_player_graphic[pnr];
4146 int last_player_frame = static_last_player_frame[pnr];
4147 boolean player_is_opaque = static_player_is_opaque[pnr];
4149 int jx = player->jx;
4150 int jy = player->jy;
4151 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4152 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4153 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4154 int last_jx = (player->is_moving ? jx - dx : jx);
4155 int last_jy = (player->is_moving ? jy - dy : jy);
4156 int next_jx = jx + dx;
4157 int next_jy = jy + dy;
4158 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4159 int sx = SCREENX(jx);
4160 int sy = SCREENY(jy);
4161 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4162 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4163 int element = Tile[jx][jy];
4164 int last_element = Tile[last_jx][last_jy];
4165 int action = (player->is_pushing ? ACTION_PUSHING :
4166 player->is_digging ? ACTION_DIGGING :
4167 player->is_collecting ? ACTION_COLLECTING :
4168 player->is_moving ? ACTION_MOVING :
4169 player->is_snapping ? ACTION_SNAPPING :
4170 player->is_dropping ? ACTION_DROPPING :
4171 player->is_waiting ? player->action_waiting :
4174 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4176 // ------------------------------------------------------------------------
4177 // initialize drawing the player
4178 // ------------------------------------------------------------------------
4180 draw_player[pnr] = FALSE;
4182 // GfxElement[][] is set to the element the player is digging or collecting;
4183 // remove also for off-screen player if the player is not moving anymore
4184 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4185 GfxElement[jx][jy] = EL_UNDEFINED;
4187 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4190 if (element == EL_EXPLOSION)
4193 InitPlayerGfxAnimation(player, action, move_dir);
4195 draw_player[pnr] = TRUE;
4197 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4199 // ------------------------------------------------------------------------
4200 // draw things in the field the player is leaving, if needed
4201 // ------------------------------------------------------------------------
4203 if (!IN_SCR_FIELD(sx, sy))
4204 draw_player[pnr] = FALSE;
4206 if (!player->is_moving)
4209 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4211 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4213 if (last_element == EL_DYNAMITE_ACTIVE ||
4214 last_element == EL_EM_DYNAMITE_ACTIVE ||
4215 last_element == EL_SP_DISK_RED_ACTIVE)
4216 DrawDynamite(last_jx, last_jy);
4218 DrawLevelFieldThruMask(last_jx, last_jy);
4220 else if (last_element == EL_DYNAMITE_ACTIVE ||
4221 last_element == EL_EM_DYNAMITE_ACTIVE ||
4222 last_element == EL_SP_DISK_RED_ACTIVE)
4223 DrawDynamite(last_jx, last_jy);
4225 DrawLevelField(last_jx, last_jy);
4227 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4229 // ------------------------------------------------------------------------
4230 // draw things behind the player, if needed
4231 // ------------------------------------------------------------------------
4235 DrawLevelElement(jx, jy, Back[jx][jy]);
4240 if (IS_ACTIVE_BOMB(element))
4242 DrawLevelElement(jx, jy, EL_EMPTY);
4247 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4249 int old_element = GfxElement[jx][jy];
4250 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4251 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4253 if (GFX_CRUMBLED(old_element))
4254 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4256 DrawScreenGraphic(sx, sy, old_graphic, frame);
4258 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4259 static_player_is_opaque[pnr] = TRUE;
4263 GfxElement[jx][jy] = EL_UNDEFINED;
4265 // make sure that pushed elements are drawn with correct frame rate
4266 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4268 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4269 GfxFrame[jx][jy] = player->StepFrame;
4271 DrawLevelField(jx, jy);
4274 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4276 // ------------------------------------------------------------------------
4277 // draw things the player is pushing, if needed
4278 // ------------------------------------------------------------------------
4280 if (!player->is_pushing || !player->is_moving)
4283 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4286 int gfx_frame = GfxFrame[jx][jy];
4288 if (!IS_MOVING(jx, jy)) // push movement already finished
4290 element = Tile[next_jx][next_jy];
4291 gfx_frame = GfxFrame[next_jx][next_jy];
4294 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4295 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4296 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4298 // draw background element under pushed element (like the Sokoban field)
4299 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4301 // this allows transparent pushing animation over non-black background
4304 DrawLevelElement(jx, jy, Back[jx][jy]);
4306 DrawLevelElement(jx, jy, EL_EMPTY);
4309 if (Back[next_jx][next_jy])
4310 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4312 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4314 int px = SCREENX(jx), py = SCREENY(jy);
4315 int pxx = (TILEX - ABS(sxx)) * dx;
4316 int pyy = (TILEY - ABS(syy)) * dy;
4319 // do not draw (EM style) pushing animation when pushing is finished
4320 // (two-tile animations usually do not contain start and end frame)
4321 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4322 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4324 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4326 // masked drawing is needed for EMC style (double) movement graphics
4327 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4328 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4331 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4333 // ------------------------------------------------------------------------
4334 // draw player himself
4335 // ------------------------------------------------------------------------
4337 int graphic = getPlayerGraphic(player, move_dir);
4339 // in the case of changed player action or direction, prevent the current
4340 // animation frame from being restarted for identical animations
4341 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4342 player->Frame = last_player_frame;
4344 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4346 if (player_is_opaque)
4347 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4349 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4351 if (SHIELD_ON(player))
4353 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4354 IMG_SHIELD_NORMAL_ACTIVE);
4355 frame = getGraphicAnimationFrame(graphic, -1);
4357 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4360 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4362 // ------------------------------------------------------------------------
4363 // draw things in front of player (active dynamite or dynabombs)
4364 // ------------------------------------------------------------------------
4366 if (IS_ACTIVE_BOMB(element))
4368 int graphic = el2img(element);
4369 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4371 if (game.emulation == EMU_SUPAPLEX)
4372 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4374 DrawGraphicThruMask(sx, sy, graphic, frame);
4377 if (player_is_moving && last_element == EL_EXPLOSION)
4379 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4380 GfxElement[last_jx][last_jy] : EL_EMPTY);
4381 int graphic = el_act2img(element, ACTION_EXPLODING);
4382 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4383 int phase = ExplodePhase[last_jx][last_jy] - 1;
4384 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4387 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4390 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4392 // ------------------------------------------------------------------------
4393 // draw elements the player is just walking/passing through/under
4394 // ------------------------------------------------------------------------
4396 if (player_is_moving)
4398 // handle the field the player is leaving ...
4399 if (IS_ACCESSIBLE_INSIDE(last_element))
4400 DrawLevelField(last_jx, last_jy);
4401 else if (IS_ACCESSIBLE_UNDER(last_element))
4402 DrawLevelFieldThruMask(last_jx, last_jy);
4405 // do not redraw accessible elements if the player is just pushing them
4406 if (!player_is_moving || !player->is_pushing)
4408 // ... and the field the player is entering
4409 if (IS_ACCESSIBLE_INSIDE(element))
4410 DrawLevelField(jx, jy);
4411 else if (IS_ACCESSIBLE_UNDER(element))
4412 DrawLevelFieldThruMask(jx, jy);
4415 MarkTileDirty(sx, sy);
4419 void DrawPlayer(struct PlayerInfo *player)
4423 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4424 DrawPlayerExt(player, i);
4427 void DrawAllPlayers(void)
4431 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4432 for (j = 0; j < MAX_PLAYERS; j++)
4433 if (stored_player[j].active)
4434 DrawPlayerExt(&stored_player[j], i);
4437 void DrawPlayerField(int x, int y)
4439 if (!IS_PLAYER(x, y))
4442 DrawPlayer(PLAYERINFO(x, y));
4445 // ----------------------------------------------------------------------------
4447 void WaitForEventToContinue(void)
4449 boolean first_wait = TRUE;
4450 boolean still_wait = TRUE;
4452 if (program.headless)
4455 // simulate releasing mouse button over last gadget, if still pressed
4457 HandleGadgets(-1, -1, 0);
4459 button_status = MB_RELEASED;
4462 ClearPlayerAction();
4468 if (NextValidEvent(&event))
4472 case EVENT_BUTTONPRESS:
4473 case EVENT_FINGERPRESS:
4477 case EVENT_BUTTONRELEASE:
4478 case EVENT_FINGERRELEASE:
4479 still_wait = first_wait;
4482 case EVENT_KEYPRESS:
4483 case SDL_CONTROLLERBUTTONDOWN:
4484 case SDL_JOYBUTTONDOWN:
4489 HandleOtherEvents(&event);
4493 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4498 if (!PendingEvent())
4503 #define MAX_REQUEST_LINES 13
4504 #define MAX_REQUEST_LINE_FONT1_LEN 7
4505 #define MAX_REQUEST_LINE_FONT2_LEN 10
4507 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4509 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4511 int draw_buffer_last = GetDrawtoField();
4512 int width = request.width;
4513 int height = request.height;
4517 // when showing request dialog after game ended, deactivate game panel
4518 if (game_just_ended)
4519 game.panel.active = FALSE;
4521 game.request_active = TRUE;
4523 setRequestPosition(&sx, &sy, FALSE);
4525 button_status = MB_RELEASED;
4527 request_gadget_id = -1;
4532 boolean event_handled = FALSE;
4534 if (game_just_ended)
4536 SetDrawtoField(draw_buffer_game);
4538 HandleGameActions();
4540 SetDrawtoField(DRAW_TO_BACKBUFFER);
4542 if (global.use_envelope_request)
4544 // copy current state of request area to middle of playfield area
4545 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4553 while (NextValidEvent(&event))
4555 event_handled = TRUE;
4559 case EVENT_BUTTONPRESS:
4560 case EVENT_BUTTONRELEASE:
4561 case EVENT_MOTIONNOTIFY:
4565 if (event.type == EVENT_MOTIONNOTIFY)
4570 motion_status = TRUE;
4571 mx = ((MotionEvent *) &event)->x;
4572 my = ((MotionEvent *) &event)->y;
4576 motion_status = FALSE;
4577 mx = ((ButtonEvent *) &event)->x;
4578 my = ((ButtonEvent *) &event)->y;
4579 if (event.type == EVENT_BUTTONPRESS)
4580 button_status = ((ButtonEvent *) &event)->button;
4582 button_status = MB_RELEASED;
4585 // this sets 'request_gadget_id'
4586 HandleGadgets(mx, my, button_status);
4588 switch (request_gadget_id)
4590 case TOOL_CTRL_ID_YES:
4591 case TOOL_CTRL_ID_TOUCH_YES:
4594 case TOOL_CTRL_ID_NO:
4595 case TOOL_CTRL_ID_TOUCH_NO:
4598 case TOOL_CTRL_ID_CONFIRM:
4599 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4600 result = TRUE | FALSE;
4603 case TOOL_CTRL_ID_PLAYER_1:
4606 case TOOL_CTRL_ID_PLAYER_2:
4609 case TOOL_CTRL_ID_PLAYER_3:
4612 case TOOL_CTRL_ID_PLAYER_4:
4617 // only check clickable animations if no request gadget clicked
4618 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4625 case SDL_WINDOWEVENT:
4626 HandleWindowEvent((WindowEvent *) &event);
4629 case SDL_APP_WILLENTERBACKGROUND:
4630 case SDL_APP_DIDENTERBACKGROUND:
4631 case SDL_APP_WILLENTERFOREGROUND:
4632 case SDL_APP_DIDENTERFOREGROUND:
4633 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4636 case EVENT_KEYPRESS:
4638 Key key = GetEventKey((KeyEvent *)&event);
4643 if (req_state & REQ_CONFIRM)
4652 #if defined(KSYM_Rewind)
4653 case KSYM_Rewind: // for Amazon Fire TV remote
4662 #if defined(KSYM_FastForward)
4663 case KSYM_FastForward: // for Amazon Fire TV remote
4669 HandleKeysDebug(key, KEY_PRESSED);
4673 if (req_state & REQ_PLAYER)
4675 int old_player_nr = setup.network_player_nr;
4678 result = old_player_nr + 1;
4683 result = old_player_nr + 1;
4714 case EVENT_FINGERRELEASE:
4715 case EVENT_KEYRELEASE:
4716 ClearPlayerAction();
4719 case SDL_CONTROLLERBUTTONDOWN:
4720 switch (event.cbutton.button)
4722 case SDL_CONTROLLER_BUTTON_A:
4723 case SDL_CONTROLLER_BUTTON_X:
4724 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4725 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4729 case SDL_CONTROLLER_BUTTON_B:
4730 case SDL_CONTROLLER_BUTTON_Y:
4731 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4732 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4733 case SDL_CONTROLLER_BUTTON_BACK:
4738 if (req_state & REQ_PLAYER)
4740 int old_player_nr = setup.network_player_nr;
4743 result = old_player_nr + 1;
4745 switch (event.cbutton.button)
4747 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4748 case SDL_CONTROLLER_BUTTON_Y:
4752 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4753 case SDL_CONTROLLER_BUTTON_B:
4757 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4758 case SDL_CONTROLLER_BUTTON_A:
4762 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4763 case SDL_CONTROLLER_BUTTON_X:
4774 case SDL_CONTROLLERBUTTONUP:
4775 HandleJoystickEvent(&event);
4776 ClearPlayerAction();
4780 HandleOtherEvents(&event);
4785 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4787 int joy = AnyJoystick();
4789 if (joy & JOY_BUTTON_1)
4791 else if (joy & JOY_BUTTON_2)
4794 else if (AnyJoystick())
4796 int joy = AnyJoystick();
4798 if (req_state & REQ_PLAYER)
4802 else if (joy & JOY_RIGHT)
4804 else if (joy & JOY_DOWN)
4806 else if (joy & JOY_LEFT)
4813 if (game_just_ended)
4815 if (global.use_envelope_request)
4817 // copy back current state of pressed buttons inside request area
4818 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4822 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4828 SetDrawtoField(draw_buffer_last);
4830 game.request_active = FALSE;
4835 static boolean RequestDoor(char *text, unsigned int req_state)
4837 int draw_buffer_last = GetDrawtoField();
4838 unsigned int old_door_state;
4839 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4840 int font_nr = FONT_TEXT_2;
4845 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4847 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4848 font_nr = FONT_TEXT_1;
4851 if (game_status == GAME_MODE_PLAYING)
4852 BlitScreenToBitmap(backbuffer);
4854 // disable deactivated drawing when quick-loading level tape recording
4855 if (tape.playing && tape.deactivate_display)
4856 TapeDeactivateDisplayOff(TRUE);
4858 SetMouseCursor(CURSOR_DEFAULT);
4860 // pause network game while waiting for request to answer
4861 if (network.enabled &&
4862 game_status == GAME_MODE_PLAYING &&
4863 !game.all_players_gone &&
4864 req_state & REQUEST_WAIT_FOR_INPUT)
4865 SendToServer_PausePlaying();
4867 old_door_state = GetDoorState();
4869 // simulate releasing mouse button over last gadget, if still pressed
4871 HandleGadgets(-1, -1, 0);
4875 // draw released gadget before proceeding
4878 if (old_door_state & DOOR_OPEN_1)
4880 CloseDoor(DOOR_CLOSE_1);
4882 // save old door content
4883 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4884 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4887 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4888 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4890 // clear door drawing field
4891 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4893 // force DOOR font inside door area
4894 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4896 // write text for request
4897 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4899 char text_line[max_request_line_len + 1];
4905 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4907 tc = *(text_ptr + tx);
4908 // if (!tc || tc == ' ')
4909 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4913 if ((tc == '?' || tc == '!') && tl == 0)
4923 strncpy(text_line, text_ptr, tl);
4926 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4927 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4928 text_line, font_nr);
4930 text_ptr += tl + (tc == ' ' ? 1 : 0);
4931 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4936 if (req_state & REQ_ASK)
4938 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4939 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4940 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4941 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4943 else if (req_state & REQ_CONFIRM)
4945 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4946 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4948 else if (req_state & REQ_PLAYER)
4950 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4951 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4952 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4953 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4956 // copy request gadgets to door backbuffer
4957 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4959 OpenDoor(DOOR_OPEN_1);
4961 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4963 if (game_status == GAME_MODE_PLAYING)
4965 SetPanelBackground();
4966 SetDrawBackgroundMask(REDRAW_DOOR_1);
4970 SetDrawBackgroundMask(REDRAW_FIELD);
4976 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4978 // ---------- handle request buttons ----------
4979 result = RequestHandleEvents(req_state, draw_buffer_last);
4983 if (!(req_state & REQ_STAY_OPEN))
4985 CloseDoor(DOOR_CLOSE_1);
4987 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4988 (req_state & REQ_REOPEN))
4989 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4994 if (game_status == GAME_MODE_PLAYING)
4996 SetPanelBackground();
4997 SetDrawBackgroundMask(REDRAW_DOOR_1);
5001 SetDrawBackgroundMask(REDRAW_FIELD);
5004 // continue network game after request
5005 if (network.enabled &&
5006 game_status == GAME_MODE_PLAYING &&
5007 !game.all_players_gone &&
5008 req_state & REQUEST_WAIT_FOR_INPUT)
5009 SendToServer_ContinuePlaying();
5011 // restore deactivated drawing when quick-loading level tape recording
5012 if (tape.playing && tape.deactivate_display)
5013 TapeDeactivateDisplayOn();
5018 static boolean RequestEnvelope(char *text, unsigned int req_state)
5020 int draw_buffer_last = GetDrawtoField();
5023 if (game_status == GAME_MODE_PLAYING)
5024 BlitScreenToBitmap(backbuffer);
5026 // disable deactivated drawing when quick-loading level tape recording
5027 if (tape.playing && tape.deactivate_display)
5028 TapeDeactivateDisplayOff(TRUE);
5030 SetMouseCursor(CURSOR_DEFAULT);
5032 // pause network game while waiting for request to answer
5033 if (network.enabled &&
5034 game_status == GAME_MODE_PLAYING &&
5035 !game.all_players_gone &&
5036 req_state & REQUEST_WAIT_FOR_INPUT)
5037 SendToServer_PausePlaying();
5039 // simulate releasing mouse button over last gadget, if still pressed
5041 HandleGadgets(-1, -1, 0);
5045 // (replace with setting corresponding request background)
5046 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5047 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5049 // clear door drawing field
5050 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5052 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5054 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5056 if (game_status == GAME_MODE_PLAYING)
5058 SetPanelBackground();
5059 SetDrawBackgroundMask(REDRAW_DOOR_1);
5063 SetDrawBackgroundMask(REDRAW_FIELD);
5069 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5071 // ---------- handle request buttons ----------
5072 result = RequestHandleEvents(req_state, draw_buffer_last);
5076 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5080 if (game_status == GAME_MODE_PLAYING)
5082 SetPanelBackground();
5083 SetDrawBackgroundMask(REDRAW_DOOR_1);
5087 SetDrawBackgroundMask(REDRAW_FIELD);
5090 // continue network game after request
5091 if (network.enabled &&
5092 game_status == GAME_MODE_PLAYING &&
5093 !game.all_players_gone &&
5094 req_state & REQUEST_WAIT_FOR_INPUT)
5095 SendToServer_ContinuePlaying();
5097 // restore deactivated drawing when quick-loading level tape recording
5098 if (tape.playing && tape.deactivate_display)
5099 TapeDeactivateDisplayOn();
5104 boolean Request(char *text, unsigned int req_state)
5106 boolean overlay_enabled = GetOverlayEnabled();
5109 game.request_active_or_moving = TRUE;
5111 SetOverlayEnabled(FALSE);
5113 if (global.use_envelope_request)
5114 result = RequestEnvelope(text, req_state);
5116 result = RequestDoor(text, req_state);
5118 SetOverlayEnabled(overlay_enabled);
5120 game.request_active_or_moving = FALSE;
5125 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5127 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5128 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5131 if (dpo1->sort_priority != dpo2->sort_priority)
5132 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5134 compare_result = dpo1->nr - dpo2->nr;
5136 return compare_result;
5139 void InitGraphicCompatibilityInfo_Doors(void)
5145 struct DoorInfo *door;
5149 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5150 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5152 { -1, -1, -1, NULL }
5154 struct Rect door_rect_list[] =
5156 { DX, DY, DXSIZE, DYSIZE },
5157 { VX, VY, VXSIZE, VYSIZE }
5161 for (i = 0; doors[i].door_token != -1; i++)
5163 int door_token = doors[i].door_token;
5164 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5165 int part_1 = doors[i].part_1;
5166 int part_8 = doors[i].part_8;
5167 int part_2 = part_1 + 1;
5168 int part_3 = part_1 + 2;
5169 struct DoorInfo *door = doors[i].door;
5170 struct Rect *door_rect = &door_rect_list[door_index];
5171 boolean door_gfx_redefined = FALSE;
5173 // check if any door part graphic definitions have been redefined
5175 for (j = 0; door_part_controls[j].door_token != -1; j++)
5177 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5178 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5180 if (dpc->door_token == door_token && fi->redefined)
5181 door_gfx_redefined = TRUE;
5184 // check for old-style door graphic/animation modifications
5186 if (!door_gfx_redefined)
5188 if (door->anim_mode & ANIM_STATIC_PANEL)
5190 door->panel.step_xoffset = 0;
5191 door->panel.step_yoffset = 0;
5194 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5196 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5197 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5198 int num_door_steps, num_panel_steps;
5200 // remove door part graphics other than the two default wings
5202 for (j = 0; door_part_controls[j].door_token != -1; j++)
5204 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5205 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5207 if (dpc->graphic >= part_3 &&
5208 dpc->graphic <= part_8)
5212 // set graphics and screen positions of the default wings
5214 g_part_1->width = door_rect->width;
5215 g_part_1->height = door_rect->height;
5216 g_part_2->width = door_rect->width;
5217 g_part_2->height = door_rect->height;
5218 g_part_2->src_x = door_rect->width;
5219 g_part_2->src_y = g_part_1->src_y;
5221 door->part_2.x = door->part_1.x;
5222 door->part_2.y = door->part_1.y;
5224 if (door->width != -1)
5226 g_part_1->width = door->width;
5227 g_part_2->width = door->width;
5229 // special treatment for graphics and screen position of right wing
5230 g_part_2->src_x += door_rect->width - door->width;
5231 door->part_2.x += door_rect->width - door->width;
5234 if (door->height != -1)
5236 g_part_1->height = door->height;
5237 g_part_2->height = door->height;
5239 // special treatment for graphics and screen position of bottom wing
5240 g_part_2->src_y += door_rect->height - door->height;
5241 door->part_2.y += door_rect->height - door->height;
5244 // set animation delays for the default wings and panels
5246 door->part_1.step_delay = door->step_delay;
5247 door->part_2.step_delay = door->step_delay;
5248 door->panel.step_delay = door->step_delay;
5250 // set animation draw order for the default wings
5252 door->part_1.sort_priority = 2; // draw left wing over ...
5253 door->part_2.sort_priority = 1; // ... right wing
5255 // set animation draw offset for the default wings
5257 if (door->anim_mode & ANIM_HORIZONTAL)
5259 door->part_1.step_xoffset = door->step_offset;
5260 door->part_1.step_yoffset = 0;
5261 door->part_2.step_xoffset = door->step_offset * -1;
5262 door->part_2.step_yoffset = 0;
5264 num_door_steps = g_part_1->width / door->step_offset;
5266 else // ANIM_VERTICAL
5268 door->part_1.step_xoffset = 0;
5269 door->part_1.step_yoffset = door->step_offset;
5270 door->part_2.step_xoffset = 0;
5271 door->part_2.step_yoffset = door->step_offset * -1;
5273 num_door_steps = g_part_1->height / door->step_offset;
5276 // set animation draw offset for the default panels
5278 if (door->step_offset > 1)
5280 num_panel_steps = 2 * door_rect->height / door->step_offset;
5281 door->panel.start_step = num_panel_steps - num_door_steps;
5282 door->panel.start_step_closing = door->panel.start_step;
5286 num_panel_steps = door_rect->height / door->step_offset;
5287 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5288 door->panel.start_step_closing = door->panel.start_step;
5289 door->panel.step_delay *= 2;
5296 void InitDoors(void)
5300 for (i = 0; door_part_controls[i].door_token != -1; i++)
5302 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5303 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5305 // initialize "start_step_opening" and "start_step_closing", if needed
5306 if (dpc->pos->start_step_opening == 0 &&
5307 dpc->pos->start_step_closing == 0)
5309 // dpc->pos->start_step_opening = dpc->pos->start_step;
5310 dpc->pos->start_step_closing = dpc->pos->start_step;
5313 // fill structure for door part draw order (sorted below)
5315 dpo->sort_priority = dpc->pos->sort_priority;
5318 // sort door part controls according to sort_priority and graphic number
5319 qsort(door_part_order, MAX_DOOR_PARTS,
5320 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5323 unsigned int OpenDoor(unsigned int door_state)
5325 if (door_state & DOOR_COPY_BACK)
5327 if (door_state & DOOR_OPEN_1)
5328 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5329 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5331 if (door_state & DOOR_OPEN_2)
5332 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5333 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5335 door_state &= ~DOOR_COPY_BACK;
5338 return MoveDoor(door_state);
5341 unsigned int CloseDoor(unsigned int door_state)
5343 unsigned int old_door_state = GetDoorState();
5345 if (!(door_state & DOOR_NO_COPY_BACK))
5347 if (old_door_state & DOOR_OPEN_1)
5348 BlitBitmap(backbuffer, bitmap_db_door_1,
5349 DX, DY, DXSIZE, DYSIZE, 0, 0);
5351 if (old_door_state & DOOR_OPEN_2)
5352 BlitBitmap(backbuffer, bitmap_db_door_2,
5353 VX, VY, VXSIZE, VYSIZE, 0, 0);
5355 door_state &= ~DOOR_NO_COPY_BACK;
5358 return MoveDoor(door_state);
5361 unsigned int GetDoorState(void)
5363 return MoveDoor(DOOR_GET_STATE);
5366 unsigned int SetDoorState(unsigned int door_state)
5368 return MoveDoor(door_state | DOOR_SET_STATE);
5371 static int euclid(int a, int b)
5373 return (b ? euclid(b, a % b) : a);
5376 unsigned int MoveDoor(unsigned int door_state)
5378 struct Rect door_rect_list[] =
5380 { DX, DY, DXSIZE, DYSIZE },
5381 { VX, VY, VXSIZE, VYSIZE }
5383 static int door1 = DOOR_CLOSE_1;
5384 static int door2 = DOOR_CLOSE_2;
5385 DelayCounter door_delay = { 0 };
5388 if (door_state == DOOR_GET_STATE)
5389 return (door1 | door2);
5391 if (door_state & DOOR_SET_STATE)
5393 if (door_state & DOOR_ACTION_1)
5394 door1 = door_state & DOOR_ACTION_1;
5395 if (door_state & DOOR_ACTION_2)
5396 door2 = door_state & DOOR_ACTION_2;
5398 return (door1 | door2);
5401 if (!(door_state & DOOR_FORCE_REDRAW))
5403 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5404 door_state &= ~DOOR_OPEN_1;
5405 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5406 door_state &= ~DOOR_CLOSE_1;
5407 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5408 door_state &= ~DOOR_OPEN_2;
5409 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5410 door_state &= ~DOOR_CLOSE_2;
5413 if (global.autoplay_leveldir)
5415 door_state |= DOOR_NO_DELAY;
5416 door_state &= ~DOOR_CLOSE_ALL;
5419 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5420 door_state |= DOOR_NO_DELAY;
5422 if (door_state & DOOR_ACTION)
5424 boolean door_panel_drawn[NUM_DOORS];
5425 boolean panel_has_doors[NUM_DOORS];
5426 boolean door_part_skip[MAX_DOOR_PARTS];
5427 boolean door_part_done[MAX_DOOR_PARTS];
5428 boolean door_part_done_all;
5429 int num_steps[MAX_DOOR_PARTS];
5430 int max_move_delay = 0; // delay for complete animations of all doors
5431 int max_step_delay = 0; // delay (ms) between two animation frames
5432 int num_move_steps = 0; // number of animation steps for all doors
5433 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5434 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5438 for (i = 0; i < NUM_DOORS; i++)
5439 panel_has_doors[i] = FALSE;
5441 for (i = 0; i < MAX_DOOR_PARTS; i++)
5443 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5444 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5445 int door_token = dpc->door_token;
5447 door_part_done[i] = FALSE;
5448 door_part_skip[i] = (!(door_state & door_token) ||
5452 for (i = 0; i < MAX_DOOR_PARTS; i++)
5454 int nr = door_part_order[i].nr;
5455 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5456 struct DoorPartPosInfo *pos = dpc->pos;
5457 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5458 int door_token = dpc->door_token;
5459 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5460 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5461 int step_xoffset = ABS(pos->step_xoffset);
5462 int step_yoffset = ABS(pos->step_yoffset);
5463 int step_delay = pos->step_delay;
5464 int current_door_state = door_state & door_token;
5465 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5466 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5467 boolean part_opening = (is_panel ? door_closing : door_opening);
5468 int start_step = (part_opening ? pos->start_step_opening :
5469 pos->start_step_closing);
5470 float move_xsize = (step_xoffset ? g->width : 0);
5471 float move_ysize = (step_yoffset ? g->height : 0);
5472 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5473 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5474 int move_steps = (move_xsteps && move_ysteps ?
5475 MIN(move_xsteps, move_ysteps) :
5476 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5477 int move_delay = move_steps * step_delay;
5479 if (door_part_skip[nr])
5482 max_move_delay = MAX(max_move_delay, move_delay);
5483 max_step_delay = (max_step_delay == 0 ? step_delay :
5484 euclid(max_step_delay, step_delay));
5485 num_steps[nr] = move_steps;
5489 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5491 panel_has_doors[door_index] = TRUE;
5495 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5497 num_move_steps = max_move_delay / max_step_delay;
5498 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5500 door_delay.value = max_step_delay;
5502 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5504 start = num_move_steps - 1;
5508 // opening door sound has priority over simultaneously closing door
5509 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5511 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5513 if (door_state & DOOR_OPEN_1)
5514 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5515 if (door_state & DOOR_OPEN_2)
5516 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5518 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5520 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5522 if (door_state & DOOR_CLOSE_1)
5523 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5524 if (door_state & DOOR_CLOSE_2)
5525 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5529 for (k = start; k < num_move_steps; k++)
5531 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5533 door_part_done_all = TRUE;
5535 for (i = 0; i < NUM_DOORS; i++)
5536 door_panel_drawn[i] = FALSE;
5538 for (i = 0; i < MAX_DOOR_PARTS; i++)
5540 int nr = door_part_order[i].nr;
5541 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5542 struct DoorPartPosInfo *pos = dpc->pos;
5543 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5544 int door_token = dpc->door_token;
5545 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5546 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5547 boolean is_panel_and_door_has_closed = FALSE;
5548 struct Rect *door_rect = &door_rect_list[door_index];
5549 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5551 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5552 int current_door_state = door_state & door_token;
5553 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5554 boolean door_closing = !door_opening;
5555 boolean part_opening = (is_panel ? door_closing : door_opening);
5556 boolean part_closing = !part_opening;
5557 int start_step = (part_opening ? pos->start_step_opening :
5558 pos->start_step_closing);
5559 int step_delay = pos->step_delay;
5560 int step_factor = step_delay / max_step_delay;
5561 int k1 = (step_factor ? k / step_factor + 1 : k);
5562 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5563 int kk = MAX(0, k2);
5566 int src_x, src_y, src_xx, src_yy;
5567 int dst_x, dst_y, dst_xx, dst_yy;
5570 if (door_part_skip[nr])
5573 if (!(door_state & door_token))
5581 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5582 int kk_door = MAX(0, k2_door);
5583 int sync_frame = kk_door * door_delay.value;
5584 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5586 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5587 &g_src_x, &g_src_y);
5592 if (!door_panel_drawn[door_index])
5594 ClearRectangle(drawto, door_rect->x, door_rect->y,
5595 door_rect->width, door_rect->height);
5597 door_panel_drawn[door_index] = TRUE;
5600 // draw opening or closing door parts
5602 if (pos->step_xoffset < 0) // door part on right side
5605 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5608 if (dst_xx + width > door_rect->width)
5609 width = door_rect->width - dst_xx;
5611 else // door part on left side
5614 dst_xx = pos->x - kk * pos->step_xoffset;
5618 src_xx = ABS(dst_xx);
5622 width = g->width - src_xx;
5624 if (width > door_rect->width)
5625 width = door_rect->width;
5627 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5630 if (pos->step_yoffset < 0) // door part on bottom side
5633 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5636 if (dst_yy + height > door_rect->height)
5637 height = door_rect->height - dst_yy;
5639 else // door part on top side
5642 dst_yy = pos->y - kk * pos->step_yoffset;
5646 src_yy = ABS(dst_yy);
5650 height = g->height - src_yy;
5653 src_x = g_src_x + src_xx;
5654 src_y = g_src_y + src_yy;
5656 dst_x = door_rect->x + dst_xx;
5657 dst_y = door_rect->y + dst_yy;
5659 is_panel_and_door_has_closed =
5662 panel_has_doors[door_index] &&
5663 k >= num_move_steps_doors_only - 1);
5665 if (width >= 0 && width <= g->width &&
5666 height >= 0 && height <= g->height &&
5667 !is_panel_and_door_has_closed)
5669 if (is_panel || !pos->draw_masked)
5670 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5673 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5677 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5679 if ((part_opening && (width < 0 || height < 0)) ||
5680 (part_closing && (width >= g->width && height >= g->height)))
5681 door_part_done[nr] = TRUE;
5683 // continue door part animations, but not panel after door has closed
5684 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5685 door_part_done_all = FALSE;
5688 if (!(door_state & DOOR_NO_DELAY))
5692 SkipUntilDelayReached(&door_delay, &k, last_frame);
5694 // prevent OS (Windows) from complaining about program not responding
5698 if (door_part_done_all)
5702 if (!(door_state & DOOR_NO_DELAY))
5704 // wait for specified door action post delay
5705 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5706 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5707 else if (door_state & DOOR_ACTION_1)
5708 door_delay.value = door_1.post_delay;
5709 else if (door_state & DOOR_ACTION_2)
5710 door_delay.value = door_2.post_delay;
5712 while (!DelayReached(&door_delay))
5717 if (door_state & DOOR_ACTION_1)
5718 door1 = door_state & DOOR_ACTION_1;
5719 if (door_state & DOOR_ACTION_2)
5720 door2 = door_state & DOOR_ACTION_2;
5722 // draw masked border over door area
5723 DrawMaskedBorder(REDRAW_DOOR_1);
5724 DrawMaskedBorder(REDRAW_DOOR_2);
5726 ClearAutoRepeatKeyEvents();
5728 return (door1 | door2);
5731 static boolean useSpecialEditorDoor(void)
5733 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5734 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5736 // do not draw special editor door if editor border defined or redefined
5737 if (graphic_info[graphic].bitmap != NULL || redefined)
5740 // do not draw special editor door if global border defined to be empty
5741 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5744 // do not draw special editor door if viewport definitions do not match
5748 EY + EYSIZE != VY + VYSIZE)
5754 void DrawSpecialEditorDoor(void)
5756 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5757 int top_border_width = gfx1->width;
5758 int top_border_height = gfx1->height;
5759 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5760 int ex = EX - outer_border;
5761 int ey = EY - outer_border;
5762 int vy = VY - outer_border;
5763 int exsize = EXSIZE + 2 * outer_border;
5765 if (!useSpecialEditorDoor())
5768 // draw bigger level editor toolbox window
5769 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5770 top_border_width, top_border_height, ex, ey - top_border_height);
5771 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5772 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5774 redraw_mask |= REDRAW_ALL;
5777 void UndrawSpecialEditorDoor(void)
5779 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5780 int top_border_width = gfx1->width;
5781 int top_border_height = gfx1->height;
5782 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5783 int ex = EX - outer_border;
5784 int ey = EY - outer_border;
5785 int ey_top = ey - top_border_height;
5786 int exsize = EXSIZE + 2 * outer_border;
5787 int eysize = EYSIZE + 2 * outer_border;
5789 if (!useSpecialEditorDoor())
5792 // draw normal tape recorder window
5793 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5795 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5796 ex, ey_top, top_border_width, top_border_height,
5798 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5799 ex, ey, exsize, eysize, ex, ey);
5803 // if screen background is set to "[NONE]", clear editor toolbox window
5804 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5805 ClearRectangle(drawto, ex, ey, exsize, eysize);
5808 redraw_mask |= REDRAW_ALL;
5812 // ---------- new tool button stuff -------------------------------------------
5817 struct TextPosInfo *pos;
5819 boolean is_touch_button;
5821 } toolbutton_info[NUM_TOOL_BUTTONS] =
5824 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5825 TOOL_CTRL_ID_YES, FALSE, "yes"
5828 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5829 TOOL_CTRL_ID_NO, FALSE, "no"
5832 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5833 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5836 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5837 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5840 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5841 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5844 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5845 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5848 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5849 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5852 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5853 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5856 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5857 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5860 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5861 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5865 void CreateToolButtons(void)
5869 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5871 int graphic = toolbutton_info[i].graphic;
5872 struct GraphicInfo *gfx = &graphic_info[graphic];
5873 struct TextPosInfo *pos = toolbutton_info[i].pos;
5874 struct GadgetInfo *gi;
5875 Bitmap *deco_bitmap = None;
5876 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5877 unsigned int event_mask = GD_EVENT_RELEASED;
5878 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5879 int base_x = (is_touch_button ? 0 : DX);
5880 int base_y = (is_touch_button ? 0 : DY);
5881 int gd_x = gfx->src_x;
5882 int gd_y = gfx->src_y;
5883 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5884 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5889 // do not use touch buttons if overlay touch buttons are disabled
5890 if (is_touch_button && !setup.touch.overlay_buttons)
5893 if (global.use_envelope_request && !is_touch_button)
5895 setRequestPosition(&base_x, &base_y, TRUE);
5897 // check if request buttons are outside of envelope and fix, if needed
5898 if (x < 0 || x + gfx->width > request.width ||
5899 y < 0 || y + gfx->height > request.height)
5901 if (id == TOOL_CTRL_ID_YES)
5904 y = request.height - 2 * request.border_size - gfx->height;
5906 else if (id == TOOL_CTRL_ID_NO)
5908 x = request.width - 2 * request.border_size - gfx->width;
5909 y = request.height - 2 * request.border_size - gfx->height;
5911 else if (id == TOOL_CTRL_ID_CONFIRM)
5913 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5914 y = request.height - 2 * request.border_size - gfx->height;
5916 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5918 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5920 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5921 y = request.height - 2 * request.border_size - gfx->height * 2;
5923 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5924 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5929 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5932 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5934 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5935 pos->size, &deco_bitmap, &deco_x, &deco_y);
5936 deco_xpos = (gfx->width - pos->size) / 2;
5937 deco_ypos = (gfx->height - pos->size) / 2;
5940 gi = CreateGadget(GDI_CUSTOM_ID, id,
5941 GDI_IMAGE_ID, graphic,
5942 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5945 GDI_WIDTH, gfx->width,
5946 GDI_HEIGHT, gfx->height,
5947 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5948 GDI_STATE, GD_BUTTON_UNPRESSED,
5949 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5950 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5951 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5952 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5953 GDI_DECORATION_SIZE, pos->size, pos->size,
5954 GDI_DECORATION_SHIFTING, 1, 1,
5955 GDI_DIRECT_DRAW, FALSE,
5956 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5957 GDI_EVENT_MASK, event_mask,
5958 GDI_CALLBACK_ACTION, HandleToolButtons,
5962 Fail("cannot create gadget");
5964 tool_gadget[id] = gi;
5968 void FreeToolButtons(void)
5972 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5973 FreeGadget(tool_gadget[i]);
5976 static void UnmapToolButtons(void)
5980 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5981 UnmapGadget(tool_gadget[i]);
5984 static void HandleToolButtons(struct GadgetInfo *gi)
5986 request_gadget_id = gi->custom_id;
5989 static struct Mapping_EM_to_RND_object
5992 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5993 boolean is_backside; // backside of moving element
5999 em_object_mapping_list[GAME_TILE_MAX + 1] =
6002 Zborder, FALSE, FALSE,
6006 Zplayer, FALSE, FALSE,
6015 Ztank, FALSE, FALSE,
6019 Zeater, FALSE, FALSE,
6023 Zdynamite, FALSE, FALSE,
6027 Zboom, FALSE, FALSE,
6032 Xchain, FALSE, FALSE,
6033 EL_DEFAULT, ACTION_EXPLODING, -1
6036 Xboom_bug, FALSE, FALSE,
6037 EL_BUG, ACTION_EXPLODING, -1
6040 Xboom_tank, FALSE, FALSE,
6041 EL_SPACESHIP, ACTION_EXPLODING, -1
6044 Xboom_android, FALSE, FALSE,
6045 EL_EMC_ANDROID, ACTION_OTHER, -1
6048 Xboom_1, FALSE, FALSE,
6049 EL_DEFAULT, ACTION_EXPLODING, -1
6052 Xboom_2, FALSE, FALSE,
6053 EL_DEFAULT, ACTION_EXPLODING, -1
6057 Xblank, TRUE, FALSE,
6062 Xsplash_e, FALSE, FALSE,
6063 EL_ACID_SPLASH_RIGHT, -1, -1
6066 Xsplash_w, FALSE, FALSE,
6067 EL_ACID_SPLASH_LEFT, -1, -1
6071 Xplant, TRUE, FALSE,
6072 EL_EMC_PLANT, -1, -1
6075 Yplant, FALSE, FALSE,
6076 EL_EMC_PLANT, -1, -1
6080 Xacid_1, TRUE, FALSE,
6084 Xacid_2, FALSE, FALSE,
6088 Xacid_3, FALSE, FALSE,
6092 Xacid_4, FALSE, FALSE,
6096 Xacid_5, FALSE, FALSE,
6100 Xacid_6, FALSE, FALSE,
6104 Xacid_7, FALSE, FALSE,
6108 Xacid_8, FALSE, FALSE,
6113 Xfake_acid_1, TRUE, FALSE,
6114 EL_EMC_FAKE_ACID, -1, -1
6117 Xfake_acid_2, FALSE, FALSE,
6118 EL_EMC_FAKE_ACID, -1, -1
6121 Xfake_acid_3, FALSE, FALSE,
6122 EL_EMC_FAKE_ACID, -1, -1
6125 Xfake_acid_4, FALSE, FALSE,
6126 EL_EMC_FAKE_ACID, -1, -1
6129 Xfake_acid_5, FALSE, FALSE,
6130 EL_EMC_FAKE_ACID, -1, -1
6133 Xfake_acid_6, FALSE, FALSE,
6134 EL_EMC_FAKE_ACID, -1, -1
6137 Xfake_acid_7, FALSE, FALSE,
6138 EL_EMC_FAKE_ACID, -1, -1
6141 Xfake_acid_8, FALSE, FALSE,
6142 EL_EMC_FAKE_ACID, -1, -1
6146 Xfake_acid_1_player, FALSE, FALSE,
6147 EL_EMC_FAKE_ACID, -1, -1
6150 Xfake_acid_2_player, FALSE, FALSE,
6151 EL_EMC_FAKE_ACID, -1, -1
6154 Xfake_acid_3_player, FALSE, FALSE,
6155 EL_EMC_FAKE_ACID, -1, -1
6158 Xfake_acid_4_player, FALSE, FALSE,
6159 EL_EMC_FAKE_ACID, -1, -1
6162 Xfake_acid_5_player, FALSE, FALSE,
6163 EL_EMC_FAKE_ACID, -1, -1
6166 Xfake_acid_6_player, FALSE, FALSE,
6167 EL_EMC_FAKE_ACID, -1, -1
6170 Xfake_acid_7_player, FALSE, FALSE,
6171 EL_EMC_FAKE_ACID, -1, -1
6174 Xfake_acid_8_player, FALSE, FALSE,
6175 EL_EMC_FAKE_ACID, -1, -1
6179 Xgrass, TRUE, FALSE,
6180 EL_EMC_GRASS, -1, -1
6183 Ygrass_nB, FALSE, FALSE,
6184 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6187 Ygrass_eB, FALSE, FALSE,
6188 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6191 Ygrass_sB, FALSE, FALSE,
6192 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6195 Ygrass_wB, FALSE, FALSE,
6196 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6204 Ydirt_nB, FALSE, FALSE,
6205 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6208 Ydirt_eB, FALSE, FALSE,
6209 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6212 Ydirt_sB, FALSE, FALSE,
6213 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6216 Ydirt_wB, FALSE, FALSE,
6217 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6221 Xandroid, TRUE, FALSE,
6222 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6225 Xandroid_1_n, FALSE, FALSE,
6226 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6229 Xandroid_2_n, FALSE, FALSE,
6230 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6233 Xandroid_1_e, FALSE, FALSE,
6234 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6237 Xandroid_2_e, FALSE, FALSE,
6238 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6241 Xandroid_1_w, FALSE, FALSE,
6242 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6245 Xandroid_2_w, FALSE, FALSE,
6246 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6249 Xandroid_1_s, FALSE, FALSE,
6250 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6253 Xandroid_2_s, FALSE, FALSE,
6254 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6257 Yandroid_n, FALSE, FALSE,
6258 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6261 Yandroid_nB, FALSE, TRUE,
6262 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6265 Yandroid_ne, FALSE, FALSE,
6266 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6269 Yandroid_neB, FALSE, TRUE,
6270 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6273 Yandroid_e, FALSE, FALSE,
6274 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6277 Yandroid_eB, FALSE, TRUE,
6278 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6281 Yandroid_se, FALSE, FALSE,
6282 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6285 Yandroid_seB, FALSE, TRUE,
6286 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6289 Yandroid_s, FALSE, FALSE,
6290 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6293 Yandroid_sB, FALSE, TRUE,
6294 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6297 Yandroid_sw, FALSE, FALSE,
6298 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6301 Yandroid_swB, FALSE, TRUE,
6302 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6305 Yandroid_w, FALSE, FALSE,
6306 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6309 Yandroid_wB, FALSE, TRUE,
6310 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6313 Yandroid_nw, FALSE, FALSE,
6314 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6317 Yandroid_nwB, FALSE, TRUE,
6318 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6322 Xeater_n, TRUE, FALSE,
6323 EL_YAMYAM_UP, -1, -1
6326 Xeater_e, TRUE, FALSE,
6327 EL_YAMYAM_RIGHT, -1, -1
6330 Xeater_w, TRUE, FALSE,
6331 EL_YAMYAM_LEFT, -1, -1
6334 Xeater_s, TRUE, FALSE,
6335 EL_YAMYAM_DOWN, -1, -1
6338 Yeater_n, FALSE, FALSE,
6339 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6342 Yeater_nB, FALSE, TRUE,
6343 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6346 Yeater_e, FALSE, FALSE,
6347 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6350 Yeater_eB, FALSE, TRUE,
6351 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6354 Yeater_s, FALSE, FALSE,
6355 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6358 Yeater_sB, FALSE, TRUE,
6359 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6362 Yeater_w, FALSE, FALSE,
6363 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6366 Yeater_wB, FALSE, TRUE,
6367 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6370 Yeater_stone, FALSE, FALSE,
6371 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6374 Yeater_spring, FALSE, FALSE,
6375 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6379 Xalien, TRUE, FALSE,
6383 Xalien_pause, FALSE, FALSE,
6387 Yalien_n, FALSE, FALSE,
6388 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6391 Yalien_nB, FALSE, TRUE,
6392 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6395 Yalien_e, FALSE, FALSE,
6396 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6399 Yalien_eB, FALSE, TRUE,
6400 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6403 Yalien_s, FALSE, FALSE,
6404 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6407 Yalien_sB, FALSE, TRUE,
6408 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6411 Yalien_w, FALSE, FALSE,
6412 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6415 Yalien_wB, FALSE, TRUE,
6416 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6419 Yalien_stone, FALSE, FALSE,
6420 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6423 Yalien_spring, FALSE, FALSE,
6424 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6428 Xbug_1_n, TRUE, FALSE,
6432 Xbug_1_e, TRUE, FALSE,
6433 EL_BUG_RIGHT, -1, -1
6436 Xbug_1_s, TRUE, FALSE,
6440 Xbug_1_w, TRUE, FALSE,
6444 Xbug_2_n, FALSE, FALSE,
6448 Xbug_2_e, FALSE, FALSE,
6449 EL_BUG_RIGHT, -1, -1
6452 Xbug_2_s, FALSE, FALSE,
6456 Xbug_2_w, FALSE, FALSE,
6460 Ybug_n, FALSE, FALSE,
6461 EL_BUG, ACTION_MOVING, MV_BIT_UP
6464 Ybug_nB, FALSE, TRUE,
6465 EL_BUG, ACTION_MOVING, MV_BIT_UP
6468 Ybug_e, FALSE, FALSE,
6469 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6472 Ybug_eB, FALSE, TRUE,
6473 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6476 Ybug_s, FALSE, FALSE,
6477 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6480 Ybug_sB, FALSE, TRUE,
6481 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6484 Ybug_w, FALSE, FALSE,
6485 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6488 Ybug_wB, FALSE, TRUE,
6489 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6492 Ybug_w_n, FALSE, FALSE,
6493 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6496 Ybug_n_e, FALSE, FALSE,
6497 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6500 Ybug_e_s, FALSE, FALSE,
6501 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6504 Ybug_s_w, FALSE, FALSE,
6505 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6508 Ybug_e_n, FALSE, FALSE,
6509 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6512 Ybug_s_e, FALSE, FALSE,
6513 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6516 Ybug_w_s, FALSE, FALSE,
6517 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6520 Ybug_n_w, FALSE, FALSE,
6521 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6524 Ybug_stone, FALSE, FALSE,
6525 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6528 Ybug_spring, FALSE, FALSE,
6529 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6533 Xtank_1_n, TRUE, FALSE,
6534 EL_SPACESHIP_UP, -1, -1
6537 Xtank_1_e, TRUE, FALSE,
6538 EL_SPACESHIP_RIGHT, -1, -1
6541 Xtank_1_s, TRUE, FALSE,
6542 EL_SPACESHIP_DOWN, -1, -1
6545 Xtank_1_w, TRUE, FALSE,
6546 EL_SPACESHIP_LEFT, -1, -1
6549 Xtank_2_n, FALSE, FALSE,
6550 EL_SPACESHIP_UP, -1, -1
6553 Xtank_2_e, FALSE, FALSE,
6554 EL_SPACESHIP_RIGHT, -1, -1
6557 Xtank_2_s, FALSE, FALSE,
6558 EL_SPACESHIP_DOWN, -1, -1
6561 Xtank_2_w, FALSE, FALSE,
6562 EL_SPACESHIP_LEFT, -1, -1
6565 Ytank_n, FALSE, FALSE,
6566 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6569 Ytank_nB, FALSE, TRUE,
6570 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6573 Ytank_e, FALSE, FALSE,
6574 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6577 Ytank_eB, FALSE, TRUE,
6578 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6581 Ytank_s, FALSE, FALSE,
6582 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6585 Ytank_sB, FALSE, TRUE,
6586 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6589 Ytank_w, FALSE, FALSE,
6590 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6593 Ytank_wB, FALSE, TRUE,
6594 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6597 Ytank_w_n, FALSE, FALSE,
6598 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6601 Ytank_n_e, FALSE, FALSE,
6602 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6605 Ytank_e_s, FALSE, FALSE,
6606 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6609 Ytank_s_w, FALSE, FALSE,
6610 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6613 Ytank_e_n, FALSE, FALSE,
6614 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6617 Ytank_s_e, FALSE, FALSE,
6618 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6621 Ytank_w_s, FALSE, FALSE,
6622 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6625 Ytank_n_w, FALSE, FALSE,
6626 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6629 Ytank_stone, FALSE, FALSE,
6630 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6633 Ytank_spring, FALSE, FALSE,
6634 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6638 Xemerald, TRUE, FALSE,
6642 Xemerald_pause, FALSE, FALSE,
6646 Xemerald_fall, FALSE, FALSE,
6650 Xemerald_shine, FALSE, FALSE,
6651 EL_EMERALD, ACTION_TWINKLING, -1
6654 Yemerald_s, FALSE, FALSE,
6655 EL_EMERALD, ACTION_FALLING, -1
6658 Yemerald_sB, FALSE, TRUE,
6659 EL_EMERALD, ACTION_FALLING, -1
6662 Yemerald_e, FALSE, FALSE,
6663 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6666 Yemerald_eB, FALSE, TRUE,
6667 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6670 Yemerald_w, FALSE, FALSE,
6671 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6674 Yemerald_wB, FALSE, TRUE,
6675 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6678 Yemerald_blank, FALSE, FALSE,
6679 EL_EMERALD, ACTION_COLLECTING, -1
6683 Xdiamond, TRUE, FALSE,
6687 Xdiamond_pause, FALSE, FALSE,
6691 Xdiamond_fall, FALSE, FALSE,
6695 Xdiamond_shine, FALSE, FALSE,
6696 EL_DIAMOND, ACTION_TWINKLING, -1
6699 Ydiamond_s, FALSE, FALSE,
6700 EL_DIAMOND, ACTION_FALLING, -1
6703 Ydiamond_sB, FALSE, TRUE,
6704 EL_DIAMOND, ACTION_FALLING, -1
6707 Ydiamond_e, FALSE, FALSE,
6708 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6711 Ydiamond_eB, FALSE, TRUE,
6712 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6715 Ydiamond_w, FALSE, FALSE,
6716 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6719 Ydiamond_wB, FALSE, TRUE,
6720 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6723 Ydiamond_blank, FALSE, FALSE,
6724 EL_DIAMOND, ACTION_COLLECTING, -1
6727 Ydiamond_stone, FALSE, FALSE,
6728 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6732 Xstone, TRUE, FALSE,
6736 Xstone_pause, FALSE, FALSE,
6740 Xstone_fall, FALSE, FALSE,
6744 Ystone_s, FALSE, FALSE,
6745 EL_ROCK, ACTION_FALLING, -1
6748 Ystone_sB, FALSE, TRUE,
6749 EL_ROCK, ACTION_FALLING, -1
6752 Ystone_e, FALSE, FALSE,
6753 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6756 Ystone_eB, FALSE, TRUE,
6757 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6760 Ystone_w, FALSE, FALSE,
6761 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6764 Ystone_wB, FALSE, TRUE,
6765 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6773 Xbomb_pause, FALSE, FALSE,
6777 Xbomb_fall, FALSE, FALSE,
6781 Ybomb_s, FALSE, FALSE,
6782 EL_BOMB, ACTION_FALLING, -1
6785 Ybomb_sB, FALSE, TRUE,
6786 EL_BOMB, ACTION_FALLING, -1
6789 Ybomb_e, FALSE, FALSE,
6790 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6793 Ybomb_eB, FALSE, TRUE,
6794 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6797 Ybomb_w, FALSE, FALSE,
6798 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6801 Ybomb_wB, FALSE, TRUE,
6802 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6805 Ybomb_blank, FALSE, FALSE,
6806 EL_BOMB, ACTION_ACTIVATING, -1
6814 Xnut_pause, FALSE, FALSE,
6818 Xnut_fall, FALSE, FALSE,
6822 Ynut_s, FALSE, FALSE,
6823 EL_NUT, ACTION_FALLING, -1
6826 Ynut_sB, FALSE, TRUE,
6827 EL_NUT, ACTION_FALLING, -1
6830 Ynut_e, FALSE, FALSE,
6831 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6834 Ynut_eB, FALSE, TRUE,
6835 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6838 Ynut_w, FALSE, FALSE,
6839 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6842 Ynut_wB, FALSE, TRUE,
6843 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6846 Ynut_stone, FALSE, FALSE,
6847 EL_NUT, ACTION_BREAKING, -1
6851 Xspring, TRUE, FALSE,
6855 Xspring_pause, FALSE, FALSE,
6859 Xspring_e, TRUE, FALSE,
6860 EL_SPRING_RIGHT, -1, -1
6863 Xspring_w, TRUE, FALSE,
6864 EL_SPRING_LEFT, -1, -1
6867 Xspring_fall, FALSE, FALSE,
6871 Yspring_s, FALSE, FALSE,
6872 EL_SPRING, ACTION_FALLING, -1
6875 Yspring_sB, FALSE, TRUE,
6876 EL_SPRING, ACTION_FALLING, -1
6879 Yspring_e, FALSE, FALSE,
6880 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6883 Yspring_eB, FALSE, TRUE,
6884 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6887 Yspring_w, FALSE, FALSE,
6888 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6891 Yspring_wB, FALSE, TRUE,
6892 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6895 Yspring_alien_e, FALSE, FALSE,
6896 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6899 Yspring_alien_eB, FALSE, TRUE,
6900 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6903 Yspring_alien_w, FALSE, FALSE,
6904 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6907 Yspring_alien_wB, FALSE, TRUE,
6908 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6912 Xpush_emerald_e, FALSE, FALSE,
6913 EL_EMERALD, -1, MV_BIT_RIGHT
6916 Xpush_emerald_w, FALSE, FALSE,
6917 EL_EMERALD, -1, MV_BIT_LEFT
6920 Xpush_diamond_e, FALSE, FALSE,
6921 EL_DIAMOND, -1, MV_BIT_RIGHT
6924 Xpush_diamond_w, FALSE, FALSE,
6925 EL_DIAMOND, -1, MV_BIT_LEFT
6928 Xpush_stone_e, FALSE, FALSE,
6929 EL_ROCK, -1, MV_BIT_RIGHT
6932 Xpush_stone_w, FALSE, FALSE,
6933 EL_ROCK, -1, MV_BIT_LEFT
6936 Xpush_bomb_e, FALSE, FALSE,
6937 EL_BOMB, -1, MV_BIT_RIGHT
6940 Xpush_bomb_w, FALSE, FALSE,
6941 EL_BOMB, -1, MV_BIT_LEFT
6944 Xpush_nut_e, FALSE, FALSE,
6945 EL_NUT, -1, MV_BIT_RIGHT
6948 Xpush_nut_w, FALSE, FALSE,
6949 EL_NUT, -1, MV_BIT_LEFT
6952 Xpush_spring_e, FALSE, FALSE,
6953 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6956 Xpush_spring_w, FALSE, FALSE,
6957 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6961 Xdynamite, TRUE, FALSE,
6962 EL_EM_DYNAMITE, -1, -1
6965 Ydynamite_blank, FALSE, FALSE,
6966 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6969 Xdynamite_1, TRUE, FALSE,
6970 EL_EM_DYNAMITE_ACTIVE, -1, -1
6973 Xdynamite_2, FALSE, FALSE,
6974 EL_EM_DYNAMITE_ACTIVE, -1, -1
6977 Xdynamite_3, FALSE, FALSE,
6978 EL_EM_DYNAMITE_ACTIVE, -1, -1
6981 Xdynamite_4, FALSE, FALSE,
6982 EL_EM_DYNAMITE_ACTIVE, -1, -1
6986 Xkey_1, TRUE, FALSE,
6990 Xkey_2, TRUE, FALSE,
6994 Xkey_3, TRUE, FALSE,
6998 Xkey_4, TRUE, FALSE,
7002 Xkey_5, TRUE, FALSE,
7003 EL_EMC_KEY_5, -1, -1
7006 Xkey_6, TRUE, FALSE,
7007 EL_EMC_KEY_6, -1, -1
7010 Xkey_7, TRUE, FALSE,
7011 EL_EMC_KEY_7, -1, -1
7014 Xkey_8, TRUE, FALSE,
7015 EL_EMC_KEY_8, -1, -1
7019 Xdoor_1, TRUE, FALSE,
7020 EL_EM_GATE_1, -1, -1
7023 Xdoor_2, TRUE, FALSE,
7024 EL_EM_GATE_2, -1, -1
7027 Xdoor_3, TRUE, FALSE,
7028 EL_EM_GATE_3, -1, -1
7031 Xdoor_4, TRUE, FALSE,
7032 EL_EM_GATE_4, -1, -1
7035 Xdoor_5, TRUE, FALSE,
7036 EL_EMC_GATE_5, -1, -1
7039 Xdoor_6, TRUE, FALSE,
7040 EL_EMC_GATE_6, -1, -1
7043 Xdoor_7, TRUE, FALSE,
7044 EL_EMC_GATE_7, -1, -1
7047 Xdoor_8, TRUE, FALSE,
7048 EL_EMC_GATE_8, -1, -1
7052 Xfake_door_1, TRUE, FALSE,
7053 EL_EM_GATE_1_GRAY, -1, -1
7056 Xfake_door_2, TRUE, FALSE,
7057 EL_EM_GATE_2_GRAY, -1, -1
7060 Xfake_door_3, TRUE, FALSE,
7061 EL_EM_GATE_3_GRAY, -1, -1
7064 Xfake_door_4, TRUE, FALSE,
7065 EL_EM_GATE_4_GRAY, -1, -1
7068 Xfake_door_5, TRUE, FALSE,
7069 EL_EMC_GATE_5_GRAY, -1, -1
7072 Xfake_door_6, TRUE, FALSE,
7073 EL_EMC_GATE_6_GRAY, -1, -1
7076 Xfake_door_7, TRUE, FALSE,
7077 EL_EMC_GATE_7_GRAY, -1, -1
7080 Xfake_door_8, TRUE, FALSE,
7081 EL_EMC_GATE_8_GRAY, -1, -1
7085 Xballoon, TRUE, FALSE,
7089 Yballoon_n, FALSE, FALSE,
7090 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7093 Yballoon_nB, FALSE, TRUE,
7094 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7097 Yballoon_e, FALSE, FALSE,
7098 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7101 Yballoon_eB, FALSE, TRUE,
7102 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7105 Yballoon_s, FALSE, FALSE,
7106 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7109 Yballoon_sB, FALSE, TRUE,
7110 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7113 Yballoon_w, FALSE, FALSE,
7114 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7117 Yballoon_wB, FALSE, TRUE,
7118 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7122 Xball_1, TRUE, FALSE,
7123 EL_EMC_MAGIC_BALL, -1, -1
7126 Yball_1, FALSE, FALSE,
7127 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7130 Xball_2, FALSE, FALSE,
7131 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7134 Yball_2, FALSE, FALSE,
7135 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7138 Yball_blank, FALSE, FALSE,
7139 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7143 Xamoeba_1, TRUE, FALSE,
7144 EL_AMOEBA_DRY, ACTION_OTHER, -1
7147 Xamoeba_2, FALSE, FALSE,
7148 EL_AMOEBA_DRY, ACTION_OTHER, -1
7151 Xamoeba_3, FALSE, FALSE,
7152 EL_AMOEBA_DRY, ACTION_OTHER, -1
7155 Xamoeba_4, FALSE, FALSE,
7156 EL_AMOEBA_DRY, ACTION_OTHER, -1
7159 Xamoeba_5, TRUE, FALSE,
7160 EL_AMOEBA_WET, ACTION_OTHER, -1
7163 Xamoeba_6, FALSE, FALSE,
7164 EL_AMOEBA_WET, ACTION_OTHER, -1
7167 Xamoeba_7, FALSE, FALSE,
7168 EL_AMOEBA_WET, ACTION_OTHER, -1
7171 Xamoeba_8, FALSE, FALSE,
7172 EL_AMOEBA_WET, ACTION_OTHER, -1
7177 EL_AMOEBA_DROP, ACTION_GROWING, -1
7180 Xdrip_fall, FALSE, FALSE,
7181 EL_AMOEBA_DROP, -1, -1
7184 Xdrip_stretch, FALSE, FALSE,
7185 EL_AMOEBA_DROP, ACTION_FALLING, -1
7188 Xdrip_stretchB, FALSE, TRUE,
7189 EL_AMOEBA_DROP, ACTION_FALLING, -1
7192 Ydrip_1_s, FALSE, FALSE,
7193 EL_AMOEBA_DROP, ACTION_FALLING, -1
7196 Ydrip_1_sB, FALSE, TRUE,
7197 EL_AMOEBA_DROP, ACTION_FALLING, -1
7200 Ydrip_2_s, FALSE, FALSE,
7201 EL_AMOEBA_DROP, ACTION_FALLING, -1
7204 Ydrip_2_sB, FALSE, TRUE,
7205 EL_AMOEBA_DROP, ACTION_FALLING, -1
7209 Xwonderwall, TRUE, FALSE,
7210 EL_MAGIC_WALL, -1, -1
7213 Ywonderwall, FALSE, FALSE,
7214 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7218 Xwheel, TRUE, FALSE,
7219 EL_ROBOT_WHEEL, -1, -1
7222 Ywheel, FALSE, FALSE,
7223 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7227 Xswitch, TRUE, FALSE,
7228 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7231 Yswitch, FALSE, FALSE,
7232 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7236 Xbumper, TRUE, FALSE,
7237 EL_EMC_SPRING_BUMPER, -1, -1
7240 Ybumper, FALSE, FALSE,
7241 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7245 Xacid_nw, TRUE, FALSE,
7246 EL_ACID_POOL_TOPLEFT, -1, -1
7249 Xacid_ne, TRUE, FALSE,
7250 EL_ACID_POOL_TOPRIGHT, -1, -1
7253 Xacid_sw, TRUE, FALSE,
7254 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7257 Xacid_s, TRUE, FALSE,
7258 EL_ACID_POOL_BOTTOM, -1, -1
7261 Xacid_se, TRUE, FALSE,
7262 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7266 Xfake_blank, TRUE, FALSE,
7267 EL_INVISIBLE_WALL, -1, -1
7270 Yfake_blank, FALSE, FALSE,
7271 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7275 Xfake_grass, TRUE, FALSE,
7276 EL_EMC_FAKE_GRASS, -1, -1
7279 Yfake_grass, FALSE, FALSE,
7280 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7284 Xfake_amoeba, TRUE, FALSE,
7285 EL_EMC_DRIPPER, -1, -1
7288 Yfake_amoeba, FALSE, FALSE,
7289 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7293 Xlenses, TRUE, FALSE,
7294 EL_EMC_LENSES, -1, -1
7298 Xmagnify, TRUE, FALSE,
7299 EL_EMC_MAGNIFIER, -1, -1
7304 EL_QUICKSAND_EMPTY, -1, -1
7307 Xsand_stone, TRUE, FALSE,
7308 EL_QUICKSAND_FULL, -1, -1
7311 Xsand_stonein_1, FALSE, TRUE,
7312 EL_ROCK, ACTION_FILLING, -1
7315 Xsand_stonein_2, FALSE, TRUE,
7316 EL_ROCK, ACTION_FILLING, -1
7319 Xsand_stonein_3, FALSE, TRUE,
7320 EL_ROCK, ACTION_FILLING, -1
7323 Xsand_stonein_4, FALSE, TRUE,
7324 EL_ROCK, ACTION_FILLING, -1
7327 Xsand_sandstone_1, FALSE, FALSE,
7328 EL_QUICKSAND_FILLING, -1, -1
7331 Xsand_sandstone_2, FALSE, FALSE,
7332 EL_QUICKSAND_FILLING, -1, -1
7335 Xsand_sandstone_3, FALSE, FALSE,
7336 EL_QUICKSAND_FILLING, -1, -1
7339 Xsand_sandstone_4, FALSE, FALSE,
7340 EL_QUICKSAND_FILLING, -1, -1
7343 Xsand_stonesand_1, FALSE, FALSE,
7344 EL_QUICKSAND_EMPTYING, -1, -1
7347 Xsand_stonesand_2, FALSE, FALSE,
7348 EL_QUICKSAND_EMPTYING, -1, -1
7351 Xsand_stonesand_3, FALSE, FALSE,
7352 EL_QUICKSAND_EMPTYING, -1, -1
7355 Xsand_stonesand_4, FALSE, FALSE,
7356 EL_QUICKSAND_EMPTYING, -1, -1
7359 Xsand_stoneout_1, FALSE, FALSE,
7360 EL_ROCK, ACTION_EMPTYING, -1
7363 Xsand_stoneout_2, FALSE, FALSE,
7364 EL_ROCK, ACTION_EMPTYING, -1
7367 Xsand_stonesand_quickout_1, FALSE, FALSE,
7368 EL_QUICKSAND_EMPTYING, -1, -1
7371 Xsand_stonesand_quickout_2, FALSE, FALSE,
7372 EL_QUICKSAND_EMPTYING, -1, -1
7376 Xslide_ns, TRUE, FALSE,
7377 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7380 Yslide_ns_blank, FALSE, FALSE,
7381 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7384 Xslide_ew, TRUE, FALSE,
7385 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7388 Yslide_ew_blank, FALSE, FALSE,
7389 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7393 Xwind_n, TRUE, FALSE,
7394 EL_BALLOON_SWITCH_UP, -1, -1
7397 Xwind_e, TRUE, FALSE,
7398 EL_BALLOON_SWITCH_RIGHT, -1, -1
7401 Xwind_s, TRUE, FALSE,
7402 EL_BALLOON_SWITCH_DOWN, -1, -1
7405 Xwind_w, TRUE, FALSE,
7406 EL_BALLOON_SWITCH_LEFT, -1, -1
7409 Xwind_any, TRUE, FALSE,
7410 EL_BALLOON_SWITCH_ANY, -1, -1
7413 Xwind_stop, TRUE, FALSE,
7414 EL_BALLOON_SWITCH_NONE, -1, -1
7419 EL_EM_EXIT_CLOSED, -1, -1
7422 Xexit_1, TRUE, FALSE,
7423 EL_EM_EXIT_OPEN, -1, -1
7426 Xexit_2, FALSE, FALSE,
7427 EL_EM_EXIT_OPEN, -1, -1
7430 Xexit_3, FALSE, FALSE,
7431 EL_EM_EXIT_OPEN, -1, -1
7435 Xpause, FALSE, FALSE,
7440 Xwall_1, TRUE, FALSE,
7444 Xwall_2, TRUE, FALSE,
7445 EL_EMC_WALL_14, -1, -1
7448 Xwall_3, TRUE, FALSE,
7449 EL_EMC_WALL_15, -1, -1
7452 Xwall_4, TRUE, FALSE,
7453 EL_EMC_WALL_16, -1, -1
7457 Xroundwall_1, TRUE, FALSE,
7458 EL_WALL_SLIPPERY, -1, -1
7461 Xroundwall_2, TRUE, FALSE,
7462 EL_EMC_WALL_SLIPPERY_2, -1, -1
7465 Xroundwall_3, TRUE, FALSE,
7466 EL_EMC_WALL_SLIPPERY_3, -1, -1
7469 Xroundwall_4, TRUE, FALSE,
7470 EL_EMC_WALL_SLIPPERY_4, -1, -1
7474 Xsteel_1, TRUE, FALSE,
7475 EL_STEELWALL, -1, -1
7478 Xsteel_2, TRUE, FALSE,
7479 EL_EMC_STEELWALL_2, -1, -1
7482 Xsteel_3, TRUE, FALSE,
7483 EL_EMC_STEELWALL_3, -1, -1
7486 Xsteel_4, TRUE, FALSE,
7487 EL_EMC_STEELWALL_4, -1, -1
7491 Xdecor_1, TRUE, FALSE,
7492 EL_EMC_WALL_8, -1, -1
7495 Xdecor_2, TRUE, FALSE,
7496 EL_EMC_WALL_6, -1, -1
7499 Xdecor_3, TRUE, FALSE,
7500 EL_EMC_WALL_4, -1, -1
7503 Xdecor_4, TRUE, FALSE,
7504 EL_EMC_WALL_7, -1, -1
7507 Xdecor_5, TRUE, FALSE,
7508 EL_EMC_WALL_5, -1, -1
7511 Xdecor_6, TRUE, FALSE,
7512 EL_EMC_WALL_9, -1, -1
7515 Xdecor_7, TRUE, FALSE,
7516 EL_EMC_WALL_10, -1, -1
7519 Xdecor_8, TRUE, FALSE,
7520 EL_EMC_WALL_1, -1, -1
7523 Xdecor_9, TRUE, FALSE,
7524 EL_EMC_WALL_2, -1, -1
7527 Xdecor_10, TRUE, FALSE,
7528 EL_EMC_WALL_3, -1, -1
7531 Xdecor_11, TRUE, FALSE,
7532 EL_EMC_WALL_11, -1, -1
7535 Xdecor_12, TRUE, FALSE,
7536 EL_EMC_WALL_12, -1, -1
7540 Xalpha_0, TRUE, FALSE,
7541 EL_CHAR('0'), -1, -1
7544 Xalpha_1, TRUE, FALSE,
7545 EL_CHAR('1'), -1, -1
7548 Xalpha_2, TRUE, FALSE,
7549 EL_CHAR('2'), -1, -1
7552 Xalpha_3, TRUE, FALSE,
7553 EL_CHAR('3'), -1, -1
7556 Xalpha_4, TRUE, FALSE,
7557 EL_CHAR('4'), -1, -1
7560 Xalpha_5, TRUE, FALSE,
7561 EL_CHAR('5'), -1, -1
7564 Xalpha_6, TRUE, FALSE,
7565 EL_CHAR('6'), -1, -1
7568 Xalpha_7, TRUE, FALSE,
7569 EL_CHAR('7'), -1, -1
7572 Xalpha_8, TRUE, FALSE,
7573 EL_CHAR('8'), -1, -1
7576 Xalpha_9, TRUE, FALSE,
7577 EL_CHAR('9'), -1, -1
7580 Xalpha_excla, TRUE, FALSE,
7581 EL_CHAR('!'), -1, -1
7584 Xalpha_apost, TRUE, FALSE,
7585 EL_CHAR('\''), -1, -1
7588 Xalpha_comma, TRUE, FALSE,
7589 EL_CHAR(','), -1, -1
7592 Xalpha_minus, TRUE, FALSE,
7593 EL_CHAR('-'), -1, -1
7596 Xalpha_perio, TRUE, FALSE,
7597 EL_CHAR('.'), -1, -1
7600 Xalpha_colon, TRUE, FALSE,
7601 EL_CHAR(':'), -1, -1
7604 Xalpha_quest, TRUE, FALSE,
7605 EL_CHAR('?'), -1, -1
7608 Xalpha_a, TRUE, FALSE,
7609 EL_CHAR('A'), -1, -1
7612 Xalpha_b, TRUE, FALSE,
7613 EL_CHAR('B'), -1, -1
7616 Xalpha_c, TRUE, FALSE,
7617 EL_CHAR('C'), -1, -1
7620 Xalpha_d, TRUE, FALSE,
7621 EL_CHAR('D'), -1, -1
7624 Xalpha_e, TRUE, FALSE,
7625 EL_CHAR('E'), -1, -1
7628 Xalpha_f, TRUE, FALSE,
7629 EL_CHAR('F'), -1, -1
7632 Xalpha_g, TRUE, FALSE,
7633 EL_CHAR('G'), -1, -1
7636 Xalpha_h, TRUE, FALSE,
7637 EL_CHAR('H'), -1, -1
7640 Xalpha_i, TRUE, FALSE,
7641 EL_CHAR('I'), -1, -1
7644 Xalpha_j, TRUE, FALSE,
7645 EL_CHAR('J'), -1, -1
7648 Xalpha_k, TRUE, FALSE,
7649 EL_CHAR('K'), -1, -1
7652 Xalpha_l, TRUE, FALSE,
7653 EL_CHAR('L'), -1, -1
7656 Xalpha_m, TRUE, FALSE,
7657 EL_CHAR('M'), -1, -1
7660 Xalpha_n, TRUE, FALSE,
7661 EL_CHAR('N'), -1, -1
7664 Xalpha_o, TRUE, FALSE,
7665 EL_CHAR('O'), -1, -1
7668 Xalpha_p, TRUE, FALSE,
7669 EL_CHAR('P'), -1, -1
7672 Xalpha_q, TRUE, FALSE,
7673 EL_CHAR('Q'), -1, -1
7676 Xalpha_r, TRUE, FALSE,
7677 EL_CHAR('R'), -1, -1
7680 Xalpha_s, TRUE, FALSE,
7681 EL_CHAR('S'), -1, -1
7684 Xalpha_t, TRUE, FALSE,
7685 EL_CHAR('T'), -1, -1
7688 Xalpha_u, TRUE, FALSE,
7689 EL_CHAR('U'), -1, -1
7692 Xalpha_v, TRUE, FALSE,
7693 EL_CHAR('V'), -1, -1
7696 Xalpha_w, TRUE, FALSE,
7697 EL_CHAR('W'), -1, -1
7700 Xalpha_x, TRUE, FALSE,
7701 EL_CHAR('X'), -1, -1
7704 Xalpha_y, TRUE, FALSE,
7705 EL_CHAR('Y'), -1, -1
7708 Xalpha_z, TRUE, FALSE,
7709 EL_CHAR('Z'), -1, -1
7712 Xalpha_arrow_e, TRUE, FALSE,
7713 EL_CHAR('>'), -1, -1
7716 Xalpha_arrow_w, TRUE, FALSE,
7717 EL_CHAR('<'), -1, -1
7720 Xalpha_copyr, TRUE, FALSE,
7721 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7725 Ykey_1_blank, FALSE, FALSE,
7726 EL_EM_KEY_1, ACTION_COLLECTING, -1
7729 Ykey_2_blank, FALSE, FALSE,
7730 EL_EM_KEY_2, ACTION_COLLECTING, -1
7733 Ykey_3_blank, FALSE, FALSE,
7734 EL_EM_KEY_3, ACTION_COLLECTING, -1
7737 Ykey_4_blank, FALSE, FALSE,
7738 EL_EM_KEY_4, ACTION_COLLECTING, -1
7741 Ykey_5_blank, FALSE, FALSE,
7742 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7745 Ykey_6_blank, FALSE, FALSE,
7746 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7749 Ykey_7_blank, FALSE, FALSE,
7750 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7753 Ykey_8_blank, FALSE, FALSE,
7754 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7757 Ylenses_blank, FALSE, FALSE,
7758 EL_EMC_LENSES, ACTION_COLLECTING, -1
7761 Ymagnify_blank, FALSE, FALSE,
7762 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7765 Ygrass_blank, FALSE, FALSE,
7766 EL_EMC_GRASS, ACTION_SNAPPING, -1
7769 Ydirt_blank, FALSE, FALSE,
7770 EL_SAND, ACTION_SNAPPING, -1
7779 static struct Mapping_EM_to_RND_player
7788 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7792 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7796 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7800 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7804 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7808 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7812 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7816 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7820 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7824 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7828 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7832 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7836 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7840 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7844 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7848 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7852 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7856 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7860 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7864 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7868 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7872 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7876 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7880 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7884 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7888 EL_PLAYER_1, ACTION_DEFAULT, -1,
7892 EL_PLAYER_2, ACTION_DEFAULT, -1,
7896 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7900 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7904 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7908 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7912 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7916 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7920 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7924 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7928 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7932 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7936 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7940 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7944 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7948 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7952 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7956 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7960 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7964 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7968 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7972 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7976 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7980 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7984 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7988 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7992 EL_PLAYER_3, ACTION_DEFAULT, -1,
7996 EL_PLAYER_4, ACTION_DEFAULT, -1,
8005 int map_element_RND_to_EM_cave(int element_rnd)
8007 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
8008 static boolean mapping_initialized = FALSE;
8010 if (!mapping_initialized)
8014 // return "Xalpha_quest" for all undefined elements in mapping array
8015 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8016 mapping_RND_to_EM[i] = Xalpha_quest;
8018 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8019 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
8020 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
8021 em_object_mapping_list[i].element_em;
8023 mapping_initialized = TRUE;
8026 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
8028 Warn("invalid RND level element %d", element_rnd);
8033 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
8036 int map_element_EM_to_RND_cave(int element_em_cave)
8038 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8039 static boolean mapping_initialized = FALSE;
8041 if (!mapping_initialized)
8045 // return "EL_UNKNOWN" for all undefined elements in mapping array
8046 for (i = 0; i < GAME_TILE_MAX; i++)
8047 mapping_EM_to_RND[i] = EL_UNKNOWN;
8049 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8050 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8051 em_object_mapping_list[i].element_rnd;
8053 mapping_initialized = TRUE;
8056 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8058 Warn("invalid EM cave element %d", element_em_cave);
8063 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8066 int map_element_EM_to_RND_game(int element_em_game)
8068 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8069 static boolean mapping_initialized = FALSE;
8071 if (!mapping_initialized)
8075 // return "EL_UNKNOWN" for all undefined elements in mapping array
8076 for (i = 0; i < GAME_TILE_MAX; i++)
8077 mapping_EM_to_RND[i] = EL_UNKNOWN;
8079 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8080 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8081 em_object_mapping_list[i].element_rnd;
8083 mapping_initialized = TRUE;
8086 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8088 Warn("invalid EM game element %d", element_em_game);
8093 return mapping_EM_to_RND[element_em_game];
8096 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8098 struct LevelInfo_EM *level_em = level->native_em_level;
8099 struct CAVE *cav = level_em->cav;
8102 for (i = 0; i < GAME_TILE_MAX; i++)
8103 cav->android_array[i] = Cblank;
8105 for (i = 0; i < level->num_android_clone_elements; i++)
8107 int element_rnd = level->android_clone_element[i];
8108 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8110 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8111 if (em_object_mapping_list[j].element_rnd == element_rnd)
8112 cav->android_array[em_object_mapping_list[j].element_em] =
8117 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8119 struct LevelInfo_EM *level_em = level->native_em_level;
8120 struct CAVE *cav = level_em->cav;
8123 level->num_android_clone_elements = 0;
8125 for (i = 0; i < GAME_TILE_MAX; i++)
8127 int element_em_cave = cav->android_array[i];
8129 boolean element_found = FALSE;
8131 if (element_em_cave == Cblank)
8134 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8136 for (j = 0; j < level->num_android_clone_elements; j++)
8137 if (level->android_clone_element[j] == element_rnd)
8138 element_found = TRUE;
8142 level->android_clone_element[level->num_android_clone_elements++] =
8145 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8150 if (level->num_android_clone_elements == 0)
8152 level->num_android_clone_elements = 1;
8153 level->android_clone_element[0] = EL_EMPTY;
8157 int map_direction_RND_to_EM(int direction)
8159 return (direction == MV_UP ? 0 :
8160 direction == MV_RIGHT ? 1 :
8161 direction == MV_DOWN ? 2 :
8162 direction == MV_LEFT ? 3 :
8166 int map_direction_EM_to_RND(int direction)
8168 return (direction == 0 ? MV_UP :
8169 direction == 1 ? MV_RIGHT :
8170 direction == 2 ? MV_DOWN :
8171 direction == 3 ? MV_LEFT :
8175 int map_element_RND_to_SP(int element_rnd)
8177 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8179 if (element_rnd >= EL_SP_START &&
8180 element_rnd <= EL_SP_END)
8181 element_sp = element_rnd - EL_SP_START;
8182 else if (element_rnd == EL_EMPTY_SPACE)
8184 else if (element_rnd == EL_INVISIBLE_WALL)
8190 int map_element_SP_to_RND(int element_sp)
8192 int element_rnd = EL_UNKNOWN;
8194 if (element_sp >= 0x00 &&
8196 element_rnd = EL_SP_START + element_sp;
8197 else if (element_sp == 0x28)
8198 element_rnd = EL_INVISIBLE_WALL;
8203 int map_action_SP_to_RND(int action_sp)
8207 case actActive: return ACTION_ACTIVE;
8208 case actImpact: return ACTION_IMPACT;
8209 case actExploding: return ACTION_EXPLODING;
8210 case actDigging: return ACTION_DIGGING;
8211 case actSnapping: return ACTION_SNAPPING;
8212 case actCollecting: return ACTION_COLLECTING;
8213 case actPassing: return ACTION_PASSING;
8214 case actPushing: return ACTION_PUSHING;
8215 case actDropping: return ACTION_DROPPING;
8217 default: return ACTION_DEFAULT;
8221 int map_element_RND_to_MM(int element_rnd)
8223 return (element_rnd >= EL_MM_START_1 &&
8224 element_rnd <= EL_MM_END_1 ?
8225 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8227 element_rnd >= EL_MM_START_2 &&
8228 element_rnd <= EL_MM_END_2 ?
8229 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8231 element_rnd >= EL_CHAR_START &&
8232 element_rnd <= EL_CHAR_END ?
8233 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8235 element_rnd >= EL_MM_RUNTIME_START &&
8236 element_rnd <= EL_MM_RUNTIME_END ?
8237 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8239 EL_MM_EMPTY_NATIVE);
8242 int map_element_MM_to_RND(int element_mm)
8244 return (element_mm == EL_MM_EMPTY_NATIVE ||
8245 element_mm == EL_DF_EMPTY_NATIVE ?
8248 element_mm >= EL_MM_START_1_NATIVE &&
8249 element_mm <= EL_MM_END_1_NATIVE ?
8250 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8252 element_mm >= EL_MM_START_2_NATIVE &&
8253 element_mm <= EL_MM_END_2_NATIVE ?
8254 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8256 element_mm >= EL_MM_CHAR_START_NATIVE &&
8257 element_mm <= EL_MM_CHAR_END_NATIVE ?
8258 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8260 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8261 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8262 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8267 int map_action_MM_to_RND(int action_mm)
8269 // all MM actions are defined to exactly match their RND counterparts
8273 int map_sound_MM_to_RND(int sound_mm)
8277 case SND_MM_GAME_LEVELTIME_CHARGING:
8278 return SND_GAME_LEVELTIME_CHARGING;
8280 case SND_MM_GAME_HEALTH_CHARGING:
8281 return SND_GAME_HEALTH_CHARGING;
8284 return SND_UNDEFINED;
8288 int map_mm_wall_element(int element)
8290 return (element >= EL_MM_STEEL_WALL_START &&
8291 element <= EL_MM_STEEL_WALL_END ?
8294 element >= EL_MM_WOODEN_WALL_START &&
8295 element <= EL_MM_WOODEN_WALL_END ?
8298 element >= EL_MM_ICE_WALL_START &&
8299 element <= EL_MM_ICE_WALL_END ?
8302 element >= EL_MM_AMOEBA_WALL_START &&
8303 element <= EL_MM_AMOEBA_WALL_END ?
8306 element >= EL_DF_STEEL_WALL_START &&
8307 element <= EL_DF_STEEL_WALL_END ?
8310 element >= EL_DF_WOODEN_WALL_START &&
8311 element <= EL_DF_WOODEN_WALL_END ?
8317 int map_mm_wall_element_editor(int element)
8321 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8322 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8323 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8324 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8325 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8326 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8328 default: return element;
8332 int get_next_element(int element)
8336 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8337 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8338 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8339 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8340 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8341 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8342 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8343 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8344 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8345 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8346 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8348 default: return element;
8352 int el2img_mm(int element_mm)
8354 return el2img(map_element_MM_to_RND(element_mm));
8357 int el_act2img_mm(int element_mm, int action)
8359 return el_act2img(map_element_MM_to_RND(element_mm), action);
8362 int el_act_dir2img(int element, int action, int direction)
8364 element = GFX_ELEMENT(element);
8365 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8367 // direction_graphic[][] == graphic[] for undefined direction graphics
8368 return element_info[element].direction_graphic[action][direction];
8371 static int el_act_dir2crm(int element, int action, int direction)
8373 element = GFX_ELEMENT(element);
8374 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8376 // direction_graphic[][] == graphic[] for undefined direction graphics
8377 return element_info[element].direction_crumbled[action][direction];
8380 int el_act2img(int element, int action)
8382 element = GFX_ELEMENT(element);
8384 return element_info[element].graphic[action];
8387 int el_act2crm(int element, int action)
8389 element = GFX_ELEMENT(element);
8391 return element_info[element].crumbled[action];
8394 int el_dir2img(int element, int direction)
8396 element = GFX_ELEMENT(element);
8398 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8401 int el2baseimg(int element)
8403 return element_info[element].graphic[ACTION_DEFAULT];
8406 int el2img(int element)
8408 element = GFX_ELEMENT(element);
8410 return element_info[element].graphic[ACTION_DEFAULT];
8413 int el2edimg(int element)
8415 element = GFX_ELEMENT(element);
8417 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8420 int el2preimg(int element)
8422 element = GFX_ELEMENT(element);
8424 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8427 int el2panelimg(int element)
8429 element = GFX_ELEMENT(element);
8431 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8434 int font2baseimg(int font_nr)
8436 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8439 int getBeltNrFromBeltElement(int element)
8441 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8442 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8443 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8446 int getBeltNrFromBeltActiveElement(int element)
8448 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8449 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8450 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8453 int getBeltNrFromBeltSwitchElement(int element)
8455 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8456 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8457 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8460 int getBeltDirNrFromBeltElement(int element)
8462 static int belt_base_element[4] =
8464 EL_CONVEYOR_BELT_1_LEFT,
8465 EL_CONVEYOR_BELT_2_LEFT,
8466 EL_CONVEYOR_BELT_3_LEFT,
8467 EL_CONVEYOR_BELT_4_LEFT
8470 int belt_nr = getBeltNrFromBeltElement(element);
8471 int belt_dir_nr = element - belt_base_element[belt_nr];
8473 return (belt_dir_nr % 3);
8476 int getBeltDirNrFromBeltSwitchElement(int element)
8478 static int belt_base_element[4] =
8480 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8481 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8482 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8483 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8486 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8487 int belt_dir_nr = element - belt_base_element[belt_nr];
8489 return (belt_dir_nr % 3);
8492 int getBeltDirFromBeltElement(int element)
8494 static int belt_move_dir[3] =
8501 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8503 return belt_move_dir[belt_dir_nr];
8506 int getBeltDirFromBeltSwitchElement(int element)
8508 static int belt_move_dir[3] =
8515 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8517 return belt_move_dir[belt_dir_nr];
8520 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8522 static int belt_base_element[4] =
8524 EL_CONVEYOR_BELT_1_LEFT,
8525 EL_CONVEYOR_BELT_2_LEFT,
8526 EL_CONVEYOR_BELT_3_LEFT,
8527 EL_CONVEYOR_BELT_4_LEFT
8530 return belt_base_element[belt_nr] + belt_dir_nr;
8533 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8535 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8537 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8540 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8542 static int belt_base_element[4] =
8544 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8545 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8546 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8547 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8550 return belt_base_element[belt_nr] + belt_dir_nr;
8553 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8555 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8557 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8560 boolean swapTiles_EM(boolean is_pre_emc_cave)
8562 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8565 boolean getTeamMode_EM(void)
8567 return game.team_mode || network_playing;
8570 boolean isActivePlayer_EM(int player_nr)
8572 return stored_player[player_nr].active;
8575 unsigned int InitRND(int seed)
8577 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8578 return InitEngineRandom_EM(seed);
8579 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8580 return InitEngineRandom_SP(seed);
8581 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8582 return InitEngineRandom_MM(seed);
8584 return InitEngineRandom_RND(seed);
8587 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8588 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8590 static int get_effective_element_EM(int tile, int frame_em)
8592 int element = object_mapping[tile].element_rnd;
8593 int action = object_mapping[tile].action;
8594 boolean is_backside = object_mapping[tile].is_backside;
8595 boolean action_removing = (action == ACTION_DIGGING ||
8596 action == ACTION_SNAPPING ||
8597 action == ACTION_COLLECTING);
8605 return (frame_em > 5 ? EL_EMPTY : element);
8611 else // frame_em == 7
8622 case Ydiamond_stone:
8626 case Xdrip_stretchB:
8642 case Ymagnify_blank:
8645 case Xsand_stonein_1:
8646 case Xsand_stonein_2:
8647 case Xsand_stonein_3:
8648 case Xsand_stonein_4:
8652 return (is_backside || action_removing ? EL_EMPTY : element);
8657 static boolean check_linear_animation_EM(int tile)
8661 case Xsand_stonesand_1:
8662 case Xsand_stonesand_quickout_1:
8663 case Xsand_sandstone_1:
8664 case Xsand_stonein_1:
8665 case Xsand_stoneout_1:
8693 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8694 boolean has_crumbled_graphics,
8695 int crumbled, int sync_frame)
8697 // if element can be crumbled, but certain action graphics are just empty
8698 // space (like instantly snapping sand to empty space in 1 frame), do not
8699 // treat these empty space graphics as crumbled graphics in EMC engine
8700 if (crumbled == IMG_EMPTY_SPACE)
8701 has_crumbled_graphics = FALSE;
8703 if (has_crumbled_graphics)
8705 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8706 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8707 g_crumbled->anim_delay,
8708 g_crumbled->anim_mode,
8709 g_crumbled->anim_start_frame,
8712 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8713 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8715 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8716 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8718 g_em->has_crumbled_graphics = TRUE;
8722 g_em->crumbled_bitmap = NULL;
8723 g_em->crumbled_src_x = 0;
8724 g_em->crumbled_src_y = 0;
8725 g_em->crumbled_border_size = 0;
8726 g_em->crumbled_tile_size = 0;
8728 g_em->has_crumbled_graphics = FALSE;
8733 void ResetGfxAnimation_EM(int x, int y, int tile)
8739 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8740 int tile, int frame_em, int x, int y)
8742 int action = object_mapping[tile].action;
8743 int direction = object_mapping[tile].direction;
8744 int effective_element = get_effective_element_EM(tile, frame_em);
8745 int graphic = (direction == MV_NONE ?
8746 el_act2img(effective_element, action) :
8747 el_act_dir2img(effective_element, action, direction));
8748 struct GraphicInfo *g = &graphic_info[graphic];
8750 boolean action_removing = (action == ACTION_DIGGING ||
8751 action == ACTION_SNAPPING ||
8752 action == ACTION_COLLECTING);
8753 boolean action_moving = (action == ACTION_FALLING ||
8754 action == ACTION_MOVING ||
8755 action == ACTION_PUSHING ||
8756 action == ACTION_EATING ||
8757 action == ACTION_FILLING ||
8758 action == ACTION_EMPTYING);
8759 boolean action_falling = (action == ACTION_FALLING ||
8760 action == ACTION_FILLING ||
8761 action == ACTION_EMPTYING);
8763 // special case: graphic uses "2nd movement tile" and has defined
8764 // 7 frames for movement animation (or less) => use default graphic
8765 // for last (8th) frame which ends the movement animation
8766 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8768 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8769 graphic = (direction == MV_NONE ?
8770 el_act2img(effective_element, action) :
8771 el_act_dir2img(effective_element, action, direction));
8773 g = &graphic_info[graphic];
8776 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8780 else if (action_moving)
8782 boolean is_backside = object_mapping[tile].is_backside;
8786 int direction = object_mapping[tile].direction;
8787 int move_dir = (action_falling ? MV_DOWN : direction);
8792 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8793 if (g->double_movement && frame_em == 0)
8797 if (move_dir == MV_LEFT)
8798 GfxFrame[x - 1][y] = GfxFrame[x][y];
8799 else if (move_dir == MV_RIGHT)
8800 GfxFrame[x + 1][y] = GfxFrame[x][y];
8801 else if (move_dir == MV_UP)
8802 GfxFrame[x][y - 1] = GfxFrame[x][y];
8803 else if (move_dir == MV_DOWN)
8804 GfxFrame[x][y + 1] = GfxFrame[x][y];
8811 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8812 if (tile == Xsand_stonesand_quickout_1 ||
8813 tile == Xsand_stonesand_quickout_2)
8817 if (graphic_info[graphic].anim_global_sync)
8818 sync_frame = FrameCounter;
8819 else if (graphic_info[graphic].anim_global_anim_sync)
8820 sync_frame = getGlobalAnimSyncFrame();
8821 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8822 sync_frame = GfxFrame[x][y];
8824 sync_frame = 0; // playfield border (pseudo steel)
8826 SetRandomAnimationValue(x, y);
8828 int frame = getAnimationFrame(g->anim_frames,
8831 g->anim_start_frame,
8834 g_em->unique_identifier =
8835 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8838 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8839 int tile, int frame_em, int x, int y)
8841 int action = object_mapping[tile].action;
8842 int direction = object_mapping[tile].direction;
8843 boolean is_backside = object_mapping[tile].is_backside;
8844 int effective_element = get_effective_element_EM(tile, frame_em);
8845 int effective_action = action;
8846 int graphic = (direction == MV_NONE ?
8847 el_act2img(effective_element, effective_action) :
8848 el_act_dir2img(effective_element, effective_action,
8850 int crumbled = (direction == MV_NONE ?
8851 el_act2crm(effective_element, effective_action) :
8852 el_act_dir2crm(effective_element, effective_action,
8854 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8855 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8856 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8857 struct GraphicInfo *g = &graphic_info[graphic];
8860 // special case: graphic uses "2nd movement tile" and has defined
8861 // 7 frames for movement animation (or less) => use default graphic
8862 // for last (8th) frame which ends the movement animation
8863 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8865 effective_action = ACTION_DEFAULT;
8866 graphic = (direction == MV_NONE ?
8867 el_act2img(effective_element, effective_action) :
8868 el_act_dir2img(effective_element, effective_action,
8870 crumbled = (direction == MV_NONE ?
8871 el_act2crm(effective_element, effective_action) :
8872 el_act_dir2crm(effective_element, effective_action,
8875 g = &graphic_info[graphic];
8878 if (graphic_info[graphic].anim_global_sync)
8879 sync_frame = FrameCounter;
8880 else if (graphic_info[graphic].anim_global_anim_sync)
8881 sync_frame = getGlobalAnimSyncFrame();
8882 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8883 sync_frame = GfxFrame[x][y];
8885 sync_frame = 0; // playfield border (pseudo steel)
8887 SetRandomAnimationValue(x, y);
8889 int frame = getAnimationFrame(g->anim_frames,
8892 g->anim_start_frame,
8895 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8896 g->double_movement && is_backside);
8898 // (updating the "crumbled" graphic definitions is probably not really needed,
8899 // as animations for crumbled graphics can't be longer than one EMC cycle)
8900 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8904 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8905 int player_nr, int anim, int frame_em)
8907 int element = player_mapping[player_nr][anim].element_rnd;
8908 int action = player_mapping[player_nr][anim].action;
8909 int direction = player_mapping[player_nr][anim].direction;
8910 int graphic = (direction == MV_NONE ?
8911 el_act2img(element, action) :
8912 el_act_dir2img(element, action, direction));
8913 struct GraphicInfo *g = &graphic_info[graphic];
8916 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8918 stored_player[player_nr].StepFrame = frame_em;
8920 sync_frame = stored_player[player_nr].Frame;
8922 int frame = getAnimationFrame(g->anim_frames,
8925 g->anim_start_frame,
8928 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8929 &g_em->src_x, &g_em->src_y, FALSE);
8932 void InitGraphicInfo_EM(void)
8936 // always start with reliable default values
8937 for (i = 0; i < GAME_TILE_MAX; i++)
8939 object_mapping[i].element_rnd = EL_UNKNOWN;
8940 object_mapping[i].is_backside = FALSE;
8941 object_mapping[i].action = ACTION_DEFAULT;
8942 object_mapping[i].direction = MV_NONE;
8945 // always start with reliable default values
8946 for (p = 0; p < MAX_PLAYERS; p++)
8948 for (i = 0; i < PLY_MAX; i++)
8950 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8951 player_mapping[p][i].action = ACTION_DEFAULT;
8952 player_mapping[p][i].direction = MV_NONE;
8956 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8958 int e = em_object_mapping_list[i].element_em;
8960 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8961 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8963 if (em_object_mapping_list[i].action != -1)
8964 object_mapping[e].action = em_object_mapping_list[i].action;
8966 if (em_object_mapping_list[i].direction != -1)
8967 object_mapping[e].direction =
8968 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8971 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8973 int a = em_player_mapping_list[i].action_em;
8974 int p = em_player_mapping_list[i].player_nr;
8976 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8978 if (em_player_mapping_list[i].action != -1)
8979 player_mapping[p][a].action = em_player_mapping_list[i].action;
8981 if (em_player_mapping_list[i].direction != -1)
8982 player_mapping[p][a].direction =
8983 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8986 for (i = 0; i < GAME_TILE_MAX; i++)
8988 int element = object_mapping[i].element_rnd;
8989 int action = object_mapping[i].action;
8990 int direction = object_mapping[i].direction;
8991 boolean is_backside = object_mapping[i].is_backside;
8992 boolean action_exploding = ((action == ACTION_EXPLODING ||
8993 action == ACTION_SMASHED_BY_ROCK ||
8994 action == ACTION_SMASHED_BY_SPRING) &&
8995 element != EL_DIAMOND);
8996 boolean action_active = (action == ACTION_ACTIVE);
8997 boolean action_other = (action == ACTION_OTHER);
8999 for (j = 0; j < 8; j++)
9001 int effective_element = get_effective_element_EM(i, j);
9002 int effective_action = (j < 7 ? action :
9003 i == Xdrip_stretch ? action :
9004 i == Xdrip_stretchB ? action :
9005 i == Ydrip_1_s ? action :
9006 i == Ydrip_1_sB ? action :
9007 i == Yball_1 ? action :
9008 i == Xball_2 ? action :
9009 i == Yball_2 ? action :
9010 i == Yball_blank ? action :
9011 i == Ykey_1_blank ? action :
9012 i == Ykey_2_blank ? action :
9013 i == Ykey_3_blank ? action :
9014 i == Ykey_4_blank ? action :
9015 i == Ykey_5_blank ? action :
9016 i == Ykey_6_blank ? action :
9017 i == Ykey_7_blank ? action :
9018 i == Ykey_8_blank ? action :
9019 i == Ylenses_blank ? action :
9020 i == Ymagnify_blank ? action :
9021 i == Ygrass_blank ? action :
9022 i == Ydirt_blank ? action :
9023 i == Xsand_stonein_1 ? action :
9024 i == Xsand_stonein_2 ? action :
9025 i == Xsand_stonein_3 ? action :
9026 i == Xsand_stonein_4 ? action :
9027 i == Xsand_stoneout_1 ? action :
9028 i == Xsand_stoneout_2 ? action :
9029 i == Xboom_android ? ACTION_EXPLODING :
9030 action_exploding ? ACTION_EXPLODING :
9031 action_active ? action :
9032 action_other ? action :
9034 int graphic = (el_act_dir2img(effective_element, effective_action,
9036 int crumbled = (el_act_dir2crm(effective_element, effective_action,
9038 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
9039 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
9040 boolean has_action_graphics = (graphic != base_graphic);
9041 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9042 struct GraphicInfo *g = &graphic_info[graphic];
9043 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9046 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9047 boolean special_animation = (action != ACTION_DEFAULT &&
9048 g->anim_frames == 3 &&
9049 g->anim_delay == 2 &&
9050 g->anim_mode & ANIM_LINEAR);
9051 int sync_frame = (i == Xdrip_stretch ? 7 :
9052 i == Xdrip_stretchB ? 7 :
9053 i == Ydrip_2_s ? j + 8 :
9054 i == Ydrip_2_sB ? j + 8 :
9063 i == Xfake_acid_1 ? 0 :
9064 i == Xfake_acid_2 ? 10 :
9065 i == Xfake_acid_3 ? 20 :
9066 i == Xfake_acid_4 ? 30 :
9067 i == Xfake_acid_5 ? 40 :
9068 i == Xfake_acid_6 ? 50 :
9069 i == Xfake_acid_7 ? 60 :
9070 i == Xfake_acid_8 ? 70 :
9071 i == Xfake_acid_1_player ? 0 :
9072 i == Xfake_acid_2_player ? 10 :
9073 i == Xfake_acid_3_player ? 20 :
9074 i == Xfake_acid_4_player ? 30 :
9075 i == Xfake_acid_5_player ? 40 :
9076 i == Xfake_acid_6_player ? 50 :
9077 i == Xfake_acid_7_player ? 60 :
9078 i == Xfake_acid_8_player ? 70 :
9080 i == Yball_2 ? j + 8 :
9081 i == Yball_blank ? j + 1 :
9082 i == Ykey_1_blank ? j + 1 :
9083 i == Ykey_2_blank ? j + 1 :
9084 i == Ykey_3_blank ? j + 1 :
9085 i == Ykey_4_blank ? j + 1 :
9086 i == Ykey_5_blank ? j + 1 :
9087 i == Ykey_6_blank ? j + 1 :
9088 i == Ykey_7_blank ? j + 1 :
9089 i == Ykey_8_blank ? j + 1 :
9090 i == Ylenses_blank ? j + 1 :
9091 i == Ymagnify_blank ? j + 1 :
9092 i == Ygrass_blank ? j + 1 :
9093 i == Ydirt_blank ? j + 1 :
9094 i == Xamoeba_1 ? 0 :
9095 i == Xamoeba_2 ? 1 :
9096 i == Xamoeba_3 ? 2 :
9097 i == Xamoeba_4 ? 3 :
9098 i == Xamoeba_5 ? 0 :
9099 i == Xamoeba_6 ? 1 :
9100 i == Xamoeba_7 ? 2 :
9101 i == Xamoeba_8 ? 3 :
9102 i == Xexit_2 ? j + 8 :
9103 i == Xexit_3 ? j + 16 :
9104 i == Xdynamite_1 ? 0 :
9105 i == Xdynamite_2 ? 8 :
9106 i == Xdynamite_3 ? 16 :
9107 i == Xdynamite_4 ? 24 :
9108 i == Xsand_stonein_1 ? j + 1 :
9109 i == Xsand_stonein_2 ? j + 9 :
9110 i == Xsand_stonein_3 ? j + 17 :
9111 i == Xsand_stonein_4 ? j + 25 :
9112 i == Xsand_stoneout_1 && j == 0 ? 0 :
9113 i == Xsand_stoneout_1 && j == 1 ? 0 :
9114 i == Xsand_stoneout_1 && j == 2 ? 1 :
9115 i == Xsand_stoneout_1 && j == 3 ? 2 :
9116 i == Xsand_stoneout_1 && j == 4 ? 2 :
9117 i == Xsand_stoneout_1 && j == 5 ? 3 :
9118 i == Xsand_stoneout_1 && j == 6 ? 4 :
9119 i == Xsand_stoneout_1 && j == 7 ? 4 :
9120 i == Xsand_stoneout_2 && j == 0 ? 5 :
9121 i == Xsand_stoneout_2 && j == 1 ? 6 :
9122 i == Xsand_stoneout_2 && j == 2 ? 7 :
9123 i == Xsand_stoneout_2 && j == 3 ? 8 :
9124 i == Xsand_stoneout_2 && j == 4 ? 9 :
9125 i == Xsand_stoneout_2 && j == 5 ? 11 :
9126 i == Xsand_stoneout_2 && j == 6 ? 13 :
9127 i == Xsand_stoneout_2 && j == 7 ? 15 :
9128 i == Xboom_bug && j == 1 ? 2 :
9129 i == Xboom_bug && j == 2 ? 2 :
9130 i == Xboom_bug && j == 3 ? 4 :
9131 i == Xboom_bug && j == 4 ? 4 :
9132 i == Xboom_bug && j == 5 ? 2 :
9133 i == Xboom_bug && j == 6 ? 2 :
9134 i == Xboom_bug && j == 7 ? 0 :
9135 i == Xboom_tank && j == 1 ? 2 :
9136 i == Xboom_tank && j == 2 ? 2 :
9137 i == Xboom_tank && j == 3 ? 4 :
9138 i == Xboom_tank && j == 4 ? 4 :
9139 i == Xboom_tank && j == 5 ? 2 :
9140 i == Xboom_tank && j == 6 ? 2 :
9141 i == Xboom_tank && j == 7 ? 0 :
9142 i == Xboom_android && j == 7 ? 6 :
9143 i == Xboom_1 && j == 1 ? 2 :
9144 i == Xboom_1 && j == 2 ? 2 :
9145 i == Xboom_1 && j == 3 ? 4 :
9146 i == Xboom_1 && j == 4 ? 4 :
9147 i == Xboom_1 && j == 5 ? 6 :
9148 i == Xboom_1 && j == 6 ? 6 :
9149 i == Xboom_1 && j == 7 ? 8 :
9150 i == Xboom_2 && j == 0 ? 8 :
9151 i == Xboom_2 && j == 1 ? 8 :
9152 i == Xboom_2 && j == 2 ? 10 :
9153 i == Xboom_2 && j == 3 ? 10 :
9154 i == Xboom_2 && j == 4 ? 10 :
9155 i == Xboom_2 && j == 5 ? 12 :
9156 i == Xboom_2 && j == 6 ? 12 :
9157 i == Xboom_2 && j == 7 ? 12 :
9158 special_animation && j == 4 ? 3 :
9159 effective_action != action ? 0 :
9161 int frame = getAnimationFrame(g->anim_frames,
9164 g->anim_start_frame,
9167 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9168 g->double_movement && is_backside);
9170 g_em->bitmap = src_bitmap;
9171 g_em->src_x = src_x;
9172 g_em->src_y = src_y;
9173 g_em->src_offset_x = 0;
9174 g_em->src_offset_y = 0;
9175 g_em->dst_offset_x = 0;
9176 g_em->dst_offset_y = 0;
9177 g_em->width = TILEX;
9178 g_em->height = TILEY;
9180 g_em->preserve_background = FALSE;
9182 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9185 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9186 effective_action == ACTION_MOVING ||
9187 effective_action == ACTION_PUSHING ||
9188 effective_action == ACTION_EATING)) ||
9189 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9190 effective_action == ACTION_EMPTYING)))
9193 (effective_action == ACTION_FALLING ||
9194 effective_action == ACTION_FILLING ||
9195 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9196 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9197 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9198 int num_steps = (i == Ydrip_1_s ? 16 :
9199 i == Ydrip_1_sB ? 16 :
9200 i == Ydrip_2_s ? 16 :
9201 i == Ydrip_2_sB ? 16 :
9202 i == Xsand_stonein_1 ? 32 :
9203 i == Xsand_stonein_2 ? 32 :
9204 i == Xsand_stonein_3 ? 32 :
9205 i == Xsand_stonein_4 ? 32 :
9206 i == Xsand_stoneout_1 ? 16 :
9207 i == Xsand_stoneout_2 ? 16 : 8);
9208 int cx = ABS(dx) * (TILEX / num_steps);
9209 int cy = ABS(dy) * (TILEY / num_steps);
9210 int step_frame = (i == Ydrip_2_s ? j + 8 :
9211 i == Ydrip_2_sB ? j + 8 :
9212 i == Xsand_stonein_2 ? j + 8 :
9213 i == Xsand_stonein_3 ? j + 16 :
9214 i == Xsand_stonein_4 ? j + 24 :
9215 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9216 int step = (is_backside ? step_frame : num_steps - step_frame);
9218 if (is_backside) // tile where movement starts
9220 if (dx < 0 || dy < 0)
9222 g_em->src_offset_x = cx * step;
9223 g_em->src_offset_y = cy * step;
9227 g_em->dst_offset_x = cx * step;
9228 g_em->dst_offset_y = cy * step;
9231 else // tile where movement ends
9233 if (dx < 0 || dy < 0)
9235 g_em->dst_offset_x = cx * step;
9236 g_em->dst_offset_y = cy * step;
9240 g_em->src_offset_x = cx * step;
9241 g_em->src_offset_y = cy * step;
9245 g_em->width = TILEX - cx * step;
9246 g_em->height = TILEY - cy * step;
9249 // create unique graphic identifier to decide if tile must be redrawn
9250 /* bit 31 - 16 (16 bit): EM style graphic
9251 bit 15 - 12 ( 4 bit): EM style frame
9252 bit 11 - 6 ( 6 bit): graphic width
9253 bit 5 - 0 ( 6 bit): graphic height */
9254 g_em->unique_identifier =
9255 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9259 for (i = 0; i < GAME_TILE_MAX; i++)
9261 for (j = 0; j < 8; j++)
9263 int element = object_mapping[i].element_rnd;
9264 int action = object_mapping[i].action;
9265 int direction = object_mapping[i].direction;
9266 boolean is_backside = object_mapping[i].is_backside;
9267 int graphic_action = el_act_dir2img(element, action, direction);
9268 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9270 if ((action == ACTION_SMASHED_BY_ROCK ||
9271 action == ACTION_SMASHED_BY_SPRING ||
9272 action == ACTION_EATING) &&
9273 graphic_action == graphic_default)
9275 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9276 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9277 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9278 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9281 // no separate animation for "smashed by rock" -- use rock instead
9282 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9283 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9285 g_em->bitmap = g_xx->bitmap;
9286 g_em->src_x = g_xx->src_x;
9287 g_em->src_y = g_xx->src_y;
9288 g_em->src_offset_x = g_xx->src_offset_x;
9289 g_em->src_offset_y = g_xx->src_offset_y;
9290 g_em->dst_offset_x = g_xx->dst_offset_x;
9291 g_em->dst_offset_y = g_xx->dst_offset_y;
9292 g_em->width = g_xx->width;
9293 g_em->height = g_xx->height;
9294 g_em->unique_identifier = g_xx->unique_identifier;
9297 g_em->preserve_background = TRUE;
9302 for (p = 0; p < MAX_PLAYERS; p++)
9304 for (i = 0; i < PLY_MAX; i++)
9306 int element = player_mapping[p][i].element_rnd;
9307 int action = player_mapping[p][i].action;
9308 int direction = player_mapping[p][i].direction;
9310 for (j = 0; j < 8; j++)
9312 int effective_element = element;
9313 int effective_action = action;
9314 int graphic = (direction == MV_NONE ?
9315 el_act2img(effective_element, effective_action) :
9316 el_act_dir2img(effective_element, effective_action,
9318 struct GraphicInfo *g = &graphic_info[graphic];
9319 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9323 int frame = getAnimationFrame(g->anim_frames,
9326 g->anim_start_frame,
9329 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9331 g_em->bitmap = src_bitmap;
9332 g_em->src_x = src_x;
9333 g_em->src_y = src_y;
9334 g_em->src_offset_x = 0;
9335 g_em->src_offset_y = 0;
9336 g_em->dst_offset_x = 0;
9337 g_em->dst_offset_y = 0;
9338 g_em->width = TILEX;
9339 g_em->height = TILEY;
9345 static void CheckSaveEngineSnapshot_EM(int frame,
9346 boolean any_player_moving,
9347 boolean any_player_snapping,
9348 boolean any_player_dropping)
9350 if (frame == 7 && !any_player_dropping)
9352 if (!local_player->was_waiting)
9354 if (!CheckSaveEngineSnapshotToList())
9357 local_player->was_waiting = TRUE;
9360 else if (any_player_moving || any_player_snapping || any_player_dropping)
9362 local_player->was_waiting = FALSE;
9366 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9367 boolean murphy_is_dropping)
9369 if (murphy_is_waiting)
9371 if (!local_player->was_waiting)
9373 if (!CheckSaveEngineSnapshotToList())
9376 local_player->was_waiting = TRUE;
9381 local_player->was_waiting = FALSE;
9385 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9386 boolean button_released)
9388 if (button_released)
9390 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9391 CheckSaveEngineSnapshotToList();
9393 else if (element_clicked)
9395 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9396 CheckSaveEngineSnapshotToList();
9398 game.snapshot.changed_action = TRUE;
9402 boolean CheckSingleStepMode_EM(int frame,
9403 boolean any_player_moving,
9404 boolean any_player_snapping,
9405 boolean any_player_dropping)
9407 if (tape.single_step && tape.recording && !tape.pausing)
9408 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9409 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9411 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9412 any_player_snapping, any_player_dropping);
9414 return tape.pausing;
9417 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9418 boolean murphy_is_dropping)
9420 boolean murphy_starts_dropping = FALSE;
9423 for (i = 0; i < MAX_PLAYERS; i++)
9424 if (stored_player[i].force_dropping)
9425 murphy_starts_dropping = TRUE;
9427 if (tape.single_step && tape.recording && !tape.pausing)
9428 if (murphy_is_waiting && !murphy_starts_dropping)
9429 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9431 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9434 void CheckSingleStepMode_MM(boolean element_clicked,
9435 boolean button_released)
9437 if (tape.single_step && tape.recording && !tape.pausing)
9438 if (button_released)
9439 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9441 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9444 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9445 int graphic, int sync_frame)
9447 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9449 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9452 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9454 return (IS_NEXT_FRAME(sync_frame, graphic));
9457 int getGraphicInfo_Delay(int graphic)
9459 return graphic_info[graphic].anim_delay;
9462 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9464 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9467 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9473 void PlayMenuSoundExt(int sound)
9475 if (sound == SND_UNDEFINED)
9478 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9479 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9482 if (IS_LOOP_SOUND(sound))
9483 PlaySoundLoop(sound);
9488 void PlayMenuSound(void)
9490 PlayMenuSoundExt(menu.sound[game_status]);
9493 void PlayMenuSoundStereo(int sound, int stereo_position)
9495 if (sound == SND_UNDEFINED)
9498 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9499 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9502 if (IS_LOOP_SOUND(sound))
9503 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9505 PlaySoundStereo(sound, stereo_position);
9508 void PlayMenuSoundIfLoopExt(int sound)
9510 if (sound == SND_UNDEFINED)
9513 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9514 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9517 if (IS_LOOP_SOUND(sound))
9518 PlaySoundLoop(sound);
9521 void PlayMenuSoundIfLoop(void)
9523 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9526 void PlayMenuMusicExt(int music)
9528 if (music == MUS_UNDEFINED)
9531 if (!setup.sound_music)
9534 if (IS_LOOP_MUSIC(music))
9535 PlayMusicLoop(music);
9540 void PlayMenuMusic(void)
9542 char *curr_music = getCurrentlyPlayingMusicFilename();
9543 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9545 if (!strEqual(curr_music, next_music))
9546 PlayMenuMusicExt(menu.music[game_status]);
9549 void PlayMenuSoundsAndMusic(void)
9555 static void FadeMenuSounds(void)
9560 static void FadeMenuMusic(void)
9562 char *curr_music = getCurrentlyPlayingMusicFilename();
9563 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9565 if (!strEqual(curr_music, next_music))
9569 void FadeMenuSoundsAndMusic(void)
9575 void PlaySoundActivating(void)
9578 PlaySound(SND_MENU_ITEM_ACTIVATING);
9582 void PlaySoundSelecting(void)
9585 PlaySound(SND_MENU_ITEM_SELECTING);
9589 void ToggleFullscreenIfNeeded(void)
9591 // if setup and video fullscreen state are already matching, nothing do do
9592 if (setup.fullscreen == video.fullscreen_enabled ||
9593 !video.fullscreen_available)
9596 SDLSetWindowFullscreen(setup.fullscreen);
9598 // set setup value according to successfully changed fullscreen mode
9599 setup.fullscreen = video.fullscreen_enabled;
9602 void ChangeWindowScalingIfNeeded(void)
9604 // if setup and video window scaling are already matching, nothing do do
9605 if (setup.window_scaling_percent == video.window_scaling_percent ||
9606 video.fullscreen_enabled)
9609 SDLSetWindowScaling(setup.window_scaling_percent);
9611 // set setup value according to successfully changed window scaling
9612 setup.window_scaling_percent = video.window_scaling_percent;
9615 void ChangeVsyncModeIfNeeded(void)
9617 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9618 int video_vsync_mode = video.vsync_mode;
9620 // if setup and video vsync mode are already matching, nothing do do
9621 if (setup_vsync_mode == video_vsync_mode)
9624 // if renderer is using OpenGL, vsync mode can directly be changed
9625 SDLSetScreenVsyncMode(setup.vsync_mode);
9627 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9628 if (video.vsync_mode == video_vsync_mode)
9630 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9632 // save backbuffer content which gets lost when re-creating screen
9633 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9635 // force re-creating screen and renderer to set new vsync mode
9636 video.fullscreen_enabled = !setup.fullscreen;
9638 // when creating new renderer, destroy textures linked to old renderer
9639 FreeAllImageTextures(); // needs old renderer to free the textures
9641 // re-create screen and renderer (including change of vsync mode)
9642 ChangeVideoModeIfNeeded(setup.fullscreen);
9644 // set setup value according to successfully changed fullscreen mode
9645 setup.fullscreen = video.fullscreen_enabled;
9647 // restore backbuffer content from temporary backbuffer backup bitmap
9648 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9649 FreeBitmap(tmp_backbuffer);
9651 // update visible window/screen
9652 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9654 // when changing vsync mode, re-create textures for new renderer
9655 InitImageTextures();
9658 // set setup value according to successfully changed vsync mode
9659 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9662 static void JoinRectangles(int *x, int *y, int *width, int *height,
9663 int x2, int y2, int width2, int height2)
9665 // do not join with "off-screen" rectangle
9666 if (x2 == -1 || y2 == -1)
9671 *width = MAX(*width, width2);
9672 *height = MAX(*height, height2);
9675 void SetAnimStatus(int anim_status_new)
9677 if (anim_status_new == GAME_MODE_MAIN)
9678 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9679 else if (anim_status_new == GAME_MODE_NAMES)
9680 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9681 else if (anim_status_new == GAME_MODE_SCORES)
9682 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9684 global.anim_status_next = anim_status_new;
9686 // directly set screen modes that are entered without fading
9687 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9688 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9689 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9690 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9691 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9692 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9693 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9694 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9695 global.anim_status = global.anim_status_next;
9698 void SetGameStatus(int game_status_new)
9700 if (game_status_new != game_status)
9701 game_status_last_screen = game_status;
9703 game_status = game_status_new;
9705 SetAnimStatus(game_status_new);
9708 void SetFontStatus(int game_status_new)
9710 static int last_game_status = -1;
9712 if (game_status_new != -1)
9714 // set game status for font use after storing last game status
9715 last_game_status = game_status;
9716 game_status = game_status_new;
9720 // reset game status after font use from last stored game status
9721 game_status = last_game_status;
9725 void ResetFontStatus(void)
9730 void SetLevelSetInfo(char *identifier, int level_nr)
9732 setString(&levelset.identifier, identifier);
9734 levelset.level_nr = level_nr;
9737 boolean CheckIfAllViewportsHaveChanged(void)
9739 // if game status has not changed, viewports have not changed either
9740 if (game_status == game_status_last)
9743 // check if all viewports have changed with current game status
9745 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9746 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9747 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9748 int new_real_sx = vp_playfield->x;
9749 int new_real_sy = vp_playfield->y;
9750 int new_full_sxsize = vp_playfield->width;
9751 int new_full_sysize = vp_playfield->height;
9752 int new_dx = vp_door_1->x;
9753 int new_dy = vp_door_1->y;
9754 int new_dxsize = vp_door_1->width;
9755 int new_dysize = vp_door_1->height;
9756 int new_vx = vp_door_2->x;
9757 int new_vy = vp_door_2->y;
9758 int new_vxsize = vp_door_2->width;
9759 int new_vysize = vp_door_2->height;
9761 boolean playfield_viewport_has_changed =
9762 (new_real_sx != REAL_SX ||
9763 new_real_sy != REAL_SY ||
9764 new_full_sxsize != FULL_SXSIZE ||
9765 new_full_sysize != FULL_SYSIZE);
9767 boolean door_1_viewport_has_changed =
9770 new_dxsize != DXSIZE ||
9771 new_dysize != DYSIZE);
9773 boolean door_2_viewport_has_changed =
9776 new_vxsize != VXSIZE ||
9777 new_vysize != VYSIZE ||
9778 game_status_last == GAME_MODE_EDITOR);
9780 return (playfield_viewport_has_changed &&
9781 door_1_viewport_has_changed &&
9782 door_2_viewport_has_changed);
9785 boolean CheckFadeAll(void)
9787 return (CheckIfGlobalBorderHasChanged() ||
9788 CheckIfAllViewportsHaveChanged());
9791 void ChangeViewportPropertiesIfNeeded(void)
9793 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9794 FALSE : setup.small_game_graphics);
9795 int gfx_game_mode = getGlobalGameStatus(game_status);
9796 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9798 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9799 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9800 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9801 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9802 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9803 int new_win_xsize = vp_window->width;
9804 int new_win_ysize = vp_window->height;
9805 int border_left = vp_playfield->border_left;
9806 int border_right = vp_playfield->border_right;
9807 int border_top = vp_playfield->border_top;
9808 int border_bottom = vp_playfield->border_bottom;
9809 int new_sx = vp_playfield->x + border_left;
9810 int new_sy = vp_playfield->y + border_top;
9811 int new_sxsize = vp_playfield->width - border_left - border_right;
9812 int new_sysize = vp_playfield->height - border_top - border_bottom;
9813 int new_real_sx = vp_playfield->x;
9814 int new_real_sy = vp_playfield->y;
9815 int new_full_sxsize = vp_playfield->width;
9816 int new_full_sysize = vp_playfield->height;
9817 int new_dx = vp_door_1->x;
9818 int new_dy = vp_door_1->y;
9819 int new_dxsize = vp_door_1->width;
9820 int new_dysize = vp_door_1->height;
9821 int new_vx = vp_door_2->x;
9822 int new_vy = vp_door_2->y;
9823 int new_vxsize = vp_door_2->width;
9824 int new_vysize = vp_door_2->height;
9825 int new_ex = vp_door_3->x;
9826 int new_ey = vp_door_3->y;
9827 int new_exsize = vp_door_3->width;
9828 int new_eysize = vp_door_3->height;
9829 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9830 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9831 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9832 int new_scr_fieldx = new_sxsize / tilesize;
9833 int new_scr_fieldy = new_sysize / tilesize;
9834 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9835 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9836 boolean init_gfx_buffers = FALSE;
9837 boolean init_video_buffer = FALSE;
9838 boolean init_gadgets_and_anims = FALSE;
9839 boolean init_em_graphics = FALSE;
9841 if (new_win_xsize != WIN_XSIZE ||
9842 new_win_ysize != WIN_YSIZE)
9844 WIN_XSIZE = new_win_xsize;
9845 WIN_YSIZE = new_win_ysize;
9847 init_video_buffer = TRUE;
9848 init_gfx_buffers = TRUE;
9849 init_gadgets_and_anims = TRUE;
9851 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9854 if (new_scr_fieldx != SCR_FIELDX ||
9855 new_scr_fieldy != SCR_FIELDY)
9857 // this always toggles between MAIN and GAME when using small tile size
9859 SCR_FIELDX = new_scr_fieldx;
9860 SCR_FIELDY = new_scr_fieldy;
9862 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9873 new_sxsize != SXSIZE ||
9874 new_sysize != SYSIZE ||
9875 new_dxsize != DXSIZE ||
9876 new_dysize != DYSIZE ||
9877 new_vxsize != VXSIZE ||
9878 new_vysize != VYSIZE ||
9879 new_exsize != EXSIZE ||
9880 new_eysize != EYSIZE ||
9881 new_real_sx != REAL_SX ||
9882 new_real_sy != REAL_SY ||
9883 new_full_sxsize != FULL_SXSIZE ||
9884 new_full_sysize != FULL_SYSIZE ||
9885 new_tilesize_var != TILESIZE_VAR
9888 // ------------------------------------------------------------------------
9889 // determine next fading area for changed viewport definitions
9890 // ------------------------------------------------------------------------
9892 // start with current playfield area (default fading area)
9895 FADE_SXSIZE = FULL_SXSIZE;
9896 FADE_SYSIZE = FULL_SYSIZE;
9898 // add new playfield area if position or size has changed
9899 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9900 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9902 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9903 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9906 // add current and new door 1 area if position or size has changed
9907 if (new_dx != DX || new_dy != DY ||
9908 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9910 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9911 DX, DY, DXSIZE, DYSIZE);
9912 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9913 new_dx, new_dy, new_dxsize, new_dysize);
9916 // add current and new door 2 area if position or size has changed
9917 if (new_vx != VX || new_vy != VY ||
9918 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9920 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9921 VX, VY, VXSIZE, VYSIZE);
9922 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9923 new_vx, new_vy, new_vxsize, new_vysize);
9926 // ------------------------------------------------------------------------
9927 // handle changed tile size
9928 // ------------------------------------------------------------------------
9930 if (new_tilesize_var != TILESIZE_VAR)
9932 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9934 // changing tile size invalidates scroll values of engine snapshots
9935 FreeEngineSnapshotSingle();
9937 // changing tile size requires update of graphic mapping for EM engine
9938 init_em_graphics = TRUE;
9949 SXSIZE = new_sxsize;
9950 SYSIZE = new_sysize;
9951 DXSIZE = new_dxsize;
9952 DYSIZE = new_dysize;
9953 VXSIZE = new_vxsize;
9954 VYSIZE = new_vysize;
9955 EXSIZE = new_exsize;
9956 EYSIZE = new_eysize;
9957 REAL_SX = new_real_sx;
9958 REAL_SY = new_real_sy;
9959 FULL_SXSIZE = new_full_sxsize;
9960 FULL_SYSIZE = new_full_sysize;
9961 TILESIZE_VAR = new_tilesize_var;
9963 init_gfx_buffers = TRUE;
9964 init_gadgets_and_anims = TRUE;
9966 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9967 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9970 if (init_gfx_buffers)
9972 // Debug("tools:viewport", "init_gfx_buffers");
9974 SCR_FIELDX = new_scr_fieldx_buffers;
9975 SCR_FIELDY = new_scr_fieldy_buffers;
9979 SCR_FIELDX = new_scr_fieldx;
9980 SCR_FIELDY = new_scr_fieldy;
9982 SetDrawDeactivationMask(REDRAW_NONE);
9983 SetDrawBackgroundMask(REDRAW_FIELD);
9986 if (init_video_buffer)
9988 // Debug("tools:viewport", "init_video_buffer");
9990 FreeAllImageTextures(); // needs old renderer to free the textures
9992 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9993 InitImageTextures();
9996 if (init_gadgets_and_anims)
9998 // Debug("tools:viewport", "init_gadgets_and_anims");
10001 InitGlobalAnimations();
10004 if (init_em_graphics)
10006 InitGraphicInfo_EM();
10010 void OpenURL(char *url)
10012 #if SDL_VERSION_ATLEAST(2,0,14)
10015 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
10016 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
10017 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
10021 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
10023 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
10027 // ============================================================================
10029 // ============================================================================
10031 #if defined(PLATFORM_WINDOWS)
10032 /* FILETIME of Jan 1 1970 00:00:00. */
10033 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
10036 * timezone information is stored outside the kernel so tzp isn't used anymore.
10038 * Note: this function is not for Win32 high precision timing purpose. See
10041 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
10043 FILETIME file_time;
10044 SYSTEMTIME system_time;
10045 ULARGE_INTEGER ularge;
10047 GetSystemTime(&system_time);
10048 SystemTimeToFileTime(&system_time, &file_time);
10049 ularge.LowPart = file_time.dwLowDateTime;
10050 ularge.HighPart = file_time.dwHighDateTime;
10052 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10053 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10059 static char *test_init_uuid_random_function_simple(void)
10061 static char seed_text[100];
10062 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10064 sprintf(seed_text, "%d", seed);
10069 static char *test_init_uuid_random_function_better(void)
10071 static char seed_text[100];
10072 struct timeval current_time;
10074 gettimeofday(¤t_time, NULL);
10076 prng_seed_bytes(¤t_time, sizeof(current_time));
10078 sprintf(seed_text, "%ld.%ld",
10079 (long)current_time.tv_sec,
10080 (long)current_time.tv_usec);
10085 #if defined(PLATFORM_WINDOWS)
10086 static char *test_init_uuid_random_function_better_windows(void)
10088 static char seed_text[100];
10089 struct timeval current_time;
10091 gettimeofday_windows(¤t_time, NULL);
10093 prng_seed_bytes(¤t_time, sizeof(current_time));
10095 sprintf(seed_text, "%ld.%ld",
10096 (long)current_time.tv_sec,
10097 (long)current_time.tv_usec);
10103 static unsigned int test_uuid_random_function_simple(int max)
10105 return GetSimpleRandom(max);
10108 static unsigned int test_uuid_random_function_better(int max)
10110 return (max > 0 ? prng_get_uint() % max : 0);
10113 #if defined(PLATFORM_WINDOWS)
10114 #define NUM_UUID_TESTS 3
10116 #define NUM_UUID_TESTS 2
10119 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10121 struct hashtable *hash_seeds =
10122 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10123 struct hashtable *hash_uuids =
10124 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10125 static char message[100];
10128 char *random_name = (nr == 0 ? "simple" : "better");
10129 char *random_type = (always_seed ? "always" : "only once");
10130 char *(*init_random_function)(void) =
10132 test_init_uuid_random_function_simple :
10133 test_init_uuid_random_function_better);
10134 unsigned int (*random_function)(int) =
10136 test_uuid_random_function_simple :
10137 test_uuid_random_function_better);
10140 #if defined(PLATFORM_WINDOWS)
10143 random_name = "windows";
10144 init_random_function = test_init_uuid_random_function_better_windows;
10150 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10151 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10153 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10154 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10155 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10157 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10161 // always initialize random number generator at least once
10162 init_random_function();
10164 unsigned int time_start = SDL_GetTicks();
10166 for (i = 0; i < num_uuids; i++)
10170 char *seed = getStringCopy(init_random_function());
10172 hashtable_remove(hash_seeds, seed);
10173 hashtable_insert(hash_seeds, seed, "1");
10176 char *uuid = getStringCopy(getUUIDExt(random_function));
10178 hashtable_remove(hash_uuids, uuid);
10179 hashtable_insert(hash_uuids, uuid, "1");
10182 int num_unique_seeds = hashtable_count(hash_seeds);
10183 int num_unique_uuids = hashtable_count(hash_uuids);
10185 unsigned int time_needed = SDL_GetTicks() - time_start;
10187 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10189 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10192 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10194 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10195 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10197 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10199 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10201 Request(message, REQ_CONFIRM);
10203 hashtable_destroy(hash_seeds, 0);
10204 hashtable_destroy(hash_uuids, 0);
10207 void TestGeneratingUUIDs(void)
10209 int num_uuids = 1000000;
10212 for (i = 0; i < NUM_UUID_TESTS; i++)
10213 for (j = 0; j < 2; j++)
10214 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10216 CloseAllAndExit(0);