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 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
3012 int xsize, int ysize)
3014 if (!global.use_envelope_request ||
3015 request.sort_priority <= 0)
3018 if (request.bitmap == NULL ||
3019 xsize > request.xsize ||
3020 ysize > request.ysize)
3022 if (request.bitmap != NULL)
3023 FreeBitmap(request.bitmap);
3025 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
3027 SDL_Surface *surface = request.bitmap->surface;
3029 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
3030 Fail("SDLGetNativeSurface() failed");
3033 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3035 SDLFreeBitmapTextures(request.bitmap);
3036 SDLCreateBitmapTextures(request.bitmap);
3038 // set envelope request run-time values
3041 request.xsize = xsize;
3042 request.ysize = ysize;
3045 void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage)
3047 if (global.use_envelope_request &&
3048 game.request_active_or_moving &&
3049 request.sort_priority > 0 &&
3050 drawing_target == DRAW_TO_SCREEN &&
3051 drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
3053 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3054 request.sx, request.sy);
3058 static void setRequestBasePosition(int *x, int *y)
3060 int sx_base, sy_base;
3062 if (request.x != -1)
3063 sx_base = request.x;
3064 else if (request.align == ALIGN_LEFT)
3066 else if (request.align == ALIGN_RIGHT)
3067 sx_base = SX + SXSIZE;
3069 sx_base = SX + SXSIZE / 2;
3071 if (request.y != -1)
3072 sy_base = request.y;
3073 else if (request.valign == VALIGN_TOP)
3075 else if (request.valign == VALIGN_BOTTOM)
3076 sy_base = SY + SYSIZE;
3078 sy_base = SY + SYSIZE / 2;
3084 static void setRequestPositionExt(int *x, int *y, int width, int height,
3085 boolean add_border_size)
3087 int border_size = request.border_size;
3088 int sx_base, sy_base;
3091 setRequestBasePosition(&sx_base, &sy_base);
3093 if (request.align == ALIGN_LEFT)
3095 else if (request.align == ALIGN_RIGHT)
3096 sx = sx_base - width;
3098 sx = sx_base - width / 2;
3100 if (request.valign == VALIGN_TOP)
3102 else if (request.valign == VALIGN_BOTTOM)
3103 sy = sy_base - height;
3105 sy = sy_base - height / 2;
3107 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3108 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3110 if (add_border_size)
3120 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3122 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3125 static void DrawEnvelopeRequest(char *text)
3127 char *text_final = text;
3128 char *text_door_style = NULL;
3129 int graphic = IMG_BACKGROUND_REQUEST;
3130 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3131 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3132 int font_nr = FONT_REQUEST;
3133 int font_width = getFontWidth(font_nr);
3134 int font_height = getFontHeight(font_nr);
3135 int border_size = request.border_size;
3136 int line_spacing = request.line_spacing;
3137 int line_height = font_height + line_spacing;
3138 int max_text_width = request.width - 2 * border_size;
3139 int max_text_height = request.height - 2 * border_size;
3140 int line_length = max_text_width / font_width;
3141 int max_lines = max_text_height / line_height;
3142 int text_width = line_length * font_width;
3143 int width = request.width;
3144 int height = request.height;
3145 int tile_size = MAX(request.step_offset, 1);
3146 int x_steps = width / tile_size;
3147 int y_steps = height / tile_size;
3148 int sx_offset = border_size;
3149 int sy_offset = border_size;
3153 if (request.centered)
3154 sx_offset = (request.width - text_width) / 2;
3156 if (request.wrap_single_words && !request.autowrap)
3158 char *src_text_ptr, *dst_text_ptr;
3160 text_door_style = checked_malloc(2 * strlen(text) + 1);
3162 src_text_ptr = text;
3163 dst_text_ptr = text_door_style;
3165 while (*src_text_ptr)
3167 if (*src_text_ptr == ' ' ||
3168 *src_text_ptr == '?' ||
3169 *src_text_ptr == '!')
3170 *dst_text_ptr++ = '\n';
3172 if (*src_text_ptr != ' ')
3173 *dst_text_ptr++ = *src_text_ptr;
3178 *dst_text_ptr = '\0';
3180 text_final = text_door_style;
3183 setRequestPosition(&sx, &sy, FALSE);
3185 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3187 for (y = 0; y < y_steps; y++)
3188 for (x = 0; x < x_steps; x++)
3189 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3190 x, y, x_steps, y_steps,
3191 tile_size, tile_size);
3193 // force DOOR font inside door area
3194 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3196 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3197 line_length, -1, max_lines, line_spacing, mask_mode,
3198 request.autowrap, request.centered, FALSE);
3202 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3203 RedrawGadget(tool_gadget[i]);
3205 // store readily prepared envelope request for later use when animating
3206 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3208 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3210 if (text_door_style)
3211 free(text_door_style);
3214 static void AnimateEnvelopeRequest(int anim_mode, int action)
3216 int graphic = IMG_BACKGROUND_REQUEST;
3217 boolean draw_masked = graphic_info[graphic].draw_masked;
3218 int delay_value_normal = request.step_delay;
3219 int delay_value_fast = delay_value_normal / 2;
3220 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3221 boolean no_delay = (tape.warp_forward);
3222 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3223 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3224 DelayCounter anim_delay = { anim_delay_value };
3226 int tile_size = MAX(request.step_offset, 1);
3227 int max_xsize = request.width / tile_size;
3228 int max_ysize = request.height / tile_size;
3229 int max_xsize_inner = max_xsize - 2;
3230 int max_ysize_inner = max_ysize - 2;
3232 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3233 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3234 int xend = max_xsize_inner;
3235 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3236 int xstep = (xstart < xend ? 1 : 0);
3237 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3239 int end = MAX(xend - xstart, yend - ystart);
3242 if (setup.quick_doors)
3249 for (i = start; i <= end; i++)
3251 int last_frame = end; // last frame of this "for" loop
3252 int x = xstart + i * xstep;
3253 int y = ystart + i * ystep;
3254 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3255 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3256 int xsize_size_left = (xsize - 1) * tile_size;
3257 int ysize_size_top = (ysize - 1) * tile_size;
3258 int max_xsize_pos = (max_xsize - 1) * tile_size;
3259 int max_ysize_pos = (max_ysize - 1) * tile_size;
3260 int width = xsize * tile_size;
3261 int height = ysize * tile_size;
3266 setRequestPosition(&src_x, &src_y, FALSE);
3267 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3269 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3271 for (yy = 0; yy < 2; yy++)
3273 for (xx = 0; xx < 2; xx++)
3275 int src_xx = src_x + xx * max_xsize_pos;
3276 int src_yy = src_y + yy * max_ysize_pos;
3277 int dst_xx = dst_x + xx * xsize_size_left;
3278 int dst_yy = dst_y + yy * ysize_size_top;
3279 int xx_size = (xx ? tile_size : xsize_size_left);
3280 int yy_size = (yy ? tile_size : ysize_size_top);
3283 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3284 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3286 BlitBitmap(bitmap_db_store_2, backbuffer,
3287 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3291 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3293 redraw_mask |= REDRAW_FIELD;
3297 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3300 ClearAutoRepeatKeyEvents();
3303 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3305 int graphic = IMG_BACKGROUND_REQUEST;
3306 int sound_opening = SND_REQUEST_OPENING;
3307 int sound_closing = SND_REQUEST_CLOSING;
3308 int anim_mode_1 = request.anim_mode; // (higher priority)
3309 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3310 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3311 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3312 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3314 if (game_status == GAME_MODE_PLAYING)
3315 BlitScreenToBitmap(backbuffer);
3317 SetDrawtoField(DRAW_TO_BACKBUFFER);
3319 // SetDrawBackgroundMask(REDRAW_NONE);
3321 if (action == ACTION_OPENING)
3323 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3325 if (req_state & REQ_ASK)
3327 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3328 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3329 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3330 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3332 else if (req_state & REQ_CONFIRM)
3334 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3335 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3337 else if (req_state & REQ_PLAYER)
3339 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3340 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3341 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3342 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3345 DrawEnvelopeRequest(text);
3348 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3350 if (action == ACTION_OPENING)
3352 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3354 if (anim_mode == ANIM_DEFAULT)
3355 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3357 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3361 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3363 if (anim_mode != ANIM_NONE)
3364 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3366 if (anim_mode == ANIM_DEFAULT)
3367 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3370 game.envelope_active = FALSE;
3372 if (action == ACTION_CLOSING)
3373 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3375 // SetDrawBackgroundMask(last_draw_background_mask);
3377 redraw_mask |= REDRAW_FIELD;
3381 if (action == ACTION_CLOSING &&
3382 game_status == GAME_MODE_PLAYING &&
3383 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3384 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3387 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3389 if (IS_MM_WALL(element))
3391 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3397 int graphic = el2preimg(element);
3399 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3400 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3405 void DrawLevel(int draw_background_mask)
3409 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3410 SetDrawBackgroundMask(draw_background_mask);
3414 for (x = BX1; x <= BX2; x++)
3415 for (y = BY1; y <= BY2; y++)
3416 DrawScreenField(x, y);
3418 redraw_mask |= REDRAW_FIELD;
3421 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3426 for (x = 0; x < size_x; x++)
3427 for (y = 0; y < size_y; y++)
3428 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3430 redraw_mask |= REDRAW_FIELD;
3433 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3437 for (x = 0; x < size_x; x++)
3438 for (y = 0; y < size_y; y++)
3439 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3441 redraw_mask |= REDRAW_FIELD;
3444 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3446 boolean show_level_border = (BorderElement != EL_EMPTY);
3447 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3448 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3449 int tile_size = preview.tile_size;
3450 int preview_width = preview.xsize * tile_size;
3451 int preview_height = preview.ysize * tile_size;
3452 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3453 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3454 int real_preview_width = real_preview_xsize * tile_size;
3455 int real_preview_height = real_preview_ysize * tile_size;
3456 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3457 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3460 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3463 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3465 dst_x += (preview_width - real_preview_width) / 2;
3466 dst_y += (preview_height - real_preview_height) / 2;
3468 for (x = 0; x < real_preview_xsize; x++)
3470 for (y = 0; y < real_preview_ysize; y++)
3472 int lx = from_x + x + (show_level_border ? -1 : 0);
3473 int ly = from_y + y + (show_level_border ? -1 : 0);
3474 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3475 getBorderElement(lx, ly));
3477 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3478 element, tile_size);
3482 redraw_mask |= REDRAW_FIELD;
3485 #define MICROLABEL_EMPTY 0
3486 #define MICROLABEL_LEVEL_NAME 1
3487 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3488 #define MICROLABEL_LEVEL_AUTHOR 3
3489 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3490 #define MICROLABEL_IMPORTED_FROM 5
3491 #define MICROLABEL_IMPORTED_BY_HEAD 6
3492 #define MICROLABEL_IMPORTED_BY 7
3494 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3496 int max_text_width = SXSIZE;
3497 int font_width = getFontWidth(font_nr);
3499 if (pos->align == ALIGN_CENTER)
3500 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3501 else if (pos->align == ALIGN_RIGHT)
3502 max_text_width = pos->x;
3504 max_text_width = SXSIZE - pos->x;
3506 return max_text_width / font_width;
3509 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3511 char label_text[MAX_OUTPUT_LINESIZE + 1];
3512 int max_len_label_text;
3513 int font_nr = pos->font;
3516 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3519 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3520 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3521 mode == MICROLABEL_IMPORTED_BY_HEAD)
3522 font_nr = pos->font_alt;
3524 max_len_label_text = getMaxTextLength(pos, font_nr);
3526 if (pos->size != -1)
3527 max_len_label_text = pos->size;
3529 for (i = 0; i < max_len_label_text; i++)
3530 label_text[i] = ' ';
3531 label_text[max_len_label_text] = '\0';
3533 if (strlen(label_text) > 0)
3534 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3537 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3538 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3539 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3540 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3541 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3542 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3543 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3544 max_len_label_text);
3545 label_text[max_len_label_text] = '\0';
3547 if (strlen(label_text) > 0)
3548 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3550 redraw_mask |= REDRAW_FIELD;
3553 static void DrawPreviewLevelLabel(int mode)
3555 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3558 static void DrawPreviewLevelInfo(int mode)
3560 if (mode == MICROLABEL_LEVEL_NAME)
3561 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3562 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3563 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3566 static void DrawPreviewLevelExt(boolean restart)
3568 static DelayCounter scroll_delay = { 0 };
3569 static DelayCounter label_delay = { 0 };
3570 static int from_x, from_y, scroll_direction;
3571 static int label_state, label_counter;
3572 boolean show_level_border = (BorderElement != EL_EMPTY);
3573 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3574 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3576 scroll_delay.value = preview.step_delay;
3577 label_delay.value = MICROLEVEL_LABEL_DELAY;
3584 if (preview.anim_mode == ANIM_CENTERED)
3586 if (level_xsize > preview.xsize)
3587 from_x = (level_xsize - preview.xsize) / 2;
3588 if (level_ysize > preview.ysize)
3589 from_y = (level_ysize - preview.ysize) / 2;
3592 from_x += preview.xoffset;
3593 from_y += preview.yoffset;
3595 scroll_direction = MV_RIGHT;
3599 DrawPreviewLevelPlayfield(from_x, from_y);
3600 DrawPreviewLevelLabel(label_state);
3602 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3603 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3605 // initialize delay counters
3606 ResetDelayCounter(&scroll_delay);
3607 ResetDelayCounter(&label_delay);
3609 if (leveldir_current->name)
3611 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3612 char label_text[MAX_OUTPUT_LINESIZE + 1];
3613 int font_nr = pos->font;
3614 int max_len_label_text = getMaxTextLength(pos, font_nr);
3616 if (pos->size != -1)
3617 max_len_label_text = pos->size;
3619 strncpy(label_text, leveldir_current->name, max_len_label_text);
3620 label_text[max_len_label_text] = '\0';
3622 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3623 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3629 // scroll preview level, if needed
3630 if (preview.anim_mode != ANIM_NONE &&
3631 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3632 DelayReached(&scroll_delay))
3634 switch (scroll_direction)
3639 from_x -= preview.step_offset;
3640 from_x = (from_x < 0 ? 0 : from_x);
3643 scroll_direction = MV_UP;
3647 if (from_x < level_xsize - preview.xsize)
3649 from_x += preview.step_offset;
3650 from_x = (from_x > level_xsize - preview.xsize ?
3651 level_xsize - preview.xsize : from_x);
3654 scroll_direction = MV_DOWN;
3660 from_y -= preview.step_offset;
3661 from_y = (from_y < 0 ? 0 : from_y);
3664 scroll_direction = MV_RIGHT;
3668 if (from_y < level_ysize - preview.ysize)
3670 from_y += preview.step_offset;
3671 from_y = (from_y > level_ysize - preview.ysize ?
3672 level_ysize - preview.ysize : from_y);
3675 scroll_direction = MV_LEFT;
3682 DrawPreviewLevelPlayfield(from_x, from_y);
3685 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3686 // redraw micro level label, if needed
3687 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3688 !strEqual(level.author, ANONYMOUS_NAME) &&
3689 !strEqual(level.author, leveldir_current->name) &&
3690 DelayReached(&label_delay))
3692 int max_label_counter = 23;
3694 if (leveldir_current->imported_from != NULL &&
3695 strlen(leveldir_current->imported_from) > 0)
3696 max_label_counter += 14;
3697 if (leveldir_current->imported_by != NULL &&
3698 strlen(leveldir_current->imported_by) > 0)
3699 max_label_counter += 14;
3701 label_counter = (label_counter + 1) % max_label_counter;
3702 label_state = (label_counter >= 0 && label_counter <= 7 ?
3703 MICROLABEL_LEVEL_NAME :
3704 label_counter >= 9 && label_counter <= 12 ?
3705 MICROLABEL_LEVEL_AUTHOR_HEAD :
3706 label_counter >= 14 && label_counter <= 21 ?
3707 MICROLABEL_LEVEL_AUTHOR :
3708 label_counter >= 23 && label_counter <= 26 ?
3709 MICROLABEL_IMPORTED_FROM_HEAD :
3710 label_counter >= 28 && label_counter <= 35 ?
3711 MICROLABEL_IMPORTED_FROM :
3712 label_counter >= 37 && label_counter <= 40 ?
3713 MICROLABEL_IMPORTED_BY_HEAD :
3714 label_counter >= 42 && label_counter <= 49 ?
3715 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3717 if (leveldir_current->imported_from == NULL &&
3718 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3719 label_state == MICROLABEL_IMPORTED_FROM))
3720 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3721 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3723 DrawPreviewLevelLabel(label_state);
3727 void DrawPreviewPlayers(void)
3729 if (game_status != GAME_MODE_MAIN)
3732 // do not draw preview players if level preview redefined, but players aren't
3733 if (preview.redefined && !menu.main.preview_players.redefined)
3736 boolean player_found[MAX_PLAYERS];
3737 int num_players = 0;
3740 for (i = 0; i < MAX_PLAYERS; i++)
3741 player_found[i] = FALSE;
3743 // check which players can be found in the level (simple approach)
3744 for (x = 0; x < lev_fieldx; x++)
3746 for (y = 0; y < lev_fieldy; y++)
3748 int element = level.field[x][y];
3750 if (IS_PLAYER_ELEMENT(element))
3752 int player_nr = GET_PLAYER_NR(element);
3754 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3756 if (!player_found[player_nr])
3759 player_found[player_nr] = TRUE;
3764 struct TextPosInfo *pos = &menu.main.preview_players;
3765 int tile_size = pos->tile_size;
3766 int border_size = pos->border_size;
3767 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3768 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3769 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3770 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3771 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3772 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3773 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3774 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3775 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3776 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3777 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3778 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3780 // clear area in which the players will be drawn
3781 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3782 max_players_width, max_players_height);
3784 if (!network.enabled && !setup.team_mode)
3787 // only draw players if level is suited for team mode
3788 if (num_players < 2)
3791 // draw all players that were found in the level
3792 for (i = 0; i < MAX_PLAYERS; i++)
3794 if (player_found[i])
3796 int graphic = el2img(EL_PLAYER_1 + i);
3798 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3800 xpos += player_xoffset;
3801 ypos += player_yoffset;
3806 void DrawPreviewLevelInitial(void)
3808 DrawPreviewLevelExt(TRUE);
3809 DrawPreviewPlayers();
3812 void DrawPreviewLevelAnimation(void)
3814 DrawPreviewLevelExt(FALSE);
3817 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3818 int border_size, int font_nr)
3820 int graphic = el2img(EL_PLAYER_1 + player_nr);
3821 int font_height = getFontHeight(font_nr);
3822 int player_height = MAX(tile_size, font_height);
3823 int xoffset_text = tile_size + border_size;
3824 int yoffset_text = (player_height - font_height) / 2;
3825 int yoffset_graphic = (player_height - tile_size) / 2;
3826 char *player_name = getNetworkPlayerName(player_nr + 1);
3828 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3830 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3833 static void DrawNetworkPlayersExt(boolean force)
3835 if (game_status != GAME_MODE_MAIN)
3838 if (!network.connected && !force)
3841 // do not draw network players if level preview redefined, but players aren't
3842 if (preview.redefined && !menu.main.network_players.redefined)
3845 int num_players = 0;
3848 for (i = 0; i < MAX_PLAYERS; i++)
3849 if (stored_player[i].connected_network)
3852 struct TextPosInfo *pos = &menu.main.network_players;
3853 int tile_size = pos->tile_size;
3854 int border_size = pos->border_size;
3855 int xoffset_text = tile_size + border_size;
3856 int font_nr = pos->font;
3857 int font_width = getFontWidth(font_nr);
3858 int font_height = getFontHeight(font_nr);
3859 int player_height = MAX(tile_size, font_height);
3860 int player_yoffset = player_height + border_size;
3861 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3862 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3863 int all_players_height = num_players * player_yoffset - border_size;
3864 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3865 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3866 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3868 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3869 max_players_width, max_players_height);
3871 // first draw local network player ...
3872 for (i = 0; i < MAX_PLAYERS; i++)
3874 if (stored_player[i].connected_network &&
3875 stored_player[i].connected_locally)
3877 char *player_name = getNetworkPlayerName(i + 1);
3878 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3879 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3881 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3883 ypos += player_yoffset;
3887 // ... then draw all other network players
3888 for (i = 0; i < MAX_PLAYERS; i++)
3890 if (stored_player[i].connected_network &&
3891 !stored_player[i].connected_locally)
3893 char *player_name = getNetworkPlayerName(i + 1);
3894 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3895 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3897 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3899 ypos += player_yoffset;
3904 void DrawNetworkPlayers(void)
3906 DrawNetworkPlayersExt(FALSE);
3909 void ClearNetworkPlayers(void)
3911 DrawNetworkPlayersExt(TRUE);
3914 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3915 int graphic, int lx, int ly,
3918 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3920 if (mask_mode == USE_MASKING)
3921 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3923 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3926 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3927 int graphic, int sync_frame, int mask_mode)
3929 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3931 if (mask_mode == USE_MASKING)
3932 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3934 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3937 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3938 int graphic, int sync_frame, int tilesize,
3941 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3943 if (mask_mode == USE_MASKING)
3944 DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
3946 DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
3949 static void DrawGraphicAnimation(int x, int y, int graphic)
3951 int lx = LEVELX(x), ly = LEVELY(y);
3952 int mask_mode = NO_MASKING;
3954 if (!IN_SCR_FIELD(x, y))
3957 if (game.use_masked_elements)
3959 if (Tile[lx][ly] != EL_EMPTY)
3961 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3963 mask_mode = USE_MASKING;
3967 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3968 graphic, lx, ly, mask_mode);
3970 MarkTileDirty(x, y);
3973 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3975 int lx = LEVELX(x), ly = LEVELY(y);
3976 int mask_mode = NO_MASKING;
3978 if (!IN_SCR_FIELD(x, y))
3981 if (game.use_masked_elements)
3983 if (Tile[lx][ly] != EL_EMPTY)
3985 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3987 mask_mode = USE_MASKING;
3991 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3992 graphic, lx, ly, mask_mode);
3994 MarkTileDirty(x, y);
3997 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3999 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4002 void DrawLevelElementAnimation(int x, int y, int element)
4004 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4006 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4009 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
4011 int sx = SCREENX(x), sy = SCREENY(y);
4013 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4016 if (Tile[x][y] == EL_EMPTY)
4017 graphic = el2img(GfxElementEmpty[x][y]);
4019 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4022 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4025 DrawGraphicAnimation(sx, sy, graphic);
4028 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4029 DrawLevelFieldCrumbled(x, y);
4031 if (GFX_CRUMBLED(Tile[x][y]))
4032 DrawLevelFieldCrumbled(x, y);
4036 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4038 int sx = SCREENX(x), sy = SCREENY(y);
4041 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4044 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4046 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4049 DrawGraphicAnimation(sx, sy, graphic);
4051 if (GFX_CRUMBLED(element))
4052 DrawLevelFieldCrumbled(x, y);
4055 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4057 if (player->use_murphy)
4059 // this works only because currently only one player can be "murphy" ...
4060 static int last_horizontal_dir = MV_LEFT;
4061 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4063 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4064 last_horizontal_dir = move_dir;
4066 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4068 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4070 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4076 return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4079 static boolean equalGraphics(int graphic1, int graphic2)
4081 struct GraphicInfo *g1 = &graphic_info[graphic1];
4082 struct GraphicInfo *g2 = &graphic_info[graphic2];
4084 return (g1->bitmap == g2->bitmap &&
4085 g1->src_x == g2->src_x &&
4086 g1->src_y == g2->src_y &&
4087 g1->anim_frames == g2->anim_frames &&
4088 g1->anim_delay == g2->anim_delay &&
4089 g1->anim_mode == g2->anim_mode);
4092 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4096 DRAW_PLAYER_STAGE_INIT = 0,
4097 DRAW_PLAYER_STAGE_LAST_FIELD,
4098 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4099 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4100 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4101 DRAW_PLAYER_STAGE_PLAYER,
4103 DRAW_PLAYER_STAGE_PLAYER,
4104 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4106 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4107 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4109 NUM_DRAW_PLAYER_STAGES
4112 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4114 static int static_last_player_graphic[MAX_PLAYERS];
4115 static int static_last_player_frame[MAX_PLAYERS];
4116 static boolean static_player_is_opaque[MAX_PLAYERS];
4117 static boolean draw_player[MAX_PLAYERS];
4118 int pnr = player->index_nr;
4120 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4122 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4123 static_last_player_frame[pnr] = player->Frame;
4124 static_player_is_opaque[pnr] = FALSE;
4126 draw_player[pnr] = TRUE;
4129 if (!draw_player[pnr])
4133 if (!IN_LEV_FIELD(player->jx, player->jy))
4135 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4136 Debug("draw:DrawPlayerExt", "This should never happen!");
4138 draw_player[pnr] = FALSE;
4144 int last_player_graphic = static_last_player_graphic[pnr];
4145 int last_player_frame = static_last_player_frame[pnr];
4146 boolean player_is_opaque = static_player_is_opaque[pnr];
4148 int jx = player->jx;
4149 int jy = player->jy;
4150 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4151 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4152 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4153 int last_jx = (player->is_moving ? jx - dx : jx);
4154 int last_jy = (player->is_moving ? jy - dy : jy);
4155 int next_jx = jx + dx;
4156 int next_jy = jy + dy;
4157 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4158 int sx = SCREENX(jx);
4159 int sy = SCREENY(jy);
4160 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4161 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4162 int element = Tile[jx][jy];
4163 int last_element = Tile[last_jx][last_jy];
4164 int action = (player->is_pushing ? ACTION_PUSHING :
4165 player->is_digging ? ACTION_DIGGING :
4166 player->is_collecting ? ACTION_COLLECTING :
4167 player->is_moving ? ACTION_MOVING :
4168 player->is_snapping ? ACTION_SNAPPING :
4169 player->is_dropping ? ACTION_DROPPING :
4170 player->is_waiting ? player->action_waiting :
4173 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4175 // ------------------------------------------------------------------------
4176 // initialize drawing the player
4177 // ------------------------------------------------------------------------
4179 draw_player[pnr] = FALSE;
4181 // GfxElement[][] is set to the element the player is digging or collecting;
4182 // remove also for off-screen player if the player is not moving anymore
4183 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4184 GfxElement[jx][jy] = EL_UNDEFINED;
4186 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4189 if (element == EL_EXPLOSION)
4192 InitPlayerGfxAnimation(player, action, move_dir);
4194 draw_player[pnr] = TRUE;
4196 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4198 // ------------------------------------------------------------------------
4199 // draw things in the field the player is leaving, if needed
4200 // ------------------------------------------------------------------------
4202 if (!IN_SCR_FIELD(sx, sy))
4203 draw_player[pnr] = FALSE;
4205 if (!player->is_moving)
4208 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4210 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4212 if (last_element == EL_DYNAMITE_ACTIVE ||
4213 last_element == EL_EM_DYNAMITE_ACTIVE ||
4214 last_element == EL_SP_DISK_RED_ACTIVE)
4215 DrawDynamite(last_jx, last_jy);
4217 DrawLevelFieldThruMask(last_jx, last_jy);
4219 else if (last_element == EL_DYNAMITE_ACTIVE ||
4220 last_element == EL_EM_DYNAMITE_ACTIVE ||
4221 last_element == EL_SP_DISK_RED_ACTIVE)
4222 DrawDynamite(last_jx, last_jy);
4224 DrawLevelField(last_jx, last_jy);
4226 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4228 // ------------------------------------------------------------------------
4229 // draw things behind the player, if needed
4230 // ------------------------------------------------------------------------
4234 DrawLevelElement(jx, jy, Back[jx][jy]);
4239 if (IS_ACTIVE_BOMB(element))
4241 DrawLevelElement(jx, jy, EL_EMPTY);
4246 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4248 int old_element = GfxElement[jx][jy];
4249 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4250 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4252 if (GFX_CRUMBLED(old_element))
4253 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4255 DrawScreenGraphic(sx, sy, old_graphic, frame);
4257 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4258 static_player_is_opaque[pnr] = TRUE;
4262 GfxElement[jx][jy] = EL_UNDEFINED;
4264 // make sure that pushed elements are drawn with correct frame rate
4265 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4267 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4268 GfxFrame[jx][jy] = player->StepFrame;
4270 DrawLevelField(jx, jy);
4273 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4275 // ------------------------------------------------------------------------
4276 // draw things the player is pushing, if needed
4277 // ------------------------------------------------------------------------
4279 if (!player->is_pushing || !player->is_moving)
4282 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4285 int gfx_frame = GfxFrame[jx][jy];
4287 if (!IS_MOVING(jx, jy)) // push movement already finished
4289 element = Tile[next_jx][next_jy];
4290 gfx_frame = GfxFrame[next_jx][next_jy];
4293 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4294 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4295 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4297 // draw background element under pushed element (like the Sokoban field)
4298 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4300 // this allows transparent pushing animation over non-black background
4303 DrawLevelElement(jx, jy, Back[jx][jy]);
4305 DrawLevelElement(jx, jy, EL_EMPTY);
4308 if (Back[next_jx][next_jy])
4309 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4311 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4313 int px = SCREENX(jx), py = SCREENY(jy);
4314 int pxx = (TILEX - ABS(sxx)) * dx;
4315 int pyy = (TILEY - ABS(syy)) * dy;
4318 // do not draw (EM style) pushing animation when pushing is finished
4319 // (two-tile animations usually do not contain start and end frame)
4320 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4321 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4323 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4325 // masked drawing is needed for EMC style (double) movement graphics
4326 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4327 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4330 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4332 // ------------------------------------------------------------------------
4333 // draw player himself
4334 // ------------------------------------------------------------------------
4336 int graphic = getPlayerGraphic(player, move_dir);
4338 // in the case of changed player action or direction, prevent the current
4339 // animation frame from being restarted for identical animations
4340 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4341 player->Frame = last_player_frame;
4343 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4345 if (player_is_opaque)
4346 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4348 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4350 if (SHIELD_ON(player))
4352 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4353 IMG_SHIELD_NORMAL_ACTIVE);
4354 frame = getGraphicAnimationFrame(graphic, -1);
4356 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4359 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4361 // ------------------------------------------------------------------------
4362 // draw things in front of player (active dynamite or dynabombs)
4363 // ------------------------------------------------------------------------
4365 if (IS_ACTIVE_BOMB(element))
4367 int graphic = el2img(element);
4368 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4370 if (game.emulation == EMU_SUPAPLEX)
4371 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4373 DrawGraphicThruMask(sx, sy, graphic, frame);
4376 if (player_is_moving && last_element == EL_EXPLOSION)
4378 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4379 GfxElement[last_jx][last_jy] : EL_EMPTY);
4380 int graphic = el_act2img(element, ACTION_EXPLODING);
4381 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4382 int phase = ExplodePhase[last_jx][last_jy] - 1;
4383 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4386 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4389 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4391 // ------------------------------------------------------------------------
4392 // draw elements the player is just walking/passing through/under
4393 // ------------------------------------------------------------------------
4395 if (player_is_moving)
4397 // handle the field the player is leaving ...
4398 if (IS_ACCESSIBLE_INSIDE(last_element))
4399 DrawLevelField(last_jx, last_jy);
4400 else if (IS_ACCESSIBLE_UNDER(last_element))
4401 DrawLevelFieldThruMask(last_jx, last_jy);
4404 // do not redraw accessible elements if the player is just pushing them
4405 if (!player_is_moving || !player->is_pushing)
4407 // ... and the field the player is entering
4408 if (IS_ACCESSIBLE_INSIDE(element))
4409 DrawLevelField(jx, jy);
4410 else if (IS_ACCESSIBLE_UNDER(element))
4411 DrawLevelFieldThruMask(jx, jy);
4414 MarkTileDirty(sx, sy);
4418 void DrawPlayer(struct PlayerInfo *player)
4422 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4423 DrawPlayerExt(player, i);
4426 void DrawAllPlayers(void)
4430 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4431 for (j = 0; j < MAX_PLAYERS; j++)
4432 if (stored_player[j].active)
4433 DrawPlayerExt(&stored_player[j], i);
4436 void DrawPlayerField(int x, int y)
4438 if (!IS_PLAYER(x, y))
4441 DrawPlayer(PLAYERINFO(x, y));
4444 // ----------------------------------------------------------------------------
4446 void WaitForEventToContinue(void)
4448 boolean first_wait = TRUE;
4449 boolean still_wait = TRUE;
4451 if (program.headless)
4454 // simulate releasing mouse button over last gadget, if still pressed
4456 HandleGadgets(-1, -1, 0);
4458 button_status = MB_RELEASED;
4461 ClearPlayerAction();
4467 if (NextValidEvent(&event))
4471 case EVENT_BUTTONPRESS:
4472 case EVENT_FINGERPRESS:
4476 case EVENT_BUTTONRELEASE:
4477 case EVENT_FINGERRELEASE:
4478 still_wait = first_wait;
4481 case EVENT_KEYPRESS:
4482 case SDL_CONTROLLERBUTTONDOWN:
4483 case SDL_JOYBUTTONDOWN:
4488 HandleOtherEvents(&event);
4492 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4497 if (!PendingEvent())
4502 #define MAX_REQUEST_LINES 13
4503 #define MAX_REQUEST_LINE_FONT1_LEN 7
4504 #define MAX_REQUEST_LINE_FONT2_LEN 10
4506 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4508 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4510 int draw_buffer_last = GetDrawtoField();
4511 int width = request.width;
4512 int height = request.height;
4516 // when showing request dialog after game ended, deactivate game panel
4517 if (game_just_ended)
4518 game.panel.active = FALSE;
4520 game.request_active = TRUE;
4522 setRequestPosition(&sx, &sy, FALSE);
4524 button_status = MB_RELEASED;
4526 request_gadget_id = -1;
4531 boolean event_handled = FALSE;
4533 if (game_just_ended)
4535 SetDrawtoField(draw_buffer_game);
4537 HandleGameActions();
4539 SetDrawtoField(DRAW_TO_BACKBUFFER);
4541 if (global.use_envelope_request)
4543 // copy current state of request area to middle of playfield area
4544 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4552 while (NextValidEvent(&event))
4554 event_handled = TRUE;
4558 case EVENT_BUTTONPRESS:
4559 case EVENT_BUTTONRELEASE:
4560 case EVENT_MOTIONNOTIFY:
4564 if (event.type == EVENT_MOTIONNOTIFY)
4569 motion_status = TRUE;
4570 mx = ((MotionEvent *) &event)->x;
4571 my = ((MotionEvent *) &event)->y;
4575 motion_status = FALSE;
4576 mx = ((ButtonEvent *) &event)->x;
4577 my = ((ButtonEvent *) &event)->y;
4578 if (event.type == EVENT_BUTTONPRESS)
4579 button_status = ((ButtonEvent *) &event)->button;
4581 button_status = MB_RELEASED;
4584 // this sets 'request_gadget_id'
4585 HandleGadgets(mx, my, button_status);
4587 switch (request_gadget_id)
4589 case TOOL_CTRL_ID_YES:
4590 case TOOL_CTRL_ID_TOUCH_YES:
4593 case TOOL_CTRL_ID_NO:
4594 case TOOL_CTRL_ID_TOUCH_NO:
4597 case TOOL_CTRL_ID_CONFIRM:
4598 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4599 result = TRUE | FALSE;
4602 case TOOL_CTRL_ID_PLAYER_1:
4605 case TOOL_CTRL_ID_PLAYER_2:
4608 case TOOL_CTRL_ID_PLAYER_3:
4611 case TOOL_CTRL_ID_PLAYER_4:
4616 // only check clickable animations if no request gadget clicked
4617 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4624 case SDL_WINDOWEVENT:
4625 HandleWindowEvent((WindowEvent *) &event);
4628 case SDL_APP_WILLENTERBACKGROUND:
4629 case SDL_APP_DIDENTERBACKGROUND:
4630 case SDL_APP_WILLENTERFOREGROUND:
4631 case SDL_APP_DIDENTERFOREGROUND:
4632 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4635 case EVENT_KEYPRESS:
4637 Key key = GetEventKey((KeyEvent *)&event);
4642 if (req_state & REQ_CONFIRM)
4651 #if defined(KSYM_Rewind)
4652 case KSYM_Rewind: // for Amazon Fire TV remote
4661 #if defined(KSYM_FastForward)
4662 case KSYM_FastForward: // for Amazon Fire TV remote
4668 HandleKeysDebug(key, KEY_PRESSED);
4672 if (req_state & REQ_PLAYER)
4674 int old_player_nr = setup.network_player_nr;
4677 result = old_player_nr + 1;
4682 result = old_player_nr + 1;
4713 case EVENT_FINGERRELEASE:
4714 case EVENT_KEYRELEASE:
4715 ClearPlayerAction();
4718 case SDL_CONTROLLERBUTTONDOWN:
4719 switch (event.cbutton.button)
4721 case SDL_CONTROLLER_BUTTON_A:
4722 case SDL_CONTROLLER_BUTTON_X:
4723 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4724 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4728 case SDL_CONTROLLER_BUTTON_B:
4729 case SDL_CONTROLLER_BUTTON_Y:
4730 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4731 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4732 case SDL_CONTROLLER_BUTTON_BACK:
4737 if (req_state & REQ_PLAYER)
4739 int old_player_nr = setup.network_player_nr;
4742 result = old_player_nr + 1;
4744 switch (event.cbutton.button)
4746 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4747 case SDL_CONTROLLER_BUTTON_Y:
4751 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4752 case SDL_CONTROLLER_BUTTON_B:
4756 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4757 case SDL_CONTROLLER_BUTTON_A:
4761 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4762 case SDL_CONTROLLER_BUTTON_X:
4773 case SDL_CONTROLLERBUTTONUP:
4774 HandleJoystickEvent(&event);
4775 ClearPlayerAction();
4779 HandleOtherEvents(&event);
4784 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4786 int joy = AnyJoystick();
4788 if (joy & JOY_BUTTON_1)
4790 else if (joy & JOY_BUTTON_2)
4793 else if (AnyJoystick())
4795 int joy = AnyJoystick();
4797 if (req_state & REQ_PLAYER)
4801 else if (joy & JOY_RIGHT)
4803 else if (joy & JOY_DOWN)
4805 else if (joy & JOY_LEFT)
4812 if (game_just_ended)
4814 if (global.use_envelope_request)
4816 // copy back current state of pressed buttons inside request area
4817 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4821 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4827 SetDrawtoField(draw_buffer_last);
4829 game.request_active = FALSE;
4834 static boolean RequestDoor(char *text, unsigned int req_state)
4836 int draw_buffer_last = GetDrawtoField();
4837 unsigned int old_door_state;
4838 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4839 int font_nr = FONT_TEXT_2;
4844 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4846 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4847 font_nr = FONT_TEXT_1;
4850 if (game_status == GAME_MODE_PLAYING)
4851 BlitScreenToBitmap(backbuffer);
4853 // disable deactivated drawing when quick-loading level tape recording
4854 if (tape.playing && tape.deactivate_display)
4855 TapeDeactivateDisplayOff(TRUE);
4857 SetMouseCursor(CURSOR_DEFAULT);
4859 // pause network game while waiting for request to answer
4860 if (network.enabled &&
4861 game_status == GAME_MODE_PLAYING &&
4862 !game.all_players_gone &&
4863 req_state & REQUEST_WAIT_FOR_INPUT)
4864 SendToServer_PausePlaying();
4866 old_door_state = GetDoorState();
4868 // simulate releasing mouse button over last gadget, if still pressed
4870 HandleGadgets(-1, -1, 0);
4874 // draw released gadget before proceeding
4877 if (old_door_state & DOOR_OPEN_1)
4879 CloseDoor(DOOR_CLOSE_1);
4881 // save old door content
4882 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4883 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4886 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4887 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4889 // clear door drawing field
4890 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4892 // force DOOR font inside door area
4893 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4895 // write text for request
4896 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4898 char text_line[max_request_line_len + 1];
4904 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4906 tc = *(text_ptr + tx);
4907 // if (!tc || tc == ' ')
4908 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4912 if ((tc == '?' || tc == '!') && tl == 0)
4922 strncpy(text_line, text_ptr, tl);
4925 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4926 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4927 text_line, font_nr);
4929 text_ptr += tl + (tc == ' ' ? 1 : 0);
4930 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4935 if (req_state & REQ_ASK)
4937 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4938 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4939 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4940 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4942 else if (req_state & REQ_CONFIRM)
4944 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4945 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4947 else if (req_state & REQ_PLAYER)
4949 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4950 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4951 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4952 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4955 // copy request gadgets to door backbuffer
4956 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4958 OpenDoor(DOOR_OPEN_1);
4960 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4962 if (game_status == GAME_MODE_PLAYING)
4964 SetPanelBackground();
4965 SetDrawBackgroundMask(REDRAW_DOOR_1);
4969 SetDrawBackgroundMask(REDRAW_FIELD);
4975 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4977 // ---------- handle request buttons ----------
4978 result = RequestHandleEvents(req_state, draw_buffer_last);
4982 if (!(req_state & REQ_STAY_OPEN))
4984 CloseDoor(DOOR_CLOSE_1);
4986 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4987 (req_state & REQ_REOPEN))
4988 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4993 if (game_status == GAME_MODE_PLAYING)
4995 SetPanelBackground();
4996 SetDrawBackgroundMask(REDRAW_DOOR_1);
5000 SetDrawBackgroundMask(REDRAW_FIELD);
5003 // continue network game after request
5004 if (network.enabled &&
5005 game_status == GAME_MODE_PLAYING &&
5006 !game.all_players_gone &&
5007 req_state & REQUEST_WAIT_FOR_INPUT)
5008 SendToServer_ContinuePlaying();
5010 // restore deactivated drawing when quick-loading level tape recording
5011 if (tape.playing && tape.deactivate_display)
5012 TapeDeactivateDisplayOn();
5017 static boolean RequestEnvelope(char *text, unsigned int req_state)
5019 int draw_buffer_last = GetDrawtoField();
5022 if (game_status == GAME_MODE_PLAYING)
5023 BlitScreenToBitmap(backbuffer);
5025 // disable deactivated drawing when quick-loading level tape recording
5026 if (tape.playing && tape.deactivate_display)
5027 TapeDeactivateDisplayOff(TRUE);
5029 SetMouseCursor(CURSOR_DEFAULT);
5031 // pause network game while waiting for request to answer
5032 if (network.enabled &&
5033 game_status == GAME_MODE_PLAYING &&
5034 !game.all_players_gone &&
5035 req_state & REQUEST_WAIT_FOR_INPUT)
5036 SendToServer_PausePlaying();
5038 // simulate releasing mouse button over last gadget, if still pressed
5040 HandleGadgets(-1, -1, 0);
5044 // (replace with setting corresponding request background)
5045 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5046 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5048 // clear door drawing field
5049 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5051 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5053 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5055 if (game_status == GAME_MODE_PLAYING)
5057 SetPanelBackground();
5058 SetDrawBackgroundMask(REDRAW_DOOR_1);
5062 SetDrawBackgroundMask(REDRAW_FIELD);
5068 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5070 // ---------- handle request buttons ----------
5071 result = RequestHandleEvents(req_state, draw_buffer_last);
5075 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5079 if (game_status == GAME_MODE_PLAYING)
5081 SetPanelBackground();
5082 SetDrawBackgroundMask(REDRAW_DOOR_1);
5086 SetDrawBackgroundMask(REDRAW_FIELD);
5089 // continue network game after request
5090 if (network.enabled &&
5091 game_status == GAME_MODE_PLAYING &&
5092 !game.all_players_gone &&
5093 req_state & REQUEST_WAIT_FOR_INPUT)
5094 SendToServer_ContinuePlaying();
5096 // restore deactivated drawing when quick-loading level tape recording
5097 if (tape.playing && tape.deactivate_display)
5098 TapeDeactivateDisplayOn();
5103 boolean Request(char *text, unsigned int req_state)
5105 boolean overlay_enabled = GetOverlayEnabled();
5108 game.request_active_or_moving = TRUE;
5110 SetOverlayEnabled(FALSE);
5112 if (global.use_envelope_request)
5113 result = RequestEnvelope(text, req_state);
5115 result = RequestDoor(text, req_state);
5117 SetOverlayEnabled(overlay_enabled);
5119 game.request_active_or_moving = FALSE;
5124 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5126 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5127 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5130 if (dpo1->sort_priority != dpo2->sort_priority)
5131 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5133 compare_result = dpo1->nr - dpo2->nr;
5135 return compare_result;
5138 void InitGraphicCompatibilityInfo_Doors(void)
5144 struct DoorInfo *door;
5148 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5149 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5151 { -1, -1, -1, NULL }
5153 struct Rect door_rect_list[] =
5155 { DX, DY, DXSIZE, DYSIZE },
5156 { VX, VY, VXSIZE, VYSIZE }
5160 for (i = 0; doors[i].door_token != -1; i++)
5162 int door_token = doors[i].door_token;
5163 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5164 int part_1 = doors[i].part_1;
5165 int part_8 = doors[i].part_8;
5166 int part_2 = part_1 + 1;
5167 int part_3 = part_1 + 2;
5168 struct DoorInfo *door = doors[i].door;
5169 struct Rect *door_rect = &door_rect_list[door_index];
5170 boolean door_gfx_redefined = FALSE;
5172 // check if any door part graphic definitions have been redefined
5174 for (j = 0; door_part_controls[j].door_token != -1; j++)
5176 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5177 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5179 if (dpc->door_token == door_token && fi->redefined)
5180 door_gfx_redefined = TRUE;
5183 // check for old-style door graphic/animation modifications
5185 if (!door_gfx_redefined)
5187 if (door->anim_mode & ANIM_STATIC_PANEL)
5189 door->panel.step_xoffset = 0;
5190 door->panel.step_yoffset = 0;
5193 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5195 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5196 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5197 int num_door_steps, num_panel_steps;
5199 // remove door part graphics other than the two default wings
5201 for (j = 0; door_part_controls[j].door_token != -1; j++)
5203 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5204 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5206 if (dpc->graphic >= part_3 &&
5207 dpc->graphic <= part_8)
5211 // set graphics and screen positions of the default wings
5213 g_part_1->width = door_rect->width;
5214 g_part_1->height = door_rect->height;
5215 g_part_2->width = door_rect->width;
5216 g_part_2->height = door_rect->height;
5217 g_part_2->src_x = door_rect->width;
5218 g_part_2->src_y = g_part_1->src_y;
5220 door->part_2.x = door->part_1.x;
5221 door->part_2.y = door->part_1.y;
5223 if (door->width != -1)
5225 g_part_1->width = door->width;
5226 g_part_2->width = door->width;
5228 // special treatment for graphics and screen position of right wing
5229 g_part_2->src_x += door_rect->width - door->width;
5230 door->part_2.x += door_rect->width - door->width;
5233 if (door->height != -1)
5235 g_part_1->height = door->height;
5236 g_part_2->height = door->height;
5238 // special treatment for graphics and screen position of bottom wing
5239 g_part_2->src_y += door_rect->height - door->height;
5240 door->part_2.y += door_rect->height - door->height;
5243 // set animation delays for the default wings and panels
5245 door->part_1.step_delay = door->step_delay;
5246 door->part_2.step_delay = door->step_delay;
5247 door->panel.step_delay = door->step_delay;
5249 // set animation draw order for the default wings
5251 door->part_1.sort_priority = 2; // draw left wing over ...
5252 door->part_2.sort_priority = 1; // ... right wing
5254 // set animation draw offset for the default wings
5256 if (door->anim_mode & ANIM_HORIZONTAL)
5258 door->part_1.step_xoffset = door->step_offset;
5259 door->part_1.step_yoffset = 0;
5260 door->part_2.step_xoffset = door->step_offset * -1;
5261 door->part_2.step_yoffset = 0;
5263 num_door_steps = g_part_1->width / door->step_offset;
5265 else // ANIM_VERTICAL
5267 door->part_1.step_xoffset = 0;
5268 door->part_1.step_yoffset = door->step_offset;
5269 door->part_2.step_xoffset = 0;
5270 door->part_2.step_yoffset = door->step_offset * -1;
5272 num_door_steps = g_part_1->height / door->step_offset;
5275 // set animation draw offset for the default panels
5277 if (door->step_offset > 1)
5279 num_panel_steps = 2 * door_rect->height / door->step_offset;
5280 door->panel.start_step = num_panel_steps - num_door_steps;
5281 door->panel.start_step_closing = door->panel.start_step;
5285 num_panel_steps = door_rect->height / door->step_offset;
5286 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5287 door->panel.start_step_closing = door->panel.start_step;
5288 door->panel.step_delay *= 2;
5295 void InitDoors(void)
5299 for (i = 0; door_part_controls[i].door_token != -1; i++)
5301 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5302 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5304 // initialize "start_step_opening" and "start_step_closing", if needed
5305 if (dpc->pos->start_step_opening == 0 &&
5306 dpc->pos->start_step_closing == 0)
5308 // dpc->pos->start_step_opening = dpc->pos->start_step;
5309 dpc->pos->start_step_closing = dpc->pos->start_step;
5312 // fill structure for door part draw order (sorted below)
5314 dpo->sort_priority = dpc->pos->sort_priority;
5317 // sort door part controls according to sort_priority and graphic number
5318 qsort(door_part_order, MAX_DOOR_PARTS,
5319 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5322 unsigned int OpenDoor(unsigned int door_state)
5324 if (door_state & DOOR_COPY_BACK)
5326 if (door_state & DOOR_OPEN_1)
5327 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5328 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5330 if (door_state & DOOR_OPEN_2)
5331 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5332 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5334 door_state &= ~DOOR_COPY_BACK;
5337 return MoveDoor(door_state);
5340 unsigned int CloseDoor(unsigned int door_state)
5342 unsigned int old_door_state = GetDoorState();
5344 if (!(door_state & DOOR_NO_COPY_BACK))
5346 if (old_door_state & DOOR_OPEN_1)
5347 BlitBitmap(backbuffer, bitmap_db_door_1,
5348 DX, DY, DXSIZE, DYSIZE, 0, 0);
5350 if (old_door_state & DOOR_OPEN_2)
5351 BlitBitmap(backbuffer, bitmap_db_door_2,
5352 VX, VY, VXSIZE, VYSIZE, 0, 0);
5354 door_state &= ~DOOR_NO_COPY_BACK;
5357 return MoveDoor(door_state);
5360 unsigned int GetDoorState(void)
5362 return MoveDoor(DOOR_GET_STATE);
5365 unsigned int SetDoorState(unsigned int door_state)
5367 return MoveDoor(door_state | DOOR_SET_STATE);
5370 static int euclid(int a, int b)
5372 return (b ? euclid(b, a % b) : a);
5375 unsigned int MoveDoor(unsigned int door_state)
5377 struct Rect door_rect_list[] =
5379 { DX, DY, DXSIZE, DYSIZE },
5380 { VX, VY, VXSIZE, VYSIZE }
5382 static int door1 = DOOR_CLOSE_1;
5383 static int door2 = DOOR_CLOSE_2;
5384 DelayCounter door_delay = { 0 };
5387 if (door_state == DOOR_GET_STATE)
5388 return (door1 | door2);
5390 if (door_state & DOOR_SET_STATE)
5392 if (door_state & DOOR_ACTION_1)
5393 door1 = door_state & DOOR_ACTION_1;
5394 if (door_state & DOOR_ACTION_2)
5395 door2 = door_state & DOOR_ACTION_2;
5397 return (door1 | door2);
5400 if (!(door_state & DOOR_FORCE_REDRAW))
5402 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5403 door_state &= ~DOOR_OPEN_1;
5404 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5405 door_state &= ~DOOR_CLOSE_1;
5406 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5407 door_state &= ~DOOR_OPEN_2;
5408 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5409 door_state &= ~DOOR_CLOSE_2;
5412 if (global.autoplay_leveldir)
5414 door_state |= DOOR_NO_DELAY;
5415 door_state &= ~DOOR_CLOSE_ALL;
5418 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5419 door_state |= DOOR_NO_DELAY;
5421 if (door_state & DOOR_ACTION)
5423 boolean door_panel_drawn[NUM_DOORS];
5424 boolean panel_has_doors[NUM_DOORS];
5425 boolean door_part_skip[MAX_DOOR_PARTS];
5426 boolean door_part_done[MAX_DOOR_PARTS];
5427 boolean door_part_done_all;
5428 int num_steps[MAX_DOOR_PARTS];
5429 int max_move_delay = 0; // delay for complete animations of all doors
5430 int max_step_delay = 0; // delay (ms) between two animation frames
5431 int num_move_steps = 0; // number of animation steps for all doors
5432 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5433 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5437 for (i = 0; i < NUM_DOORS; i++)
5438 panel_has_doors[i] = FALSE;
5440 for (i = 0; i < MAX_DOOR_PARTS; i++)
5442 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5443 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5444 int door_token = dpc->door_token;
5446 door_part_done[i] = FALSE;
5447 door_part_skip[i] = (!(door_state & door_token) ||
5451 for (i = 0; i < MAX_DOOR_PARTS; i++)
5453 int nr = door_part_order[i].nr;
5454 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5455 struct DoorPartPosInfo *pos = dpc->pos;
5456 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5457 int door_token = dpc->door_token;
5458 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5459 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5460 int step_xoffset = ABS(pos->step_xoffset);
5461 int step_yoffset = ABS(pos->step_yoffset);
5462 int step_delay = pos->step_delay;
5463 int current_door_state = door_state & door_token;
5464 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5465 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5466 boolean part_opening = (is_panel ? door_closing : door_opening);
5467 int start_step = (part_opening ? pos->start_step_opening :
5468 pos->start_step_closing);
5469 float move_xsize = (step_xoffset ? g->width : 0);
5470 float move_ysize = (step_yoffset ? g->height : 0);
5471 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5472 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5473 int move_steps = (move_xsteps && move_ysteps ?
5474 MIN(move_xsteps, move_ysteps) :
5475 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5476 int move_delay = move_steps * step_delay;
5478 if (door_part_skip[nr])
5481 max_move_delay = MAX(max_move_delay, move_delay);
5482 max_step_delay = (max_step_delay == 0 ? step_delay :
5483 euclid(max_step_delay, step_delay));
5484 num_steps[nr] = move_steps;
5488 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5490 panel_has_doors[door_index] = TRUE;
5494 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5496 num_move_steps = max_move_delay / max_step_delay;
5497 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5499 door_delay.value = max_step_delay;
5501 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5503 start = num_move_steps - 1;
5507 // opening door sound has priority over simultaneously closing door
5508 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5510 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5512 if (door_state & DOOR_OPEN_1)
5513 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5514 if (door_state & DOOR_OPEN_2)
5515 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5517 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5519 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5521 if (door_state & DOOR_CLOSE_1)
5522 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5523 if (door_state & DOOR_CLOSE_2)
5524 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5528 for (k = start; k < num_move_steps; k++)
5530 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5532 door_part_done_all = TRUE;
5534 for (i = 0; i < NUM_DOORS; i++)
5535 door_panel_drawn[i] = FALSE;
5537 for (i = 0; i < MAX_DOOR_PARTS; i++)
5539 int nr = door_part_order[i].nr;
5540 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5541 struct DoorPartPosInfo *pos = dpc->pos;
5542 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5543 int door_token = dpc->door_token;
5544 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5545 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5546 boolean is_panel_and_door_has_closed = FALSE;
5547 struct Rect *door_rect = &door_rect_list[door_index];
5548 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5550 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5551 int current_door_state = door_state & door_token;
5552 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5553 boolean door_closing = !door_opening;
5554 boolean part_opening = (is_panel ? door_closing : door_opening);
5555 boolean part_closing = !part_opening;
5556 int start_step = (part_opening ? pos->start_step_opening :
5557 pos->start_step_closing);
5558 int step_delay = pos->step_delay;
5559 int step_factor = step_delay / max_step_delay;
5560 int k1 = (step_factor ? k / step_factor + 1 : k);
5561 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5562 int kk = MAX(0, k2);
5565 int src_x, src_y, src_xx, src_yy;
5566 int dst_x, dst_y, dst_xx, dst_yy;
5569 if (door_part_skip[nr])
5572 if (!(door_state & door_token))
5580 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5581 int kk_door = MAX(0, k2_door);
5582 int sync_frame = kk_door * door_delay.value;
5583 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5585 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5586 &g_src_x, &g_src_y);
5591 if (!door_panel_drawn[door_index])
5593 ClearRectangle(drawto, door_rect->x, door_rect->y,
5594 door_rect->width, door_rect->height);
5596 door_panel_drawn[door_index] = TRUE;
5599 // draw opening or closing door parts
5601 if (pos->step_xoffset < 0) // door part on right side
5604 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5607 if (dst_xx + width > door_rect->width)
5608 width = door_rect->width - dst_xx;
5610 else // door part on left side
5613 dst_xx = pos->x - kk * pos->step_xoffset;
5617 src_xx = ABS(dst_xx);
5621 width = g->width - src_xx;
5623 if (width > door_rect->width)
5624 width = door_rect->width;
5626 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5629 if (pos->step_yoffset < 0) // door part on bottom side
5632 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5635 if (dst_yy + height > door_rect->height)
5636 height = door_rect->height - dst_yy;
5638 else // door part on top side
5641 dst_yy = pos->y - kk * pos->step_yoffset;
5645 src_yy = ABS(dst_yy);
5649 height = g->height - src_yy;
5652 src_x = g_src_x + src_xx;
5653 src_y = g_src_y + src_yy;
5655 dst_x = door_rect->x + dst_xx;
5656 dst_y = door_rect->y + dst_yy;
5658 is_panel_and_door_has_closed =
5661 panel_has_doors[door_index] &&
5662 k >= num_move_steps_doors_only - 1);
5664 if (width >= 0 && width <= g->width &&
5665 height >= 0 && height <= g->height &&
5666 !is_panel_and_door_has_closed)
5668 if (is_panel || !pos->draw_masked)
5669 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5672 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5676 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5678 if ((part_opening && (width < 0 || height < 0)) ||
5679 (part_closing && (width >= g->width && height >= g->height)))
5680 door_part_done[nr] = TRUE;
5682 // continue door part animations, but not panel after door has closed
5683 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5684 door_part_done_all = FALSE;
5687 if (!(door_state & DOOR_NO_DELAY))
5691 SkipUntilDelayReached(&door_delay, &k, last_frame);
5693 // prevent OS (Windows) from complaining about program not responding
5697 if (door_part_done_all)
5701 if (!(door_state & DOOR_NO_DELAY))
5703 // wait for specified door action post delay
5704 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5705 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5706 else if (door_state & DOOR_ACTION_1)
5707 door_delay.value = door_1.post_delay;
5708 else if (door_state & DOOR_ACTION_2)
5709 door_delay.value = door_2.post_delay;
5711 while (!DelayReached(&door_delay))
5716 if (door_state & DOOR_ACTION_1)
5717 door1 = door_state & DOOR_ACTION_1;
5718 if (door_state & DOOR_ACTION_2)
5719 door2 = door_state & DOOR_ACTION_2;
5721 // draw masked border over door area
5722 DrawMaskedBorder(REDRAW_DOOR_1);
5723 DrawMaskedBorder(REDRAW_DOOR_2);
5725 ClearAutoRepeatKeyEvents();
5727 return (door1 | door2);
5730 static boolean useSpecialEditorDoor(void)
5732 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5733 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5735 // do not draw special editor door if editor border defined or redefined
5736 if (graphic_info[graphic].bitmap != NULL || redefined)
5739 // do not draw special editor door if global border defined to be empty
5740 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5743 // do not draw special editor door if viewport definitions do not match
5747 EY + EYSIZE != VY + VYSIZE)
5753 void DrawSpecialEditorDoor(void)
5755 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5756 int top_border_width = gfx1->width;
5757 int top_border_height = gfx1->height;
5758 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5759 int ex = EX - outer_border;
5760 int ey = EY - outer_border;
5761 int vy = VY - outer_border;
5762 int exsize = EXSIZE + 2 * outer_border;
5764 if (!useSpecialEditorDoor())
5767 // draw bigger level editor toolbox window
5768 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5769 top_border_width, top_border_height, ex, ey - top_border_height);
5770 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5771 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5773 redraw_mask |= REDRAW_ALL;
5776 void UndrawSpecialEditorDoor(void)
5778 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5779 int top_border_width = gfx1->width;
5780 int top_border_height = gfx1->height;
5781 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5782 int ex = EX - outer_border;
5783 int ey = EY - outer_border;
5784 int ey_top = ey - top_border_height;
5785 int exsize = EXSIZE + 2 * outer_border;
5786 int eysize = EYSIZE + 2 * outer_border;
5788 if (!useSpecialEditorDoor())
5791 // draw normal tape recorder window
5792 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5794 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5795 ex, ey_top, top_border_width, top_border_height,
5797 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5798 ex, ey, exsize, eysize, ex, ey);
5802 // if screen background is set to "[NONE]", clear editor toolbox window
5803 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5804 ClearRectangle(drawto, ex, ey, exsize, eysize);
5807 redraw_mask |= REDRAW_ALL;
5811 // ---------- new tool button stuff -------------------------------------------
5816 struct TextPosInfo *pos;
5818 boolean is_touch_button;
5820 } toolbutton_info[NUM_TOOL_BUTTONS] =
5823 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5824 TOOL_CTRL_ID_YES, FALSE, "yes"
5827 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5828 TOOL_CTRL_ID_NO, FALSE, "no"
5831 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5832 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5835 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5836 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5839 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5840 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5843 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5844 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5847 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5848 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5851 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5852 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5855 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5856 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5859 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5860 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5864 void CreateToolButtons(void)
5868 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5870 int graphic = toolbutton_info[i].graphic;
5871 struct GraphicInfo *gfx = &graphic_info[graphic];
5872 struct TextPosInfo *pos = toolbutton_info[i].pos;
5873 struct GadgetInfo *gi;
5874 Bitmap *deco_bitmap = None;
5875 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5876 unsigned int event_mask = GD_EVENT_RELEASED;
5877 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5878 int base_x = (is_touch_button ? 0 : DX);
5879 int base_y = (is_touch_button ? 0 : DY);
5880 int gd_x = gfx->src_x;
5881 int gd_y = gfx->src_y;
5882 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5883 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5888 // do not use touch buttons if overlay touch buttons are disabled
5889 if (is_touch_button && !setup.touch.overlay_buttons)
5892 if (global.use_envelope_request && !is_touch_button)
5894 setRequestPosition(&base_x, &base_y, TRUE);
5896 // check if request buttons are outside of envelope and fix, if needed
5897 if (x < 0 || x + gfx->width > request.width ||
5898 y < 0 || y + gfx->height > request.height)
5900 if (id == TOOL_CTRL_ID_YES)
5903 y = request.height - 2 * request.border_size - gfx->height;
5905 else if (id == TOOL_CTRL_ID_NO)
5907 x = request.width - 2 * request.border_size - gfx->width;
5908 y = request.height - 2 * request.border_size - gfx->height;
5910 else if (id == TOOL_CTRL_ID_CONFIRM)
5912 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5913 y = request.height - 2 * request.border_size - gfx->height;
5915 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5917 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5919 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5920 y = request.height - 2 * request.border_size - gfx->height * 2;
5922 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5923 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5928 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5931 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5933 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5934 pos->size, &deco_bitmap, &deco_x, &deco_y);
5935 deco_xpos = (gfx->width - pos->size) / 2;
5936 deco_ypos = (gfx->height - pos->size) / 2;
5939 gi = CreateGadget(GDI_CUSTOM_ID, id,
5940 GDI_IMAGE_ID, graphic,
5941 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5944 GDI_WIDTH, gfx->width,
5945 GDI_HEIGHT, gfx->height,
5946 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5947 GDI_STATE, GD_BUTTON_UNPRESSED,
5948 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5949 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5950 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5951 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5952 GDI_DECORATION_SIZE, pos->size, pos->size,
5953 GDI_DECORATION_SHIFTING, 1, 1,
5954 GDI_DIRECT_DRAW, FALSE,
5955 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5956 GDI_EVENT_MASK, event_mask,
5957 GDI_CALLBACK_ACTION, HandleToolButtons,
5961 Fail("cannot create gadget");
5963 tool_gadget[id] = gi;
5967 void FreeToolButtons(void)
5971 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5972 FreeGadget(tool_gadget[i]);
5975 static void UnmapToolButtons(void)
5979 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5980 UnmapGadget(tool_gadget[i]);
5983 static void HandleToolButtons(struct GadgetInfo *gi)
5985 request_gadget_id = gi->custom_id;
5988 static struct Mapping_EM_to_RND_object
5991 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5992 boolean is_backside; // backside of moving element
5998 em_object_mapping_list[GAME_TILE_MAX + 1] =
6001 Zborder, FALSE, FALSE,
6005 Zplayer, FALSE, FALSE,
6014 Ztank, FALSE, FALSE,
6018 Zeater, FALSE, FALSE,
6022 Zdynamite, FALSE, FALSE,
6026 Zboom, FALSE, FALSE,
6031 Xchain, FALSE, FALSE,
6032 EL_DEFAULT, ACTION_EXPLODING, -1
6035 Xboom_bug, FALSE, FALSE,
6036 EL_BUG, ACTION_EXPLODING, -1
6039 Xboom_tank, FALSE, FALSE,
6040 EL_SPACESHIP, ACTION_EXPLODING, -1
6043 Xboom_android, FALSE, FALSE,
6044 EL_EMC_ANDROID, ACTION_OTHER, -1
6047 Xboom_1, FALSE, FALSE,
6048 EL_DEFAULT, ACTION_EXPLODING, -1
6051 Xboom_2, FALSE, FALSE,
6052 EL_DEFAULT, ACTION_EXPLODING, -1
6056 Xblank, TRUE, FALSE,
6061 Xsplash_e, FALSE, FALSE,
6062 EL_ACID_SPLASH_RIGHT, -1, -1
6065 Xsplash_w, FALSE, FALSE,
6066 EL_ACID_SPLASH_LEFT, -1, -1
6070 Xplant, TRUE, FALSE,
6071 EL_EMC_PLANT, -1, -1
6074 Yplant, FALSE, FALSE,
6075 EL_EMC_PLANT, -1, -1
6079 Xacid_1, TRUE, FALSE,
6083 Xacid_2, FALSE, FALSE,
6087 Xacid_3, FALSE, FALSE,
6091 Xacid_4, FALSE, FALSE,
6095 Xacid_5, FALSE, FALSE,
6099 Xacid_6, FALSE, FALSE,
6103 Xacid_7, FALSE, FALSE,
6107 Xacid_8, FALSE, FALSE,
6112 Xfake_acid_1, TRUE, FALSE,
6113 EL_EMC_FAKE_ACID, -1, -1
6116 Xfake_acid_2, FALSE, FALSE,
6117 EL_EMC_FAKE_ACID, -1, -1
6120 Xfake_acid_3, FALSE, FALSE,
6121 EL_EMC_FAKE_ACID, -1, -1
6124 Xfake_acid_4, FALSE, FALSE,
6125 EL_EMC_FAKE_ACID, -1, -1
6128 Xfake_acid_5, FALSE, FALSE,
6129 EL_EMC_FAKE_ACID, -1, -1
6132 Xfake_acid_6, FALSE, FALSE,
6133 EL_EMC_FAKE_ACID, -1, -1
6136 Xfake_acid_7, FALSE, FALSE,
6137 EL_EMC_FAKE_ACID, -1, -1
6140 Xfake_acid_8, FALSE, FALSE,
6141 EL_EMC_FAKE_ACID, -1, -1
6145 Xfake_acid_1_player, FALSE, FALSE,
6146 EL_EMC_FAKE_ACID, -1, -1
6149 Xfake_acid_2_player, FALSE, FALSE,
6150 EL_EMC_FAKE_ACID, -1, -1
6153 Xfake_acid_3_player, FALSE, FALSE,
6154 EL_EMC_FAKE_ACID, -1, -1
6157 Xfake_acid_4_player, FALSE, FALSE,
6158 EL_EMC_FAKE_ACID, -1, -1
6161 Xfake_acid_5_player, FALSE, FALSE,
6162 EL_EMC_FAKE_ACID, -1, -1
6165 Xfake_acid_6_player, FALSE, FALSE,
6166 EL_EMC_FAKE_ACID, -1, -1
6169 Xfake_acid_7_player, FALSE, FALSE,
6170 EL_EMC_FAKE_ACID, -1, -1
6173 Xfake_acid_8_player, FALSE, FALSE,
6174 EL_EMC_FAKE_ACID, -1, -1
6178 Xgrass, TRUE, FALSE,
6179 EL_EMC_GRASS, -1, -1
6182 Ygrass_nB, FALSE, FALSE,
6183 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6186 Ygrass_eB, FALSE, FALSE,
6187 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6190 Ygrass_sB, FALSE, FALSE,
6191 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6194 Ygrass_wB, FALSE, FALSE,
6195 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6203 Ydirt_nB, FALSE, FALSE,
6204 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6207 Ydirt_eB, FALSE, FALSE,
6208 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6211 Ydirt_sB, FALSE, FALSE,
6212 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6215 Ydirt_wB, FALSE, FALSE,
6216 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6220 Xandroid, TRUE, FALSE,
6221 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6224 Xandroid_1_n, FALSE, FALSE,
6225 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6228 Xandroid_2_n, FALSE, FALSE,
6229 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6232 Xandroid_1_e, FALSE, FALSE,
6233 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6236 Xandroid_2_e, FALSE, FALSE,
6237 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6240 Xandroid_1_w, FALSE, FALSE,
6241 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6244 Xandroid_2_w, FALSE, FALSE,
6245 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6248 Xandroid_1_s, FALSE, FALSE,
6249 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6252 Xandroid_2_s, FALSE, FALSE,
6253 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6256 Yandroid_n, FALSE, FALSE,
6257 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6260 Yandroid_nB, FALSE, TRUE,
6261 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6264 Yandroid_ne, FALSE, FALSE,
6265 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6268 Yandroid_neB, FALSE, TRUE,
6269 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6272 Yandroid_e, FALSE, FALSE,
6273 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6276 Yandroid_eB, FALSE, TRUE,
6277 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6280 Yandroid_se, FALSE, FALSE,
6281 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6284 Yandroid_seB, FALSE, TRUE,
6285 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6288 Yandroid_s, FALSE, FALSE,
6289 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6292 Yandroid_sB, FALSE, TRUE,
6293 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6296 Yandroid_sw, FALSE, FALSE,
6297 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6300 Yandroid_swB, FALSE, TRUE,
6301 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6304 Yandroid_w, FALSE, FALSE,
6305 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6308 Yandroid_wB, FALSE, TRUE,
6309 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6312 Yandroid_nw, FALSE, FALSE,
6313 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6316 Yandroid_nwB, FALSE, TRUE,
6317 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6321 Xeater_n, TRUE, FALSE,
6322 EL_YAMYAM_UP, -1, -1
6325 Xeater_e, TRUE, FALSE,
6326 EL_YAMYAM_RIGHT, -1, -1
6329 Xeater_w, TRUE, FALSE,
6330 EL_YAMYAM_LEFT, -1, -1
6333 Xeater_s, TRUE, FALSE,
6334 EL_YAMYAM_DOWN, -1, -1
6337 Yeater_n, FALSE, FALSE,
6338 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6341 Yeater_nB, FALSE, TRUE,
6342 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6345 Yeater_e, FALSE, FALSE,
6346 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6349 Yeater_eB, FALSE, TRUE,
6350 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6353 Yeater_s, FALSE, FALSE,
6354 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6357 Yeater_sB, FALSE, TRUE,
6358 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6361 Yeater_w, FALSE, FALSE,
6362 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6365 Yeater_wB, FALSE, TRUE,
6366 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6369 Yeater_stone, FALSE, FALSE,
6370 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6373 Yeater_spring, FALSE, FALSE,
6374 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6378 Xalien, TRUE, FALSE,
6382 Xalien_pause, FALSE, FALSE,
6386 Yalien_n, FALSE, FALSE,
6387 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6390 Yalien_nB, FALSE, TRUE,
6391 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6394 Yalien_e, FALSE, FALSE,
6395 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6398 Yalien_eB, FALSE, TRUE,
6399 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6402 Yalien_s, FALSE, FALSE,
6403 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6406 Yalien_sB, FALSE, TRUE,
6407 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6410 Yalien_w, FALSE, FALSE,
6411 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6414 Yalien_wB, FALSE, TRUE,
6415 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6418 Yalien_stone, FALSE, FALSE,
6419 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6422 Yalien_spring, FALSE, FALSE,
6423 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6427 Xbug_1_n, TRUE, FALSE,
6431 Xbug_1_e, TRUE, FALSE,
6432 EL_BUG_RIGHT, -1, -1
6435 Xbug_1_s, TRUE, FALSE,
6439 Xbug_1_w, TRUE, FALSE,
6443 Xbug_2_n, FALSE, FALSE,
6447 Xbug_2_e, FALSE, FALSE,
6448 EL_BUG_RIGHT, -1, -1
6451 Xbug_2_s, FALSE, FALSE,
6455 Xbug_2_w, FALSE, FALSE,
6459 Ybug_n, FALSE, FALSE,
6460 EL_BUG, ACTION_MOVING, MV_BIT_UP
6463 Ybug_nB, FALSE, TRUE,
6464 EL_BUG, ACTION_MOVING, MV_BIT_UP
6467 Ybug_e, FALSE, FALSE,
6468 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6471 Ybug_eB, FALSE, TRUE,
6472 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6475 Ybug_s, FALSE, FALSE,
6476 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6479 Ybug_sB, FALSE, TRUE,
6480 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6483 Ybug_w, FALSE, FALSE,
6484 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6487 Ybug_wB, FALSE, TRUE,
6488 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6491 Ybug_w_n, FALSE, FALSE,
6492 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6495 Ybug_n_e, FALSE, FALSE,
6496 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6499 Ybug_e_s, FALSE, FALSE,
6500 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6503 Ybug_s_w, FALSE, FALSE,
6504 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6507 Ybug_e_n, FALSE, FALSE,
6508 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6511 Ybug_s_e, FALSE, FALSE,
6512 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6515 Ybug_w_s, FALSE, FALSE,
6516 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6519 Ybug_n_w, FALSE, FALSE,
6520 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6523 Ybug_stone, FALSE, FALSE,
6524 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6527 Ybug_spring, FALSE, FALSE,
6528 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6532 Xtank_1_n, TRUE, FALSE,
6533 EL_SPACESHIP_UP, -1, -1
6536 Xtank_1_e, TRUE, FALSE,
6537 EL_SPACESHIP_RIGHT, -1, -1
6540 Xtank_1_s, TRUE, FALSE,
6541 EL_SPACESHIP_DOWN, -1, -1
6544 Xtank_1_w, TRUE, FALSE,
6545 EL_SPACESHIP_LEFT, -1, -1
6548 Xtank_2_n, FALSE, FALSE,
6549 EL_SPACESHIP_UP, -1, -1
6552 Xtank_2_e, FALSE, FALSE,
6553 EL_SPACESHIP_RIGHT, -1, -1
6556 Xtank_2_s, FALSE, FALSE,
6557 EL_SPACESHIP_DOWN, -1, -1
6560 Xtank_2_w, FALSE, FALSE,
6561 EL_SPACESHIP_LEFT, -1, -1
6564 Ytank_n, FALSE, FALSE,
6565 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6568 Ytank_nB, FALSE, TRUE,
6569 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6572 Ytank_e, FALSE, FALSE,
6573 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6576 Ytank_eB, FALSE, TRUE,
6577 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6580 Ytank_s, FALSE, FALSE,
6581 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6584 Ytank_sB, FALSE, TRUE,
6585 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6588 Ytank_w, FALSE, FALSE,
6589 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6592 Ytank_wB, FALSE, TRUE,
6593 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6596 Ytank_w_n, FALSE, FALSE,
6597 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6600 Ytank_n_e, FALSE, FALSE,
6601 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6604 Ytank_e_s, FALSE, FALSE,
6605 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6608 Ytank_s_w, FALSE, FALSE,
6609 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6612 Ytank_e_n, FALSE, FALSE,
6613 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6616 Ytank_s_e, FALSE, FALSE,
6617 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6620 Ytank_w_s, FALSE, FALSE,
6621 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6624 Ytank_n_w, FALSE, FALSE,
6625 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6628 Ytank_stone, FALSE, FALSE,
6629 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6632 Ytank_spring, FALSE, FALSE,
6633 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6637 Xemerald, TRUE, FALSE,
6641 Xemerald_pause, FALSE, FALSE,
6645 Xemerald_fall, FALSE, FALSE,
6649 Xemerald_shine, FALSE, FALSE,
6650 EL_EMERALD, ACTION_TWINKLING, -1
6653 Yemerald_s, FALSE, FALSE,
6654 EL_EMERALD, ACTION_FALLING, -1
6657 Yemerald_sB, FALSE, TRUE,
6658 EL_EMERALD, ACTION_FALLING, -1
6661 Yemerald_e, FALSE, FALSE,
6662 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6665 Yemerald_eB, FALSE, TRUE,
6666 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6669 Yemerald_w, FALSE, FALSE,
6670 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6673 Yemerald_wB, FALSE, TRUE,
6674 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6677 Yemerald_blank, FALSE, FALSE,
6678 EL_EMERALD, ACTION_COLLECTING, -1
6682 Xdiamond, TRUE, FALSE,
6686 Xdiamond_pause, FALSE, FALSE,
6690 Xdiamond_fall, FALSE, FALSE,
6694 Xdiamond_shine, FALSE, FALSE,
6695 EL_DIAMOND, ACTION_TWINKLING, -1
6698 Ydiamond_s, FALSE, FALSE,
6699 EL_DIAMOND, ACTION_FALLING, -1
6702 Ydiamond_sB, FALSE, TRUE,
6703 EL_DIAMOND, ACTION_FALLING, -1
6706 Ydiamond_e, FALSE, FALSE,
6707 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6710 Ydiamond_eB, FALSE, TRUE,
6711 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6714 Ydiamond_w, FALSE, FALSE,
6715 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6718 Ydiamond_wB, FALSE, TRUE,
6719 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6722 Ydiamond_blank, FALSE, FALSE,
6723 EL_DIAMOND, ACTION_COLLECTING, -1
6726 Ydiamond_stone, FALSE, FALSE,
6727 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6731 Xstone, TRUE, FALSE,
6735 Xstone_pause, FALSE, FALSE,
6739 Xstone_fall, FALSE, FALSE,
6743 Ystone_s, FALSE, FALSE,
6744 EL_ROCK, ACTION_FALLING, -1
6747 Ystone_sB, FALSE, TRUE,
6748 EL_ROCK, ACTION_FALLING, -1
6751 Ystone_e, FALSE, FALSE,
6752 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6755 Ystone_eB, FALSE, TRUE,
6756 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6759 Ystone_w, FALSE, FALSE,
6760 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6763 Ystone_wB, FALSE, TRUE,
6764 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6772 Xbomb_pause, FALSE, FALSE,
6776 Xbomb_fall, FALSE, FALSE,
6780 Ybomb_s, FALSE, FALSE,
6781 EL_BOMB, ACTION_FALLING, -1
6784 Ybomb_sB, FALSE, TRUE,
6785 EL_BOMB, ACTION_FALLING, -1
6788 Ybomb_e, FALSE, FALSE,
6789 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6792 Ybomb_eB, FALSE, TRUE,
6793 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6796 Ybomb_w, FALSE, FALSE,
6797 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6800 Ybomb_wB, FALSE, TRUE,
6801 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6804 Ybomb_blank, FALSE, FALSE,
6805 EL_BOMB, ACTION_ACTIVATING, -1
6813 Xnut_pause, FALSE, FALSE,
6817 Xnut_fall, FALSE, FALSE,
6821 Ynut_s, FALSE, FALSE,
6822 EL_NUT, ACTION_FALLING, -1
6825 Ynut_sB, FALSE, TRUE,
6826 EL_NUT, ACTION_FALLING, -1
6829 Ynut_e, FALSE, FALSE,
6830 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6833 Ynut_eB, FALSE, TRUE,
6834 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6837 Ynut_w, FALSE, FALSE,
6838 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6841 Ynut_wB, FALSE, TRUE,
6842 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6845 Ynut_stone, FALSE, FALSE,
6846 EL_NUT, ACTION_BREAKING, -1
6850 Xspring, TRUE, FALSE,
6854 Xspring_pause, FALSE, FALSE,
6858 Xspring_e, TRUE, FALSE,
6859 EL_SPRING_RIGHT, -1, -1
6862 Xspring_w, TRUE, FALSE,
6863 EL_SPRING_LEFT, -1, -1
6866 Xspring_fall, FALSE, FALSE,
6870 Yspring_s, FALSE, FALSE,
6871 EL_SPRING, ACTION_FALLING, -1
6874 Yspring_sB, FALSE, TRUE,
6875 EL_SPRING, ACTION_FALLING, -1
6878 Yspring_e, FALSE, FALSE,
6879 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6882 Yspring_eB, FALSE, TRUE,
6883 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6886 Yspring_w, FALSE, FALSE,
6887 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6890 Yspring_wB, FALSE, TRUE,
6891 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6894 Yspring_alien_e, FALSE, FALSE,
6895 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6898 Yspring_alien_eB, FALSE, TRUE,
6899 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6902 Yspring_alien_w, FALSE, FALSE,
6903 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6906 Yspring_alien_wB, FALSE, TRUE,
6907 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6911 Xpush_emerald_e, FALSE, FALSE,
6912 EL_EMERALD, -1, MV_BIT_RIGHT
6915 Xpush_emerald_w, FALSE, FALSE,
6916 EL_EMERALD, -1, MV_BIT_LEFT
6919 Xpush_diamond_e, FALSE, FALSE,
6920 EL_DIAMOND, -1, MV_BIT_RIGHT
6923 Xpush_diamond_w, FALSE, FALSE,
6924 EL_DIAMOND, -1, MV_BIT_LEFT
6927 Xpush_stone_e, FALSE, FALSE,
6928 EL_ROCK, -1, MV_BIT_RIGHT
6931 Xpush_stone_w, FALSE, FALSE,
6932 EL_ROCK, -1, MV_BIT_LEFT
6935 Xpush_bomb_e, FALSE, FALSE,
6936 EL_BOMB, -1, MV_BIT_RIGHT
6939 Xpush_bomb_w, FALSE, FALSE,
6940 EL_BOMB, -1, MV_BIT_LEFT
6943 Xpush_nut_e, FALSE, FALSE,
6944 EL_NUT, -1, MV_BIT_RIGHT
6947 Xpush_nut_w, FALSE, FALSE,
6948 EL_NUT, -1, MV_BIT_LEFT
6951 Xpush_spring_e, FALSE, FALSE,
6952 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6955 Xpush_spring_w, FALSE, FALSE,
6956 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6960 Xdynamite, TRUE, FALSE,
6961 EL_EM_DYNAMITE, -1, -1
6964 Ydynamite_blank, FALSE, FALSE,
6965 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6968 Xdynamite_1, TRUE, FALSE,
6969 EL_EM_DYNAMITE_ACTIVE, -1, -1
6972 Xdynamite_2, FALSE, FALSE,
6973 EL_EM_DYNAMITE_ACTIVE, -1, -1
6976 Xdynamite_3, FALSE, FALSE,
6977 EL_EM_DYNAMITE_ACTIVE, -1, -1
6980 Xdynamite_4, FALSE, FALSE,
6981 EL_EM_DYNAMITE_ACTIVE, -1, -1
6985 Xkey_1, TRUE, FALSE,
6989 Xkey_2, TRUE, FALSE,
6993 Xkey_3, TRUE, FALSE,
6997 Xkey_4, TRUE, FALSE,
7001 Xkey_5, TRUE, FALSE,
7002 EL_EMC_KEY_5, -1, -1
7005 Xkey_6, TRUE, FALSE,
7006 EL_EMC_KEY_6, -1, -1
7009 Xkey_7, TRUE, FALSE,
7010 EL_EMC_KEY_7, -1, -1
7013 Xkey_8, TRUE, FALSE,
7014 EL_EMC_KEY_8, -1, -1
7018 Xdoor_1, TRUE, FALSE,
7019 EL_EM_GATE_1, -1, -1
7022 Xdoor_2, TRUE, FALSE,
7023 EL_EM_GATE_2, -1, -1
7026 Xdoor_3, TRUE, FALSE,
7027 EL_EM_GATE_3, -1, -1
7030 Xdoor_4, TRUE, FALSE,
7031 EL_EM_GATE_4, -1, -1
7034 Xdoor_5, TRUE, FALSE,
7035 EL_EMC_GATE_5, -1, -1
7038 Xdoor_6, TRUE, FALSE,
7039 EL_EMC_GATE_6, -1, -1
7042 Xdoor_7, TRUE, FALSE,
7043 EL_EMC_GATE_7, -1, -1
7046 Xdoor_8, TRUE, FALSE,
7047 EL_EMC_GATE_8, -1, -1
7051 Xfake_door_1, TRUE, FALSE,
7052 EL_EM_GATE_1_GRAY, -1, -1
7055 Xfake_door_2, TRUE, FALSE,
7056 EL_EM_GATE_2_GRAY, -1, -1
7059 Xfake_door_3, TRUE, FALSE,
7060 EL_EM_GATE_3_GRAY, -1, -1
7063 Xfake_door_4, TRUE, FALSE,
7064 EL_EM_GATE_4_GRAY, -1, -1
7067 Xfake_door_5, TRUE, FALSE,
7068 EL_EMC_GATE_5_GRAY, -1, -1
7071 Xfake_door_6, TRUE, FALSE,
7072 EL_EMC_GATE_6_GRAY, -1, -1
7075 Xfake_door_7, TRUE, FALSE,
7076 EL_EMC_GATE_7_GRAY, -1, -1
7079 Xfake_door_8, TRUE, FALSE,
7080 EL_EMC_GATE_8_GRAY, -1, -1
7084 Xballoon, TRUE, FALSE,
7088 Yballoon_n, FALSE, FALSE,
7089 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7092 Yballoon_nB, FALSE, TRUE,
7093 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7096 Yballoon_e, FALSE, FALSE,
7097 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7100 Yballoon_eB, FALSE, TRUE,
7101 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7104 Yballoon_s, FALSE, FALSE,
7105 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7108 Yballoon_sB, FALSE, TRUE,
7109 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7112 Yballoon_w, FALSE, FALSE,
7113 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7116 Yballoon_wB, FALSE, TRUE,
7117 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7121 Xball_1, TRUE, FALSE,
7122 EL_EMC_MAGIC_BALL, -1, -1
7125 Yball_1, FALSE, FALSE,
7126 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7129 Xball_2, FALSE, FALSE,
7130 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7133 Yball_2, FALSE, FALSE,
7134 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7137 Yball_blank, FALSE, FALSE,
7138 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7142 Xamoeba_1, TRUE, FALSE,
7143 EL_AMOEBA_DRY, ACTION_OTHER, -1
7146 Xamoeba_2, FALSE, FALSE,
7147 EL_AMOEBA_DRY, ACTION_OTHER, -1
7150 Xamoeba_3, FALSE, FALSE,
7151 EL_AMOEBA_DRY, ACTION_OTHER, -1
7154 Xamoeba_4, FALSE, FALSE,
7155 EL_AMOEBA_DRY, ACTION_OTHER, -1
7158 Xamoeba_5, TRUE, FALSE,
7159 EL_AMOEBA_WET, ACTION_OTHER, -1
7162 Xamoeba_6, FALSE, FALSE,
7163 EL_AMOEBA_WET, ACTION_OTHER, -1
7166 Xamoeba_7, FALSE, FALSE,
7167 EL_AMOEBA_WET, ACTION_OTHER, -1
7170 Xamoeba_8, FALSE, FALSE,
7171 EL_AMOEBA_WET, ACTION_OTHER, -1
7176 EL_AMOEBA_DROP, ACTION_GROWING, -1
7179 Xdrip_fall, FALSE, FALSE,
7180 EL_AMOEBA_DROP, -1, -1
7183 Xdrip_stretch, FALSE, FALSE,
7184 EL_AMOEBA_DROP, ACTION_FALLING, -1
7187 Xdrip_stretchB, FALSE, TRUE,
7188 EL_AMOEBA_DROP, ACTION_FALLING, -1
7191 Ydrip_1_s, FALSE, FALSE,
7192 EL_AMOEBA_DROP, ACTION_FALLING, -1
7195 Ydrip_1_sB, FALSE, TRUE,
7196 EL_AMOEBA_DROP, ACTION_FALLING, -1
7199 Ydrip_2_s, FALSE, FALSE,
7200 EL_AMOEBA_DROP, ACTION_FALLING, -1
7203 Ydrip_2_sB, FALSE, TRUE,
7204 EL_AMOEBA_DROP, ACTION_FALLING, -1
7208 Xwonderwall, TRUE, FALSE,
7209 EL_MAGIC_WALL, -1, -1
7212 Ywonderwall, FALSE, FALSE,
7213 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7217 Xwheel, TRUE, FALSE,
7218 EL_ROBOT_WHEEL, -1, -1
7221 Ywheel, FALSE, FALSE,
7222 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7226 Xswitch, TRUE, FALSE,
7227 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7230 Yswitch, FALSE, FALSE,
7231 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7235 Xbumper, TRUE, FALSE,
7236 EL_EMC_SPRING_BUMPER, -1, -1
7239 Ybumper, FALSE, FALSE,
7240 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7244 Xacid_nw, TRUE, FALSE,
7245 EL_ACID_POOL_TOPLEFT, -1, -1
7248 Xacid_ne, TRUE, FALSE,
7249 EL_ACID_POOL_TOPRIGHT, -1, -1
7252 Xacid_sw, TRUE, FALSE,
7253 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7256 Xacid_s, TRUE, FALSE,
7257 EL_ACID_POOL_BOTTOM, -1, -1
7260 Xacid_se, TRUE, FALSE,
7261 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7265 Xfake_blank, TRUE, FALSE,
7266 EL_INVISIBLE_WALL, -1, -1
7269 Yfake_blank, FALSE, FALSE,
7270 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7274 Xfake_grass, TRUE, FALSE,
7275 EL_EMC_FAKE_GRASS, -1, -1
7278 Yfake_grass, FALSE, FALSE,
7279 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7283 Xfake_amoeba, TRUE, FALSE,
7284 EL_EMC_DRIPPER, -1, -1
7287 Yfake_amoeba, FALSE, FALSE,
7288 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7292 Xlenses, TRUE, FALSE,
7293 EL_EMC_LENSES, -1, -1
7297 Xmagnify, TRUE, FALSE,
7298 EL_EMC_MAGNIFIER, -1, -1
7303 EL_QUICKSAND_EMPTY, -1, -1
7306 Xsand_stone, TRUE, FALSE,
7307 EL_QUICKSAND_FULL, -1, -1
7310 Xsand_stonein_1, FALSE, TRUE,
7311 EL_ROCK, ACTION_FILLING, -1
7314 Xsand_stonein_2, FALSE, TRUE,
7315 EL_ROCK, ACTION_FILLING, -1
7318 Xsand_stonein_3, FALSE, TRUE,
7319 EL_ROCK, ACTION_FILLING, -1
7322 Xsand_stonein_4, FALSE, TRUE,
7323 EL_ROCK, ACTION_FILLING, -1
7326 Xsand_sandstone_1, FALSE, FALSE,
7327 EL_QUICKSAND_FILLING, -1, -1
7330 Xsand_sandstone_2, FALSE, FALSE,
7331 EL_QUICKSAND_FILLING, -1, -1
7334 Xsand_sandstone_3, FALSE, FALSE,
7335 EL_QUICKSAND_FILLING, -1, -1
7338 Xsand_sandstone_4, FALSE, FALSE,
7339 EL_QUICKSAND_FILLING, -1, -1
7342 Xsand_stonesand_1, FALSE, FALSE,
7343 EL_QUICKSAND_EMPTYING, -1, -1
7346 Xsand_stonesand_2, FALSE, FALSE,
7347 EL_QUICKSAND_EMPTYING, -1, -1
7350 Xsand_stonesand_3, FALSE, FALSE,
7351 EL_QUICKSAND_EMPTYING, -1, -1
7354 Xsand_stonesand_4, FALSE, FALSE,
7355 EL_QUICKSAND_EMPTYING, -1, -1
7358 Xsand_stoneout_1, FALSE, FALSE,
7359 EL_ROCK, ACTION_EMPTYING, -1
7362 Xsand_stoneout_2, FALSE, FALSE,
7363 EL_ROCK, ACTION_EMPTYING, -1
7366 Xsand_stonesand_quickout_1, FALSE, FALSE,
7367 EL_QUICKSAND_EMPTYING, -1, -1
7370 Xsand_stonesand_quickout_2, FALSE, FALSE,
7371 EL_QUICKSAND_EMPTYING, -1, -1
7375 Xslide_ns, TRUE, FALSE,
7376 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7379 Yslide_ns_blank, FALSE, FALSE,
7380 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7383 Xslide_ew, TRUE, FALSE,
7384 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7387 Yslide_ew_blank, FALSE, FALSE,
7388 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7392 Xwind_n, TRUE, FALSE,
7393 EL_BALLOON_SWITCH_UP, -1, -1
7396 Xwind_e, TRUE, FALSE,
7397 EL_BALLOON_SWITCH_RIGHT, -1, -1
7400 Xwind_s, TRUE, FALSE,
7401 EL_BALLOON_SWITCH_DOWN, -1, -1
7404 Xwind_w, TRUE, FALSE,
7405 EL_BALLOON_SWITCH_LEFT, -1, -1
7408 Xwind_any, TRUE, FALSE,
7409 EL_BALLOON_SWITCH_ANY, -1, -1
7412 Xwind_stop, TRUE, FALSE,
7413 EL_BALLOON_SWITCH_NONE, -1, -1
7418 EL_EM_EXIT_CLOSED, -1, -1
7421 Xexit_1, TRUE, FALSE,
7422 EL_EM_EXIT_OPEN, -1, -1
7425 Xexit_2, FALSE, FALSE,
7426 EL_EM_EXIT_OPEN, -1, -1
7429 Xexit_3, FALSE, FALSE,
7430 EL_EM_EXIT_OPEN, -1, -1
7434 Xpause, FALSE, FALSE,
7439 Xwall_1, TRUE, FALSE,
7443 Xwall_2, TRUE, FALSE,
7444 EL_EMC_WALL_14, -1, -1
7447 Xwall_3, TRUE, FALSE,
7448 EL_EMC_WALL_15, -1, -1
7451 Xwall_4, TRUE, FALSE,
7452 EL_EMC_WALL_16, -1, -1
7456 Xroundwall_1, TRUE, FALSE,
7457 EL_WALL_SLIPPERY, -1, -1
7460 Xroundwall_2, TRUE, FALSE,
7461 EL_EMC_WALL_SLIPPERY_2, -1, -1
7464 Xroundwall_3, TRUE, FALSE,
7465 EL_EMC_WALL_SLIPPERY_3, -1, -1
7468 Xroundwall_4, TRUE, FALSE,
7469 EL_EMC_WALL_SLIPPERY_4, -1, -1
7473 Xsteel_1, TRUE, FALSE,
7474 EL_STEELWALL, -1, -1
7477 Xsteel_2, TRUE, FALSE,
7478 EL_EMC_STEELWALL_2, -1, -1
7481 Xsteel_3, TRUE, FALSE,
7482 EL_EMC_STEELWALL_3, -1, -1
7485 Xsteel_4, TRUE, FALSE,
7486 EL_EMC_STEELWALL_4, -1, -1
7490 Xdecor_1, TRUE, FALSE,
7491 EL_EMC_WALL_8, -1, -1
7494 Xdecor_2, TRUE, FALSE,
7495 EL_EMC_WALL_6, -1, -1
7498 Xdecor_3, TRUE, FALSE,
7499 EL_EMC_WALL_4, -1, -1
7502 Xdecor_4, TRUE, FALSE,
7503 EL_EMC_WALL_7, -1, -1
7506 Xdecor_5, TRUE, FALSE,
7507 EL_EMC_WALL_5, -1, -1
7510 Xdecor_6, TRUE, FALSE,
7511 EL_EMC_WALL_9, -1, -1
7514 Xdecor_7, TRUE, FALSE,
7515 EL_EMC_WALL_10, -1, -1
7518 Xdecor_8, TRUE, FALSE,
7519 EL_EMC_WALL_1, -1, -1
7522 Xdecor_9, TRUE, FALSE,
7523 EL_EMC_WALL_2, -1, -1
7526 Xdecor_10, TRUE, FALSE,
7527 EL_EMC_WALL_3, -1, -1
7530 Xdecor_11, TRUE, FALSE,
7531 EL_EMC_WALL_11, -1, -1
7534 Xdecor_12, TRUE, FALSE,
7535 EL_EMC_WALL_12, -1, -1
7539 Xalpha_0, TRUE, FALSE,
7540 EL_CHAR('0'), -1, -1
7543 Xalpha_1, TRUE, FALSE,
7544 EL_CHAR('1'), -1, -1
7547 Xalpha_2, TRUE, FALSE,
7548 EL_CHAR('2'), -1, -1
7551 Xalpha_3, TRUE, FALSE,
7552 EL_CHAR('3'), -1, -1
7555 Xalpha_4, TRUE, FALSE,
7556 EL_CHAR('4'), -1, -1
7559 Xalpha_5, TRUE, FALSE,
7560 EL_CHAR('5'), -1, -1
7563 Xalpha_6, TRUE, FALSE,
7564 EL_CHAR('6'), -1, -1
7567 Xalpha_7, TRUE, FALSE,
7568 EL_CHAR('7'), -1, -1
7571 Xalpha_8, TRUE, FALSE,
7572 EL_CHAR('8'), -1, -1
7575 Xalpha_9, TRUE, FALSE,
7576 EL_CHAR('9'), -1, -1
7579 Xalpha_excla, TRUE, FALSE,
7580 EL_CHAR('!'), -1, -1
7583 Xalpha_apost, TRUE, FALSE,
7584 EL_CHAR('\''), -1, -1
7587 Xalpha_comma, TRUE, FALSE,
7588 EL_CHAR(','), -1, -1
7591 Xalpha_minus, TRUE, FALSE,
7592 EL_CHAR('-'), -1, -1
7595 Xalpha_perio, TRUE, FALSE,
7596 EL_CHAR('.'), -1, -1
7599 Xalpha_colon, TRUE, FALSE,
7600 EL_CHAR(':'), -1, -1
7603 Xalpha_quest, TRUE, FALSE,
7604 EL_CHAR('?'), -1, -1
7607 Xalpha_a, TRUE, FALSE,
7608 EL_CHAR('A'), -1, -1
7611 Xalpha_b, TRUE, FALSE,
7612 EL_CHAR('B'), -1, -1
7615 Xalpha_c, TRUE, FALSE,
7616 EL_CHAR('C'), -1, -1
7619 Xalpha_d, TRUE, FALSE,
7620 EL_CHAR('D'), -1, -1
7623 Xalpha_e, TRUE, FALSE,
7624 EL_CHAR('E'), -1, -1
7627 Xalpha_f, TRUE, FALSE,
7628 EL_CHAR('F'), -1, -1
7631 Xalpha_g, TRUE, FALSE,
7632 EL_CHAR('G'), -1, -1
7635 Xalpha_h, TRUE, FALSE,
7636 EL_CHAR('H'), -1, -1
7639 Xalpha_i, TRUE, FALSE,
7640 EL_CHAR('I'), -1, -1
7643 Xalpha_j, TRUE, FALSE,
7644 EL_CHAR('J'), -1, -1
7647 Xalpha_k, TRUE, FALSE,
7648 EL_CHAR('K'), -1, -1
7651 Xalpha_l, TRUE, FALSE,
7652 EL_CHAR('L'), -1, -1
7655 Xalpha_m, TRUE, FALSE,
7656 EL_CHAR('M'), -1, -1
7659 Xalpha_n, TRUE, FALSE,
7660 EL_CHAR('N'), -1, -1
7663 Xalpha_o, TRUE, FALSE,
7664 EL_CHAR('O'), -1, -1
7667 Xalpha_p, TRUE, FALSE,
7668 EL_CHAR('P'), -1, -1
7671 Xalpha_q, TRUE, FALSE,
7672 EL_CHAR('Q'), -1, -1
7675 Xalpha_r, TRUE, FALSE,
7676 EL_CHAR('R'), -1, -1
7679 Xalpha_s, TRUE, FALSE,
7680 EL_CHAR('S'), -1, -1
7683 Xalpha_t, TRUE, FALSE,
7684 EL_CHAR('T'), -1, -1
7687 Xalpha_u, TRUE, FALSE,
7688 EL_CHAR('U'), -1, -1
7691 Xalpha_v, TRUE, FALSE,
7692 EL_CHAR('V'), -1, -1
7695 Xalpha_w, TRUE, FALSE,
7696 EL_CHAR('W'), -1, -1
7699 Xalpha_x, TRUE, FALSE,
7700 EL_CHAR('X'), -1, -1
7703 Xalpha_y, TRUE, FALSE,
7704 EL_CHAR('Y'), -1, -1
7707 Xalpha_z, TRUE, FALSE,
7708 EL_CHAR('Z'), -1, -1
7711 Xalpha_arrow_e, TRUE, FALSE,
7712 EL_CHAR('>'), -1, -1
7715 Xalpha_arrow_w, TRUE, FALSE,
7716 EL_CHAR('<'), -1, -1
7719 Xalpha_copyr, TRUE, FALSE,
7720 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7724 Ykey_1_blank, FALSE, FALSE,
7725 EL_EM_KEY_1, ACTION_COLLECTING, -1
7728 Ykey_2_blank, FALSE, FALSE,
7729 EL_EM_KEY_2, ACTION_COLLECTING, -1
7732 Ykey_3_blank, FALSE, FALSE,
7733 EL_EM_KEY_3, ACTION_COLLECTING, -1
7736 Ykey_4_blank, FALSE, FALSE,
7737 EL_EM_KEY_4, ACTION_COLLECTING, -1
7740 Ykey_5_blank, FALSE, FALSE,
7741 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7744 Ykey_6_blank, FALSE, FALSE,
7745 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7748 Ykey_7_blank, FALSE, FALSE,
7749 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7752 Ykey_8_blank, FALSE, FALSE,
7753 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7756 Ylenses_blank, FALSE, FALSE,
7757 EL_EMC_LENSES, ACTION_COLLECTING, -1
7760 Ymagnify_blank, FALSE, FALSE,
7761 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7764 Ygrass_blank, FALSE, FALSE,
7765 EL_EMC_GRASS, ACTION_SNAPPING, -1
7768 Ydirt_blank, FALSE, FALSE,
7769 EL_SAND, ACTION_SNAPPING, -1
7778 static struct Mapping_EM_to_RND_player
7787 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7791 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7795 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7799 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7803 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7807 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7811 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7815 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7819 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7823 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7827 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7831 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7835 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7839 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7843 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7847 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7851 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7855 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7859 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7863 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7867 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7871 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7875 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7879 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7883 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7887 EL_PLAYER_1, ACTION_DEFAULT, -1,
7891 EL_PLAYER_2, ACTION_DEFAULT, -1,
7895 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7899 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7903 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7907 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7911 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7915 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7919 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7923 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7927 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7931 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7935 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7939 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7943 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7947 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7951 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7955 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7959 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7963 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7967 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7971 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7975 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7979 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7983 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7987 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7991 EL_PLAYER_3, ACTION_DEFAULT, -1,
7995 EL_PLAYER_4, ACTION_DEFAULT, -1,
8004 int map_element_RND_to_EM_cave(int element_rnd)
8006 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
8007 static boolean mapping_initialized = FALSE;
8009 if (!mapping_initialized)
8013 // return "Xalpha_quest" for all undefined elements in mapping array
8014 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8015 mapping_RND_to_EM[i] = Xalpha_quest;
8017 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8018 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
8019 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
8020 em_object_mapping_list[i].element_em;
8022 mapping_initialized = TRUE;
8025 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
8027 Warn("invalid RND level element %d", element_rnd);
8032 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
8035 int map_element_EM_to_RND_cave(int element_em_cave)
8037 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8038 static boolean mapping_initialized = FALSE;
8040 if (!mapping_initialized)
8044 // return "EL_UNKNOWN" for all undefined elements in mapping array
8045 for (i = 0; i < GAME_TILE_MAX; i++)
8046 mapping_EM_to_RND[i] = EL_UNKNOWN;
8048 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8049 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8050 em_object_mapping_list[i].element_rnd;
8052 mapping_initialized = TRUE;
8055 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8057 Warn("invalid EM cave element %d", element_em_cave);
8062 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8065 int map_element_EM_to_RND_game(int element_em_game)
8067 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8068 static boolean mapping_initialized = FALSE;
8070 if (!mapping_initialized)
8074 // return "EL_UNKNOWN" for all undefined elements in mapping array
8075 for (i = 0; i < GAME_TILE_MAX; i++)
8076 mapping_EM_to_RND[i] = EL_UNKNOWN;
8078 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8079 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8080 em_object_mapping_list[i].element_rnd;
8082 mapping_initialized = TRUE;
8085 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8087 Warn("invalid EM game element %d", element_em_game);
8092 return mapping_EM_to_RND[element_em_game];
8095 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8097 struct LevelInfo_EM *level_em = level->native_em_level;
8098 struct CAVE *cav = level_em->cav;
8101 for (i = 0; i < GAME_TILE_MAX; i++)
8102 cav->android_array[i] = Cblank;
8104 for (i = 0; i < level->num_android_clone_elements; i++)
8106 int element_rnd = level->android_clone_element[i];
8107 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8109 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8110 if (em_object_mapping_list[j].element_rnd == element_rnd)
8111 cav->android_array[em_object_mapping_list[j].element_em] =
8116 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8118 struct LevelInfo_EM *level_em = level->native_em_level;
8119 struct CAVE *cav = level_em->cav;
8122 level->num_android_clone_elements = 0;
8124 for (i = 0; i < GAME_TILE_MAX; i++)
8126 int element_em_cave = cav->android_array[i];
8128 boolean element_found = FALSE;
8130 if (element_em_cave == Cblank)
8133 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8135 for (j = 0; j < level->num_android_clone_elements; j++)
8136 if (level->android_clone_element[j] == element_rnd)
8137 element_found = TRUE;
8141 level->android_clone_element[level->num_android_clone_elements++] =
8144 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8149 if (level->num_android_clone_elements == 0)
8151 level->num_android_clone_elements = 1;
8152 level->android_clone_element[0] = EL_EMPTY;
8156 int map_direction_RND_to_EM(int direction)
8158 return (direction == MV_UP ? 0 :
8159 direction == MV_RIGHT ? 1 :
8160 direction == MV_DOWN ? 2 :
8161 direction == MV_LEFT ? 3 :
8165 int map_direction_EM_to_RND(int direction)
8167 return (direction == 0 ? MV_UP :
8168 direction == 1 ? MV_RIGHT :
8169 direction == 2 ? MV_DOWN :
8170 direction == 3 ? MV_LEFT :
8174 int map_element_RND_to_SP(int element_rnd)
8176 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8178 if (element_rnd >= EL_SP_START &&
8179 element_rnd <= EL_SP_END)
8180 element_sp = element_rnd - EL_SP_START;
8181 else if (element_rnd == EL_EMPTY_SPACE)
8183 else if (element_rnd == EL_INVISIBLE_WALL)
8189 int map_element_SP_to_RND(int element_sp)
8191 int element_rnd = EL_UNKNOWN;
8193 if (element_sp >= 0x00 &&
8195 element_rnd = EL_SP_START + element_sp;
8196 else if (element_sp == 0x28)
8197 element_rnd = EL_INVISIBLE_WALL;
8202 int map_action_SP_to_RND(int action_sp)
8206 case actActive: return ACTION_ACTIVE;
8207 case actImpact: return ACTION_IMPACT;
8208 case actExploding: return ACTION_EXPLODING;
8209 case actDigging: return ACTION_DIGGING;
8210 case actSnapping: return ACTION_SNAPPING;
8211 case actCollecting: return ACTION_COLLECTING;
8212 case actPassing: return ACTION_PASSING;
8213 case actPushing: return ACTION_PUSHING;
8214 case actDropping: return ACTION_DROPPING;
8216 default: return ACTION_DEFAULT;
8220 int map_element_RND_to_MM(int element_rnd)
8222 return (element_rnd >= EL_MM_START_1 &&
8223 element_rnd <= EL_MM_END_1 ?
8224 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8226 element_rnd >= EL_MM_START_2 &&
8227 element_rnd <= EL_MM_END_2 ?
8228 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8230 element_rnd >= EL_MM_START_3 &&
8231 element_rnd <= EL_MM_END_3 ?
8232 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
8234 element_rnd >= EL_CHAR_START &&
8235 element_rnd <= EL_CHAR_END ?
8236 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8238 element_rnd >= EL_MM_RUNTIME_START &&
8239 element_rnd <= EL_MM_RUNTIME_END ?
8240 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8242 EL_MM_EMPTY_NATIVE);
8245 int map_element_MM_to_RND(int element_mm)
8247 return (element_mm == EL_MM_EMPTY_NATIVE ||
8248 element_mm == EL_DF_EMPTY_NATIVE ?
8251 element_mm >= EL_MM_START_1_NATIVE &&
8252 element_mm <= EL_MM_END_1_NATIVE ?
8253 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8255 element_mm >= EL_MM_START_2_NATIVE &&
8256 element_mm <= EL_MM_END_2_NATIVE ?
8257 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8259 element_mm >= EL_MM_START_3_NATIVE &&
8260 element_mm <= EL_MM_END_3_NATIVE ?
8261 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
8263 element_mm >= EL_MM_CHAR_START_NATIVE &&
8264 element_mm <= EL_MM_CHAR_END_NATIVE ?
8265 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8267 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8268 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8269 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8274 int map_action_MM_to_RND(int action_mm)
8276 // all MM actions are defined to exactly match their RND counterparts
8280 int map_sound_MM_to_RND(int sound_mm)
8284 case SND_MM_GAME_LEVELTIME_CHARGING:
8285 return SND_GAME_LEVELTIME_CHARGING;
8287 case SND_MM_GAME_HEALTH_CHARGING:
8288 return SND_GAME_HEALTH_CHARGING;
8291 return SND_UNDEFINED;
8295 int map_mm_wall_element(int element)
8297 return (element >= EL_MM_STEEL_WALL_START &&
8298 element <= EL_MM_STEEL_WALL_END ?
8301 element >= EL_MM_WOODEN_WALL_START &&
8302 element <= EL_MM_WOODEN_WALL_END ?
8305 element >= EL_MM_ICE_WALL_START &&
8306 element <= EL_MM_ICE_WALL_END ?
8309 element >= EL_MM_AMOEBA_WALL_START &&
8310 element <= EL_MM_AMOEBA_WALL_END ?
8313 element >= EL_DF_STEEL_WALL_START &&
8314 element <= EL_DF_STEEL_WALL_END ?
8317 element >= EL_DF_WOODEN_WALL_START &&
8318 element <= EL_DF_WOODEN_WALL_END ?
8324 int map_mm_wall_element_editor(int element)
8328 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8329 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8330 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8331 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8332 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8333 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8335 default: return element;
8339 int get_next_element(int element)
8343 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8344 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8345 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8346 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8347 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8348 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8349 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8350 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8351 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8352 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8353 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8355 default: return element;
8359 int el2img_mm(int element_mm)
8361 return el2img(map_element_MM_to_RND(element_mm));
8364 int el_act2img_mm(int element_mm, int action)
8366 return el_act2img(map_element_MM_to_RND(element_mm), action);
8369 int el_act_dir2img(int element, int action, int direction)
8371 element = GFX_ELEMENT(element);
8372 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8374 // direction_graphic[][] == graphic[] for undefined direction graphics
8375 return element_info[element].direction_graphic[action][direction];
8378 static int el_act_dir2crm(int element, int action, int direction)
8380 element = GFX_ELEMENT(element);
8381 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8383 // direction_graphic[][] == graphic[] for undefined direction graphics
8384 return element_info[element].direction_crumbled[action][direction];
8387 int el_act2img(int element, int action)
8389 element = GFX_ELEMENT(element);
8391 return element_info[element].graphic[action];
8394 int el_act2crm(int element, int action)
8396 element = GFX_ELEMENT(element);
8398 return element_info[element].crumbled[action];
8401 int el_dir2img(int element, int direction)
8403 element = GFX_ELEMENT(element);
8405 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8408 int el2baseimg(int element)
8410 return element_info[element].graphic[ACTION_DEFAULT];
8413 int el2img(int element)
8415 element = GFX_ELEMENT(element);
8417 return element_info[element].graphic[ACTION_DEFAULT];
8420 int el2edimg(int element)
8422 element = GFX_ELEMENT(element);
8424 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8427 int el2preimg(int element)
8429 element = GFX_ELEMENT(element);
8431 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8434 int el2panelimg(int element)
8436 element = GFX_ELEMENT(element);
8438 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8441 int font2baseimg(int font_nr)
8443 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8446 int getBeltNrFromBeltElement(int element)
8448 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8449 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8450 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8453 int getBeltNrFromBeltActiveElement(int element)
8455 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8456 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8457 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8460 int getBeltNrFromBeltSwitchElement(int element)
8462 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8463 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8464 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8467 int getBeltDirNrFromBeltElement(int element)
8469 static int belt_base_element[4] =
8471 EL_CONVEYOR_BELT_1_LEFT,
8472 EL_CONVEYOR_BELT_2_LEFT,
8473 EL_CONVEYOR_BELT_3_LEFT,
8474 EL_CONVEYOR_BELT_4_LEFT
8477 int belt_nr = getBeltNrFromBeltElement(element);
8478 int belt_dir_nr = element - belt_base_element[belt_nr];
8480 return (belt_dir_nr % 3);
8483 int getBeltDirNrFromBeltSwitchElement(int element)
8485 static int belt_base_element[4] =
8487 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8488 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8489 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8490 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8493 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8494 int belt_dir_nr = element - belt_base_element[belt_nr];
8496 return (belt_dir_nr % 3);
8499 int getBeltDirFromBeltElement(int element)
8501 static int belt_move_dir[3] =
8508 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8510 return belt_move_dir[belt_dir_nr];
8513 int getBeltDirFromBeltSwitchElement(int element)
8515 static int belt_move_dir[3] =
8522 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8524 return belt_move_dir[belt_dir_nr];
8527 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8529 static int belt_base_element[4] =
8531 EL_CONVEYOR_BELT_1_LEFT,
8532 EL_CONVEYOR_BELT_2_LEFT,
8533 EL_CONVEYOR_BELT_3_LEFT,
8534 EL_CONVEYOR_BELT_4_LEFT
8537 return belt_base_element[belt_nr] + belt_dir_nr;
8540 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8542 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8544 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8547 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8549 static int belt_base_element[4] =
8551 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8552 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8553 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8554 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8557 return belt_base_element[belt_nr] + belt_dir_nr;
8560 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8562 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8564 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8567 boolean swapTiles_EM(boolean is_pre_emc_cave)
8569 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8572 boolean getTeamMode_EM(void)
8574 return game.team_mode || network_playing;
8577 boolean isActivePlayer_EM(int player_nr)
8579 return stored_player[player_nr].active;
8582 unsigned int InitRND(int seed)
8584 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8585 return InitEngineRandom_EM(seed);
8586 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8587 return InitEngineRandom_SP(seed);
8588 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8589 return InitEngineRandom_MM(seed);
8591 return InitEngineRandom_RND(seed);
8594 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8595 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8597 static int get_effective_element_EM(int tile, int frame_em)
8599 int element = object_mapping[tile].element_rnd;
8600 int action = object_mapping[tile].action;
8601 boolean is_backside = object_mapping[tile].is_backside;
8602 boolean action_removing = (action == ACTION_DIGGING ||
8603 action == ACTION_SNAPPING ||
8604 action == ACTION_COLLECTING);
8612 return (frame_em > 5 ? EL_EMPTY : element);
8618 else // frame_em == 7
8629 case Ydiamond_stone:
8633 case Xdrip_stretchB:
8649 case Ymagnify_blank:
8652 case Xsand_stonein_1:
8653 case Xsand_stonein_2:
8654 case Xsand_stonein_3:
8655 case Xsand_stonein_4:
8659 return (is_backside || action_removing ? EL_EMPTY : element);
8664 static boolean check_linear_animation_EM(int tile)
8668 case Xsand_stonesand_1:
8669 case Xsand_stonesand_quickout_1:
8670 case Xsand_sandstone_1:
8671 case Xsand_stonein_1:
8672 case Xsand_stoneout_1:
8700 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8701 boolean has_crumbled_graphics,
8702 int crumbled, int sync_frame)
8704 // if element can be crumbled, but certain action graphics are just empty
8705 // space (like instantly snapping sand to empty space in 1 frame), do not
8706 // treat these empty space graphics as crumbled graphics in EMC engine
8707 if (crumbled == IMG_EMPTY_SPACE)
8708 has_crumbled_graphics = FALSE;
8710 if (has_crumbled_graphics)
8712 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8713 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8714 g_crumbled->anim_delay,
8715 g_crumbled->anim_mode,
8716 g_crumbled->anim_start_frame,
8719 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8720 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8722 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8723 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8725 g_em->has_crumbled_graphics = TRUE;
8729 g_em->crumbled_bitmap = NULL;
8730 g_em->crumbled_src_x = 0;
8731 g_em->crumbled_src_y = 0;
8732 g_em->crumbled_border_size = 0;
8733 g_em->crumbled_tile_size = 0;
8735 g_em->has_crumbled_graphics = FALSE;
8740 void ResetGfxAnimation_EM(int x, int y, int tile)
8746 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8747 int tile, int frame_em, int x, int y)
8749 int action = object_mapping[tile].action;
8750 int direction = object_mapping[tile].direction;
8751 int effective_element = get_effective_element_EM(tile, frame_em);
8752 int graphic = (direction == MV_NONE ?
8753 el_act2img(effective_element, action) :
8754 el_act_dir2img(effective_element, action, direction));
8755 struct GraphicInfo *g = &graphic_info[graphic];
8757 boolean action_removing = (action == ACTION_DIGGING ||
8758 action == ACTION_SNAPPING ||
8759 action == ACTION_COLLECTING);
8760 boolean action_moving = (action == ACTION_FALLING ||
8761 action == ACTION_MOVING ||
8762 action == ACTION_PUSHING ||
8763 action == ACTION_EATING ||
8764 action == ACTION_FILLING ||
8765 action == ACTION_EMPTYING);
8766 boolean action_falling = (action == ACTION_FALLING ||
8767 action == ACTION_FILLING ||
8768 action == ACTION_EMPTYING);
8770 // special case: graphic uses "2nd movement tile" and has defined
8771 // 7 frames for movement animation (or less) => use default graphic
8772 // for last (8th) frame which ends the movement animation
8773 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8775 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8776 graphic = (direction == MV_NONE ?
8777 el_act2img(effective_element, action) :
8778 el_act_dir2img(effective_element, action, direction));
8780 g = &graphic_info[graphic];
8783 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8787 else if (action_moving)
8789 boolean is_backside = object_mapping[tile].is_backside;
8793 int direction = object_mapping[tile].direction;
8794 int move_dir = (action_falling ? MV_DOWN : direction);
8799 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8800 if (g->double_movement && frame_em == 0)
8804 if (move_dir == MV_LEFT)
8805 GfxFrame[x - 1][y] = GfxFrame[x][y];
8806 else if (move_dir == MV_RIGHT)
8807 GfxFrame[x + 1][y] = GfxFrame[x][y];
8808 else if (move_dir == MV_UP)
8809 GfxFrame[x][y - 1] = GfxFrame[x][y];
8810 else if (move_dir == MV_DOWN)
8811 GfxFrame[x][y + 1] = GfxFrame[x][y];
8818 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8819 if (tile == Xsand_stonesand_quickout_1 ||
8820 tile == Xsand_stonesand_quickout_2)
8824 if (graphic_info[graphic].anim_global_sync)
8825 sync_frame = FrameCounter;
8826 else if (graphic_info[graphic].anim_global_anim_sync)
8827 sync_frame = getGlobalAnimSyncFrame();
8828 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8829 sync_frame = GfxFrame[x][y];
8831 sync_frame = 0; // playfield border (pseudo steel)
8833 SetRandomAnimationValue(x, y);
8835 int frame = getAnimationFrame(g->anim_frames,
8838 g->anim_start_frame,
8841 g_em->unique_identifier =
8842 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8845 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8846 int tile, int frame_em, int x, int y)
8848 int action = object_mapping[tile].action;
8849 int direction = object_mapping[tile].direction;
8850 boolean is_backside = object_mapping[tile].is_backside;
8851 int effective_element = get_effective_element_EM(tile, frame_em);
8852 int effective_action = action;
8853 int graphic = (direction == MV_NONE ?
8854 el_act2img(effective_element, effective_action) :
8855 el_act_dir2img(effective_element, effective_action,
8857 int crumbled = (direction == MV_NONE ?
8858 el_act2crm(effective_element, effective_action) :
8859 el_act_dir2crm(effective_element, effective_action,
8861 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8862 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8863 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8864 struct GraphicInfo *g = &graphic_info[graphic];
8867 // special case: graphic uses "2nd movement tile" and has defined
8868 // 7 frames for movement animation (or less) => use default graphic
8869 // for last (8th) frame which ends the movement animation
8870 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8872 effective_action = ACTION_DEFAULT;
8873 graphic = (direction == MV_NONE ?
8874 el_act2img(effective_element, effective_action) :
8875 el_act_dir2img(effective_element, effective_action,
8877 crumbled = (direction == MV_NONE ?
8878 el_act2crm(effective_element, effective_action) :
8879 el_act_dir2crm(effective_element, effective_action,
8882 g = &graphic_info[graphic];
8885 if (graphic_info[graphic].anim_global_sync)
8886 sync_frame = FrameCounter;
8887 else if (graphic_info[graphic].anim_global_anim_sync)
8888 sync_frame = getGlobalAnimSyncFrame();
8889 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8890 sync_frame = GfxFrame[x][y];
8892 sync_frame = 0; // playfield border (pseudo steel)
8894 SetRandomAnimationValue(x, y);
8896 int frame = getAnimationFrame(g->anim_frames,
8899 g->anim_start_frame,
8902 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8903 g->double_movement && is_backside);
8905 // (updating the "crumbled" graphic definitions is probably not really needed,
8906 // as animations for crumbled graphics can't be longer than one EMC cycle)
8907 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8911 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8912 int player_nr, int anim, int frame_em)
8914 int element = player_mapping[player_nr][anim].element_rnd;
8915 int action = player_mapping[player_nr][anim].action;
8916 int direction = player_mapping[player_nr][anim].direction;
8917 int graphic = (direction == MV_NONE ?
8918 el_act2img(element, action) :
8919 el_act_dir2img(element, action, direction));
8920 struct GraphicInfo *g = &graphic_info[graphic];
8923 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8925 stored_player[player_nr].StepFrame = frame_em;
8927 sync_frame = stored_player[player_nr].Frame;
8929 int frame = getAnimationFrame(g->anim_frames,
8932 g->anim_start_frame,
8935 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8936 &g_em->src_x, &g_em->src_y, FALSE);
8939 void InitGraphicInfo_EM(void)
8943 // always start with reliable default values
8944 for (i = 0; i < GAME_TILE_MAX; i++)
8946 object_mapping[i].element_rnd = EL_UNKNOWN;
8947 object_mapping[i].is_backside = FALSE;
8948 object_mapping[i].action = ACTION_DEFAULT;
8949 object_mapping[i].direction = MV_NONE;
8952 // always start with reliable default values
8953 for (p = 0; p < MAX_PLAYERS; p++)
8955 for (i = 0; i < PLY_MAX; i++)
8957 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8958 player_mapping[p][i].action = ACTION_DEFAULT;
8959 player_mapping[p][i].direction = MV_NONE;
8963 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8965 int e = em_object_mapping_list[i].element_em;
8967 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8968 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8970 if (em_object_mapping_list[i].action != -1)
8971 object_mapping[e].action = em_object_mapping_list[i].action;
8973 if (em_object_mapping_list[i].direction != -1)
8974 object_mapping[e].direction =
8975 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8978 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8980 int a = em_player_mapping_list[i].action_em;
8981 int p = em_player_mapping_list[i].player_nr;
8983 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8985 if (em_player_mapping_list[i].action != -1)
8986 player_mapping[p][a].action = em_player_mapping_list[i].action;
8988 if (em_player_mapping_list[i].direction != -1)
8989 player_mapping[p][a].direction =
8990 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8993 for (i = 0; i < GAME_TILE_MAX; i++)
8995 int element = object_mapping[i].element_rnd;
8996 int action = object_mapping[i].action;
8997 int direction = object_mapping[i].direction;
8998 boolean is_backside = object_mapping[i].is_backside;
8999 boolean action_exploding = ((action == ACTION_EXPLODING ||
9000 action == ACTION_SMASHED_BY_ROCK ||
9001 action == ACTION_SMASHED_BY_SPRING) &&
9002 element != EL_DIAMOND);
9003 boolean action_active = (action == ACTION_ACTIVE);
9004 boolean action_other = (action == ACTION_OTHER);
9006 for (j = 0; j < 8; j++)
9008 int effective_element = get_effective_element_EM(i, j);
9009 int effective_action = (j < 7 ? action :
9010 i == Xdrip_stretch ? action :
9011 i == Xdrip_stretchB ? action :
9012 i == Ydrip_1_s ? action :
9013 i == Ydrip_1_sB ? action :
9014 i == Yball_1 ? action :
9015 i == Xball_2 ? action :
9016 i == Yball_2 ? action :
9017 i == Yball_blank ? action :
9018 i == Ykey_1_blank ? action :
9019 i == Ykey_2_blank ? action :
9020 i == Ykey_3_blank ? action :
9021 i == Ykey_4_blank ? action :
9022 i == Ykey_5_blank ? action :
9023 i == Ykey_6_blank ? action :
9024 i == Ykey_7_blank ? action :
9025 i == Ykey_8_blank ? action :
9026 i == Ylenses_blank ? action :
9027 i == Ymagnify_blank ? action :
9028 i == Ygrass_blank ? action :
9029 i == Ydirt_blank ? action :
9030 i == Xsand_stonein_1 ? action :
9031 i == Xsand_stonein_2 ? action :
9032 i == Xsand_stonein_3 ? action :
9033 i == Xsand_stonein_4 ? action :
9034 i == Xsand_stoneout_1 ? action :
9035 i == Xsand_stoneout_2 ? action :
9036 i == Xboom_android ? ACTION_EXPLODING :
9037 action_exploding ? ACTION_EXPLODING :
9038 action_active ? action :
9039 action_other ? action :
9041 int graphic = (el_act_dir2img(effective_element, effective_action,
9043 int crumbled = (el_act_dir2crm(effective_element, effective_action,
9045 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
9046 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
9047 boolean has_action_graphics = (graphic != base_graphic);
9048 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9049 struct GraphicInfo *g = &graphic_info[graphic];
9050 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9053 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9054 boolean special_animation = (action != ACTION_DEFAULT &&
9055 g->anim_frames == 3 &&
9056 g->anim_delay == 2 &&
9057 g->anim_mode & ANIM_LINEAR);
9058 int sync_frame = (i == Xdrip_stretch ? 7 :
9059 i == Xdrip_stretchB ? 7 :
9060 i == Ydrip_2_s ? j + 8 :
9061 i == Ydrip_2_sB ? j + 8 :
9070 i == Xfake_acid_1 ? 0 :
9071 i == Xfake_acid_2 ? 10 :
9072 i == Xfake_acid_3 ? 20 :
9073 i == Xfake_acid_4 ? 30 :
9074 i == Xfake_acid_5 ? 40 :
9075 i == Xfake_acid_6 ? 50 :
9076 i == Xfake_acid_7 ? 60 :
9077 i == Xfake_acid_8 ? 70 :
9078 i == Xfake_acid_1_player ? 0 :
9079 i == Xfake_acid_2_player ? 10 :
9080 i == Xfake_acid_3_player ? 20 :
9081 i == Xfake_acid_4_player ? 30 :
9082 i == Xfake_acid_5_player ? 40 :
9083 i == Xfake_acid_6_player ? 50 :
9084 i == Xfake_acid_7_player ? 60 :
9085 i == Xfake_acid_8_player ? 70 :
9087 i == Yball_2 ? j + 8 :
9088 i == Yball_blank ? j + 1 :
9089 i == Ykey_1_blank ? j + 1 :
9090 i == Ykey_2_blank ? j + 1 :
9091 i == Ykey_3_blank ? j + 1 :
9092 i == Ykey_4_blank ? j + 1 :
9093 i == Ykey_5_blank ? j + 1 :
9094 i == Ykey_6_blank ? j + 1 :
9095 i == Ykey_7_blank ? j + 1 :
9096 i == Ykey_8_blank ? j + 1 :
9097 i == Ylenses_blank ? j + 1 :
9098 i == Ymagnify_blank ? j + 1 :
9099 i == Ygrass_blank ? j + 1 :
9100 i == Ydirt_blank ? j + 1 :
9101 i == Xamoeba_1 ? 0 :
9102 i == Xamoeba_2 ? 1 :
9103 i == Xamoeba_3 ? 2 :
9104 i == Xamoeba_4 ? 3 :
9105 i == Xamoeba_5 ? 0 :
9106 i == Xamoeba_6 ? 1 :
9107 i == Xamoeba_7 ? 2 :
9108 i == Xamoeba_8 ? 3 :
9109 i == Xexit_2 ? j + 8 :
9110 i == Xexit_3 ? j + 16 :
9111 i == Xdynamite_1 ? 0 :
9112 i == Xdynamite_2 ? 8 :
9113 i == Xdynamite_3 ? 16 :
9114 i == Xdynamite_4 ? 24 :
9115 i == Xsand_stonein_1 ? j + 1 :
9116 i == Xsand_stonein_2 ? j + 9 :
9117 i == Xsand_stonein_3 ? j + 17 :
9118 i == Xsand_stonein_4 ? j + 25 :
9119 i == Xsand_stoneout_1 && j == 0 ? 0 :
9120 i == Xsand_stoneout_1 && j == 1 ? 0 :
9121 i == Xsand_stoneout_1 && j == 2 ? 1 :
9122 i == Xsand_stoneout_1 && j == 3 ? 2 :
9123 i == Xsand_stoneout_1 && j == 4 ? 2 :
9124 i == Xsand_stoneout_1 && j == 5 ? 3 :
9125 i == Xsand_stoneout_1 && j == 6 ? 4 :
9126 i == Xsand_stoneout_1 && j == 7 ? 4 :
9127 i == Xsand_stoneout_2 && j == 0 ? 5 :
9128 i == Xsand_stoneout_2 && j == 1 ? 6 :
9129 i == Xsand_stoneout_2 && j == 2 ? 7 :
9130 i == Xsand_stoneout_2 && j == 3 ? 8 :
9131 i == Xsand_stoneout_2 && j == 4 ? 9 :
9132 i == Xsand_stoneout_2 && j == 5 ? 11 :
9133 i == Xsand_stoneout_2 && j == 6 ? 13 :
9134 i == Xsand_stoneout_2 && j == 7 ? 15 :
9135 i == Xboom_bug && j == 1 ? 2 :
9136 i == Xboom_bug && j == 2 ? 2 :
9137 i == Xboom_bug && j == 3 ? 4 :
9138 i == Xboom_bug && j == 4 ? 4 :
9139 i == Xboom_bug && j == 5 ? 2 :
9140 i == Xboom_bug && j == 6 ? 2 :
9141 i == Xboom_bug && j == 7 ? 0 :
9142 i == Xboom_tank && j == 1 ? 2 :
9143 i == Xboom_tank && j == 2 ? 2 :
9144 i == Xboom_tank && j == 3 ? 4 :
9145 i == Xboom_tank && j == 4 ? 4 :
9146 i == Xboom_tank && j == 5 ? 2 :
9147 i == Xboom_tank && j == 6 ? 2 :
9148 i == Xboom_tank && j == 7 ? 0 :
9149 i == Xboom_android && j == 7 ? 6 :
9150 i == Xboom_1 && j == 1 ? 2 :
9151 i == Xboom_1 && j == 2 ? 2 :
9152 i == Xboom_1 && j == 3 ? 4 :
9153 i == Xboom_1 && j == 4 ? 4 :
9154 i == Xboom_1 && j == 5 ? 6 :
9155 i == Xboom_1 && j == 6 ? 6 :
9156 i == Xboom_1 && j == 7 ? 8 :
9157 i == Xboom_2 && j == 0 ? 8 :
9158 i == Xboom_2 && j == 1 ? 8 :
9159 i == Xboom_2 && j == 2 ? 10 :
9160 i == Xboom_2 && j == 3 ? 10 :
9161 i == Xboom_2 && j == 4 ? 10 :
9162 i == Xboom_2 && j == 5 ? 12 :
9163 i == Xboom_2 && j == 6 ? 12 :
9164 i == Xboom_2 && j == 7 ? 12 :
9165 special_animation && j == 4 ? 3 :
9166 effective_action != action ? 0 :
9168 int frame = getAnimationFrame(g->anim_frames,
9171 g->anim_start_frame,
9174 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9175 g->double_movement && is_backside);
9177 g_em->bitmap = src_bitmap;
9178 g_em->src_x = src_x;
9179 g_em->src_y = src_y;
9180 g_em->src_offset_x = 0;
9181 g_em->src_offset_y = 0;
9182 g_em->dst_offset_x = 0;
9183 g_em->dst_offset_y = 0;
9184 g_em->width = TILEX;
9185 g_em->height = TILEY;
9187 g_em->preserve_background = FALSE;
9189 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9192 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9193 effective_action == ACTION_MOVING ||
9194 effective_action == ACTION_PUSHING ||
9195 effective_action == ACTION_EATING)) ||
9196 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9197 effective_action == ACTION_EMPTYING)))
9200 (effective_action == ACTION_FALLING ||
9201 effective_action == ACTION_FILLING ||
9202 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9203 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9204 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9205 int num_steps = (i == Ydrip_1_s ? 16 :
9206 i == Ydrip_1_sB ? 16 :
9207 i == Ydrip_2_s ? 16 :
9208 i == Ydrip_2_sB ? 16 :
9209 i == Xsand_stonein_1 ? 32 :
9210 i == Xsand_stonein_2 ? 32 :
9211 i == Xsand_stonein_3 ? 32 :
9212 i == Xsand_stonein_4 ? 32 :
9213 i == Xsand_stoneout_1 ? 16 :
9214 i == Xsand_stoneout_2 ? 16 : 8);
9215 int cx = ABS(dx) * (TILEX / num_steps);
9216 int cy = ABS(dy) * (TILEY / num_steps);
9217 int step_frame = (i == Ydrip_2_s ? j + 8 :
9218 i == Ydrip_2_sB ? j + 8 :
9219 i == Xsand_stonein_2 ? j + 8 :
9220 i == Xsand_stonein_3 ? j + 16 :
9221 i == Xsand_stonein_4 ? j + 24 :
9222 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9223 int step = (is_backside ? step_frame : num_steps - step_frame);
9225 if (is_backside) // tile where movement starts
9227 if (dx < 0 || dy < 0)
9229 g_em->src_offset_x = cx * step;
9230 g_em->src_offset_y = cy * step;
9234 g_em->dst_offset_x = cx * step;
9235 g_em->dst_offset_y = cy * step;
9238 else // tile where movement ends
9240 if (dx < 0 || dy < 0)
9242 g_em->dst_offset_x = cx * step;
9243 g_em->dst_offset_y = cy * step;
9247 g_em->src_offset_x = cx * step;
9248 g_em->src_offset_y = cy * step;
9252 g_em->width = TILEX - cx * step;
9253 g_em->height = TILEY - cy * step;
9256 // create unique graphic identifier to decide if tile must be redrawn
9257 /* bit 31 - 16 (16 bit): EM style graphic
9258 bit 15 - 12 ( 4 bit): EM style frame
9259 bit 11 - 6 ( 6 bit): graphic width
9260 bit 5 - 0 ( 6 bit): graphic height */
9261 g_em->unique_identifier =
9262 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9266 for (i = 0; i < GAME_TILE_MAX; i++)
9268 for (j = 0; j < 8; j++)
9270 int element = object_mapping[i].element_rnd;
9271 int action = object_mapping[i].action;
9272 int direction = object_mapping[i].direction;
9273 boolean is_backside = object_mapping[i].is_backside;
9274 int graphic_action = el_act_dir2img(element, action, direction);
9275 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9277 if ((action == ACTION_SMASHED_BY_ROCK ||
9278 action == ACTION_SMASHED_BY_SPRING ||
9279 action == ACTION_EATING) &&
9280 graphic_action == graphic_default)
9282 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9283 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9284 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9285 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9288 // no separate animation for "smashed by rock" -- use rock instead
9289 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9290 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9292 g_em->bitmap = g_xx->bitmap;
9293 g_em->src_x = g_xx->src_x;
9294 g_em->src_y = g_xx->src_y;
9295 g_em->src_offset_x = g_xx->src_offset_x;
9296 g_em->src_offset_y = g_xx->src_offset_y;
9297 g_em->dst_offset_x = g_xx->dst_offset_x;
9298 g_em->dst_offset_y = g_xx->dst_offset_y;
9299 g_em->width = g_xx->width;
9300 g_em->height = g_xx->height;
9301 g_em->unique_identifier = g_xx->unique_identifier;
9304 g_em->preserve_background = TRUE;
9309 for (p = 0; p < MAX_PLAYERS; p++)
9311 for (i = 0; i < PLY_MAX; i++)
9313 int element = player_mapping[p][i].element_rnd;
9314 int action = player_mapping[p][i].action;
9315 int direction = player_mapping[p][i].direction;
9317 for (j = 0; j < 8; j++)
9319 int effective_element = element;
9320 int effective_action = action;
9321 int graphic = (direction == MV_NONE ?
9322 el_act2img(effective_element, effective_action) :
9323 el_act_dir2img(effective_element, effective_action,
9325 struct GraphicInfo *g = &graphic_info[graphic];
9326 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9330 int frame = getAnimationFrame(g->anim_frames,
9333 g->anim_start_frame,
9336 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9338 g_em->bitmap = src_bitmap;
9339 g_em->src_x = src_x;
9340 g_em->src_y = src_y;
9341 g_em->src_offset_x = 0;
9342 g_em->src_offset_y = 0;
9343 g_em->dst_offset_x = 0;
9344 g_em->dst_offset_y = 0;
9345 g_em->width = TILEX;
9346 g_em->height = TILEY;
9352 static void CheckSaveEngineSnapshot_EM(int frame,
9353 boolean any_player_moving,
9354 boolean any_player_snapping,
9355 boolean any_player_dropping)
9357 if (frame == 7 && !any_player_dropping)
9359 if (!local_player->was_waiting)
9361 if (!CheckSaveEngineSnapshotToList())
9364 local_player->was_waiting = TRUE;
9367 else if (any_player_moving || any_player_snapping || any_player_dropping)
9369 local_player->was_waiting = FALSE;
9373 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9374 boolean murphy_is_dropping)
9376 if (murphy_is_waiting)
9378 if (!local_player->was_waiting)
9380 if (!CheckSaveEngineSnapshotToList())
9383 local_player->was_waiting = TRUE;
9388 local_player->was_waiting = FALSE;
9392 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9393 boolean button_released)
9395 if (button_released)
9397 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9398 CheckSaveEngineSnapshotToList();
9400 else if (element_clicked)
9402 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9403 CheckSaveEngineSnapshotToList();
9405 game.snapshot.changed_action = TRUE;
9409 boolean CheckSingleStepMode_EM(int frame,
9410 boolean any_player_moving,
9411 boolean any_player_snapping,
9412 boolean any_player_dropping)
9414 if (tape.single_step && tape.recording && !tape.pausing)
9415 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9416 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9418 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9419 any_player_snapping, any_player_dropping);
9421 return tape.pausing;
9424 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9425 boolean murphy_is_dropping)
9427 boolean murphy_starts_dropping = FALSE;
9430 for (i = 0; i < MAX_PLAYERS; i++)
9431 if (stored_player[i].force_dropping)
9432 murphy_starts_dropping = TRUE;
9434 if (tape.single_step && tape.recording && !tape.pausing)
9435 if (murphy_is_waiting && !murphy_starts_dropping)
9436 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9438 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9441 void CheckSingleStepMode_MM(boolean element_clicked,
9442 boolean button_released)
9444 if (tape.single_step && tape.recording && !tape.pausing)
9445 if (button_released)
9446 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9448 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9451 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9452 int graphic, int sync_frame)
9454 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9456 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9459 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9461 return (IS_NEXT_FRAME(sync_frame, graphic));
9464 int getGraphicInfo_Delay(int graphic)
9466 return graphic_info[graphic].anim_delay;
9469 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9471 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9474 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9480 void PlayMenuSoundExt(int sound)
9482 if (sound == SND_UNDEFINED)
9485 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9486 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9489 if (IS_LOOP_SOUND(sound))
9490 PlaySoundLoop(sound);
9495 void PlayMenuSound(void)
9497 PlayMenuSoundExt(menu.sound[game_status]);
9500 void PlayMenuSoundStereo(int sound, int stereo_position)
9502 if (sound == SND_UNDEFINED)
9505 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9506 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9509 if (IS_LOOP_SOUND(sound))
9510 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9512 PlaySoundStereo(sound, stereo_position);
9515 void PlayMenuSoundIfLoopExt(int sound)
9517 if (sound == SND_UNDEFINED)
9520 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9521 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9524 if (IS_LOOP_SOUND(sound))
9525 PlaySoundLoop(sound);
9528 void PlayMenuSoundIfLoop(void)
9530 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9533 void PlayMenuMusicExt(int music)
9535 if (music == MUS_UNDEFINED)
9538 if (!setup.sound_music)
9541 if (IS_LOOP_MUSIC(music))
9542 PlayMusicLoop(music);
9547 void PlayMenuMusic(void)
9549 char *curr_music = getCurrentlyPlayingMusicFilename();
9550 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9552 if (!strEqual(curr_music, next_music))
9553 PlayMenuMusicExt(menu.music[game_status]);
9556 void PlayMenuSoundsAndMusic(void)
9562 static void FadeMenuSounds(void)
9567 static void FadeMenuMusic(void)
9569 char *curr_music = getCurrentlyPlayingMusicFilename();
9570 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9572 if (!strEqual(curr_music, next_music))
9576 void FadeMenuSoundsAndMusic(void)
9582 void PlaySoundActivating(void)
9585 PlaySound(SND_MENU_ITEM_ACTIVATING);
9589 void PlaySoundSelecting(void)
9592 PlaySound(SND_MENU_ITEM_SELECTING);
9596 void ToggleFullscreenIfNeeded(void)
9598 // if setup and video fullscreen state are already matching, nothing do do
9599 if (setup.fullscreen == video.fullscreen_enabled ||
9600 !video.fullscreen_available)
9603 SDLSetWindowFullscreen(setup.fullscreen);
9605 // set setup value according to successfully changed fullscreen mode
9606 setup.fullscreen = video.fullscreen_enabled;
9609 void ChangeWindowScalingIfNeeded(void)
9611 // if setup and video window scaling are already matching, nothing do do
9612 if (setup.window_scaling_percent == video.window_scaling_percent ||
9613 video.fullscreen_enabled)
9616 SDLSetWindowScaling(setup.window_scaling_percent);
9618 // set setup value according to successfully changed window scaling
9619 setup.window_scaling_percent = video.window_scaling_percent;
9622 void ChangeVsyncModeIfNeeded(void)
9624 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9625 int video_vsync_mode = video.vsync_mode;
9627 // if setup and video vsync mode are already matching, nothing do do
9628 if (setup_vsync_mode == video_vsync_mode)
9631 // if renderer is using OpenGL, vsync mode can directly be changed
9632 SDLSetScreenVsyncMode(setup.vsync_mode);
9634 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9635 if (video.vsync_mode == video_vsync_mode)
9637 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9639 // save backbuffer content which gets lost when re-creating screen
9640 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9642 // force re-creating screen and renderer to set new vsync mode
9643 video.fullscreen_enabled = !setup.fullscreen;
9645 // when creating new renderer, destroy textures linked to old renderer
9646 FreeAllImageTextures(); // needs old renderer to free the textures
9648 // re-create screen and renderer (including change of vsync mode)
9649 ChangeVideoModeIfNeeded(setup.fullscreen);
9651 // set setup value according to successfully changed fullscreen mode
9652 setup.fullscreen = video.fullscreen_enabled;
9654 // restore backbuffer content from temporary backbuffer backup bitmap
9655 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9656 FreeBitmap(tmp_backbuffer);
9658 // update visible window/screen
9659 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9661 // when changing vsync mode, re-create textures for new renderer
9662 InitImageTextures();
9665 // set setup value according to successfully changed vsync mode
9666 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9669 static void JoinRectangles(int *x, int *y, int *width, int *height,
9670 int x2, int y2, int width2, int height2)
9672 // do not join with "off-screen" rectangle
9673 if (x2 == -1 || y2 == -1)
9678 *width = MAX(*width, width2);
9679 *height = MAX(*height, height2);
9682 void SetAnimStatus(int anim_status_new)
9684 if (anim_status_new == GAME_MODE_MAIN)
9685 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9686 else if (anim_status_new == GAME_MODE_NAMES)
9687 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9688 else if (anim_status_new == GAME_MODE_SCORES)
9689 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9691 global.anim_status_next = anim_status_new;
9693 // directly set screen modes that are entered without fading
9694 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9695 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9696 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9697 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9698 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9699 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9700 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9701 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9702 global.anim_status = global.anim_status_next;
9705 void SetGameStatus(int game_status_new)
9707 if (game_status_new != game_status)
9708 game_status_last_screen = game_status;
9710 game_status = game_status_new;
9712 SetAnimStatus(game_status_new);
9715 void SetFontStatus(int game_status_new)
9717 static int last_game_status = -1;
9719 if (game_status_new != -1)
9721 // set game status for font use after storing last game status
9722 last_game_status = game_status;
9723 game_status = game_status_new;
9727 // reset game status after font use from last stored game status
9728 game_status = last_game_status;
9732 void ResetFontStatus(void)
9737 void SetLevelSetInfo(char *identifier, int level_nr)
9739 setString(&levelset.identifier, identifier);
9741 levelset.level_nr = level_nr;
9744 boolean CheckIfAllViewportsHaveChanged(void)
9746 // if game status has not changed, viewports have not changed either
9747 if (game_status == game_status_last)
9750 // check if all viewports have changed with current game status
9752 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9753 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9754 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9755 int new_real_sx = vp_playfield->x;
9756 int new_real_sy = vp_playfield->y;
9757 int new_full_sxsize = vp_playfield->width;
9758 int new_full_sysize = vp_playfield->height;
9759 int new_dx = vp_door_1->x;
9760 int new_dy = vp_door_1->y;
9761 int new_dxsize = vp_door_1->width;
9762 int new_dysize = vp_door_1->height;
9763 int new_vx = vp_door_2->x;
9764 int new_vy = vp_door_2->y;
9765 int new_vxsize = vp_door_2->width;
9766 int new_vysize = vp_door_2->height;
9768 boolean playfield_viewport_has_changed =
9769 (new_real_sx != REAL_SX ||
9770 new_real_sy != REAL_SY ||
9771 new_full_sxsize != FULL_SXSIZE ||
9772 new_full_sysize != FULL_SYSIZE);
9774 boolean door_1_viewport_has_changed =
9777 new_dxsize != DXSIZE ||
9778 new_dysize != DYSIZE);
9780 boolean door_2_viewport_has_changed =
9783 new_vxsize != VXSIZE ||
9784 new_vysize != VYSIZE ||
9785 game_status_last == GAME_MODE_EDITOR);
9787 return (playfield_viewport_has_changed &&
9788 door_1_viewport_has_changed &&
9789 door_2_viewport_has_changed);
9792 boolean CheckFadeAll(void)
9794 return (CheckIfGlobalBorderHasChanged() ||
9795 CheckIfAllViewportsHaveChanged());
9798 void ChangeViewportPropertiesIfNeeded(void)
9800 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9801 FALSE : setup.small_game_graphics);
9802 int gfx_game_mode = getGlobalGameStatus(game_status);
9803 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9805 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9806 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9807 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9808 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9809 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9810 int new_win_xsize = vp_window->width;
9811 int new_win_ysize = vp_window->height;
9812 int border_left = vp_playfield->border_left;
9813 int border_right = vp_playfield->border_right;
9814 int border_top = vp_playfield->border_top;
9815 int border_bottom = vp_playfield->border_bottom;
9816 int new_sx = vp_playfield->x + border_left;
9817 int new_sy = vp_playfield->y + border_top;
9818 int new_sxsize = vp_playfield->width - border_left - border_right;
9819 int new_sysize = vp_playfield->height - border_top - border_bottom;
9820 int new_real_sx = vp_playfield->x;
9821 int new_real_sy = vp_playfield->y;
9822 int new_full_sxsize = vp_playfield->width;
9823 int new_full_sysize = vp_playfield->height;
9824 int new_dx = vp_door_1->x;
9825 int new_dy = vp_door_1->y;
9826 int new_dxsize = vp_door_1->width;
9827 int new_dysize = vp_door_1->height;
9828 int new_vx = vp_door_2->x;
9829 int new_vy = vp_door_2->y;
9830 int new_vxsize = vp_door_2->width;
9831 int new_vysize = vp_door_2->height;
9832 int new_ex = vp_door_3->x;
9833 int new_ey = vp_door_3->y;
9834 int new_exsize = vp_door_3->width;
9835 int new_eysize = vp_door_3->height;
9836 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9837 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9838 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9839 int new_scr_fieldx = new_sxsize / tilesize;
9840 int new_scr_fieldy = new_sysize / tilesize;
9841 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9842 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9843 boolean init_gfx_buffers = FALSE;
9844 boolean init_video_buffer = FALSE;
9845 boolean init_gadgets_and_anims = FALSE;
9846 boolean init_em_graphics = FALSE;
9848 if (new_win_xsize != WIN_XSIZE ||
9849 new_win_ysize != WIN_YSIZE)
9851 WIN_XSIZE = new_win_xsize;
9852 WIN_YSIZE = new_win_ysize;
9854 init_video_buffer = TRUE;
9855 init_gfx_buffers = TRUE;
9856 init_gadgets_and_anims = TRUE;
9858 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9861 if (new_scr_fieldx != SCR_FIELDX ||
9862 new_scr_fieldy != SCR_FIELDY)
9864 // this always toggles between MAIN and GAME when using small tile size
9866 SCR_FIELDX = new_scr_fieldx;
9867 SCR_FIELDY = new_scr_fieldy;
9869 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9880 new_sxsize != SXSIZE ||
9881 new_sysize != SYSIZE ||
9882 new_dxsize != DXSIZE ||
9883 new_dysize != DYSIZE ||
9884 new_vxsize != VXSIZE ||
9885 new_vysize != VYSIZE ||
9886 new_exsize != EXSIZE ||
9887 new_eysize != EYSIZE ||
9888 new_real_sx != REAL_SX ||
9889 new_real_sy != REAL_SY ||
9890 new_full_sxsize != FULL_SXSIZE ||
9891 new_full_sysize != FULL_SYSIZE ||
9892 new_tilesize_var != TILESIZE_VAR
9895 // ------------------------------------------------------------------------
9896 // determine next fading area for changed viewport definitions
9897 // ------------------------------------------------------------------------
9899 // start with current playfield area (default fading area)
9902 FADE_SXSIZE = FULL_SXSIZE;
9903 FADE_SYSIZE = FULL_SYSIZE;
9905 // add new playfield area if position or size has changed
9906 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9907 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9909 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9910 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9913 // add current and new door 1 area if position or size has changed
9914 if (new_dx != DX || new_dy != DY ||
9915 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9917 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9918 DX, DY, DXSIZE, DYSIZE);
9919 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9920 new_dx, new_dy, new_dxsize, new_dysize);
9923 // add current and new door 2 area if position or size has changed
9924 if (new_vx != VX || new_vy != VY ||
9925 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9927 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9928 VX, VY, VXSIZE, VYSIZE);
9929 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9930 new_vx, new_vy, new_vxsize, new_vysize);
9933 // ------------------------------------------------------------------------
9934 // handle changed tile size
9935 // ------------------------------------------------------------------------
9937 if (new_tilesize_var != TILESIZE_VAR)
9939 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9941 // changing tile size invalidates scroll values of engine snapshots
9942 FreeEngineSnapshotSingle();
9944 // changing tile size requires update of graphic mapping for EM engine
9945 init_em_graphics = TRUE;
9956 SXSIZE = new_sxsize;
9957 SYSIZE = new_sysize;
9958 DXSIZE = new_dxsize;
9959 DYSIZE = new_dysize;
9960 VXSIZE = new_vxsize;
9961 VYSIZE = new_vysize;
9962 EXSIZE = new_exsize;
9963 EYSIZE = new_eysize;
9964 REAL_SX = new_real_sx;
9965 REAL_SY = new_real_sy;
9966 FULL_SXSIZE = new_full_sxsize;
9967 FULL_SYSIZE = new_full_sysize;
9968 TILESIZE_VAR = new_tilesize_var;
9970 init_gfx_buffers = TRUE;
9971 init_gadgets_and_anims = TRUE;
9973 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9974 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9977 if (init_gfx_buffers)
9979 // Debug("tools:viewport", "init_gfx_buffers");
9981 SCR_FIELDX = new_scr_fieldx_buffers;
9982 SCR_FIELDY = new_scr_fieldy_buffers;
9986 SCR_FIELDX = new_scr_fieldx;
9987 SCR_FIELDY = new_scr_fieldy;
9989 SetDrawDeactivationMask(REDRAW_NONE);
9990 SetDrawBackgroundMask(REDRAW_FIELD);
9993 if (init_video_buffer)
9995 // Debug("tools:viewport", "init_video_buffer");
9997 FreeAllImageTextures(); // needs old renderer to free the textures
9999 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
10000 InitImageTextures();
10003 if (init_gadgets_and_anims)
10005 // Debug("tools:viewport", "init_gadgets_and_anims");
10008 InitGlobalAnimations();
10011 if (init_em_graphics)
10013 InitGraphicInfo_EM();
10017 void OpenURL(char *url)
10019 #if SDL_VERSION_ATLEAST(2,0,14)
10022 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
10023 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
10024 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
10028 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
10030 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
10034 // ============================================================================
10036 // ============================================================================
10038 #if defined(PLATFORM_WINDOWS)
10039 /* FILETIME of Jan 1 1970 00:00:00. */
10040 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
10043 * timezone information is stored outside the kernel so tzp isn't used anymore.
10045 * Note: this function is not for Win32 high precision timing purpose. See
10048 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
10050 FILETIME file_time;
10051 SYSTEMTIME system_time;
10052 ULARGE_INTEGER ularge;
10054 GetSystemTime(&system_time);
10055 SystemTimeToFileTime(&system_time, &file_time);
10056 ularge.LowPart = file_time.dwLowDateTime;
10057 ularge.HighPart = file_time.dwHighDateTime;
10059 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10060 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10066 static char *test_init_uuid_random_function_simple(void)
10068 static char seed_text[100];
10069 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10071 sprintf(seed_text, "%d", seed);
10076 static char *test_init_uuid_random_function_better(void)
10078 static char seed_text[100];
10079 struct timeval current_time;
10081 gettimeofday(¤t_time, NULL);
10083 prng_seed_bytes(¤t_time, sizeof(current_time));
10085 sprintf(seed_text, "%ld.%ld",
10086 (long)current_time.tv_sec,
10087 (long)current_time.tv_usec);
10092 #if defined(PLATFORM_WINDOWS)
10093 static char *test_init_uuid_random_function_better_windows(void)
10095 static char seed_text[100];
10096 struct timeval current_time;
10098 gettimeofday_windows(¤t_time, NULL);
10100 prng_seed_bytes(¤t_time, sizeof(current_time));
10102 sprintf(seed_text, "%ld.%ld",
10103 (long)current_time.tv_sec,
10104 (long)current_time.tv_usec);
10110 static unsigned int test_uuid_random_function_simple(int max)
10112 return GetSimpleRandom(max);
10115 static unsigned int test_uuid_random_function_better(int max)
10117 return (max > 0 ? prng_get_uint() % max : 0);
10120 #if defined(PLATFORM_WINDOWS)
10121 #define NUM_UUID_TESTS 3
10123 #define NUM_UUID_TESTS 2
10126 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10128 struct hashtable *hash_seeds =
10129 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10130 struct hashtable *hash_uuids =
10131 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10132 static char message[100];
10135 char *random_name = (nr == 0 ? "simple" : "better");
10136 char *random_type = (always_seed ? "always" : "only once");
10137 char *(*init_random_function)(void) =
10139 test_init_uuid_random_function_simple :
10140 test_init_uuid_random_function_better);
10141 unsigned int (*random_function)(int) =
10143 test_uuid_random_function_simple :
10144 test_uuid_random_function_better);
10147 #if defined(PLATFORM_WINDOWS)
10150 random_name = "windows";
10151 init_random_function = test_init_uuid_random_function_better_windows;
10157 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10158 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10160 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10161 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10162 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10164 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10168 // always initialize random number generator at least once
10169 init_random_function();
10171 unsigned int time_start = SDL_GetTicks();
10173 for (i = 0; i < num_uuids; i++)
10177 char *seed = getStringCopy(init_random_function());
10179 hashtable_remove(hash_seeds, seed);
10180 hashtable_insert(hash_seeds, seed, "1");
10183 char *uuid = getStringCopy(getUUIDExt(random_function));
10185 hashtable_remove(hash_uuids, uuid);
10186 hashtable_insert(hash_uuids, uuid, "1");
10189 int num_unique_seeds = hashtable_count(hash_seeds);
10190 int num_unique_uuids = hashtable_count(hash_uuids);
10192 unsigned int time_needed = SDL_GetTicks() - time_start;
10194 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10196 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10199 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10201 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10202 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10204 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10206 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10208 Request(message, REQ_CONFIRM);
10210 hashtable_destroy(hash_seeds, 0);
10211 hashtable_destroy(hash_uuids, 0);
10214 void TestGeneratingUUIDs(void)
10216 int num_uuids = 1000000;
10219 for (i = 0; i < NUM_UUID_TESTS; i++)
10220 for (j = 0; j < 2; j++)
10221 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10223 CloseAllAndExit(0);