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)
3047 if (global.use_envelope_request &&
3048 game.request_active_or_moving &&
3049 request.sort_priority > 0 &&
3050 drawing_target == DRAW_TO_SCREEN)
3052 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3053 request.sx, request.sy);
3057 static void setRequestBasePosition(int *x, int *y)
3059 int sx_base, sy_base;
3061 if (request.x != -1)
3062 sx_base = request.x;
3063 else if (request.align == ALIGN_LEFT)
3065 else if (request.align == ALIGN_RIGHT)
3066 sx_base = SX + SXSIZE;
3068 sx_base = SX + SXSIZE / 2;
3070 if (request.y != -1)
3071 sy_base = request.y;
3072 else if (request.valign == VALIGN_TOP)
3074 else if (request.valign == VALIGN_BOTTOM)
3075 sy_base = SY + SYSIZE;
3077 sy_base = SY + SYSIZE / 2;
3083 static void setRequestPositionExt(int *x, int *y, int width, int height,
3084 boolean add_border_size)
3086 int border_size = request.border_size;
3087 int sx_base, sy_base;
3090 setRequestBasePosition(&sx_base, &sy_base);
3092 if (request.align == ALIGN_LEFT)
3094 else if (request.align == ALIGN_RIGHT)
3095 sx = sx_base - width;
3097 sx = sx_base - width / 2;
3099 if (request.valign == VALIGN_TOP)
3101 else if (request.valign == VALIGN_BOTTOM)
3102 sy = sy_base - height;
3104 sy = sy_base - height / 2;
3106 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3107 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3109 if (add_border_size)
3119 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3121 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3124 static void DrawEnvelopeRequest(char *text)
3126 char *text_final = text;
3127 char *text_door_style = NULL;
3128 int graphic = IMG_BACKGROUND_REQUEST;
3129 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3130 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3131 int font_nr = FONT_REQUEST;
3132 int font_width = getFontWidth(font_nr);
3133 int font_height = getFontHeight(font_nr);
3134 int border_size = request.border_size;
3135 int line_spacing = request.line_spacing;
3136 int line_height = font_height + line_spacing;
3137 int max_text_width = request.width - 2 * border_size;
3138 int max_text_height = request.height - 2 * border_size;
3139 int line_length = max_text_width / font_width;
3140 int max_lines = max_text_height / line_height;
3141 int text_width = line_length * font_width;
3142 int width = request.width;
3143 int height = request.height;
3144 int tile_size = MAX(request.step_offset, 1);
3145 int x_steps = width / tile_size;
3146 int y_steps = height / tile_size;
3147 int sx_offset = border_size;
3148 int sy_offset = border_size;
3152 if (request.centered)
3153 sx_offset = (request.width - text_width) / 2;
3155 if (request.wrap_single_words && !request.autowrap)
3157 char *src_text_ptr, *dst_text_ptr;
3159 text_door_style = checked_malloc(2 * strlen(text) + 1);
3161 src_text_ptr = text;
3162 dst_text_ptr = text_door_style;
3164 while (*src_text_ptr)
3166 if (*src_text_ptr == ' ' ||
3167 *src_text_ptr == '?' ||
3168 *src_text_ptr == '!')
3169 *dst_text_ptr++ = '\n';
3171 if (*src_text_ptr != ' ')
3172 *dst_text_ptr++ = *src_text_ptr;
3177 *dst_text_ptr = '\0';
3179 text_final = text_door_style;
3182 setRequestPosition(&sx, &sy, FALSE);
3184 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3186 for (y = 0; y < y_steps; y++)
3187 for (x = 0; x < x_steps; x++)
3188 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3189 x, y, x_steps, y_steps,
3190 tile_size, tile_size);
3192 // force DOOR font inside door area
3193 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3195 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3196 line_length, -1, max_lines, line_spacing, mask_mode,
3197 request.autowrap, request.centered, FALSE);
3201 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3202 RedrawGadget(tool_gadget[i]);
3204 // store readily prepared envelope request for later use when animating
3205 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3207 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3209 if (text_door_style)
3210 free(text_door_style);
3213 static void AnimateEnvelopeRequest(int anim_mode, int action)
3215 int graphic = IMG_BACKGROUND_REQUEST;
3216 boolean draw_masked = graphic_info[graphic].draw_masked;
3217 int delay_value_normal = request.step_delay;
3218 int delay_value_fast = delay_value_normal / 2;
3219 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3220 boolean no_delay = (tape.warp_forward);
3221 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3222 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3223 DelayCounter anim_delay = { anim_delay_value };
3225 int tile_size = MAX(request.step_offset, 1);
3226 int max_xsize = request.width / tile_size;
3227 int max_ysize = request.height / tile_size;
3228 int max_xsize_inner = max_xsize - 2;
3229 int max_ysize_inner = max_ysize - 2;
3231 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3232 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3233 int xend = max_xsize_inner;
3234 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3235 int xstep = (xstart < xend ? 1 : 0);
3236 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3238 int end = MAX(xend - xstart, yend - ystart);
3241 if (setup.quick_doors)
3248 for (i = start; i <= end; i++)
3250 int last_frame = end; // last frame of this "for" loop
3251 int x = xstart + i * xstep;
3252 int y = ystart + i * ystep;
3253 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3254 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3255 int xsize_size_left = (xsize - 1) * tile_size;
3256 int ysize_size_top = (ysize - 1) * tile_size;
3257 int max_xsize_pos = (max_xsize - 1) * tile_size;
3258 int max_ysize_pos = (max_ysize - 1) * tile_size;
3259 int width = xsize * tile_size;
3260 int height = ysize * tile_size;
3265 setRequestPosition(&src_x, &src_y, FALSE);
3266 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3268 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3270 for (yy = 0; yy < 2; yy++)
3272 for (xx = 0; xx < 2; xx++)
3274 int src_xx = src_x + xx * max_xsize_pos;
3275 int src_yy = src_y + yy * max_ysize_pos;
3276 int dst_xx = dst_x + xx * xsize_size_left;
3277 int dst_yy = dst_y + yy * ysize_size_top;
3278 int xx_size = (xx ? tile_size : xsize_size_left);
3279 int yy_size = (yy ? tile_size : ysize_size_top);
3282 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3283 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3285 BlitBitmap(bitmap_db_store_2, backbuffer,
3286 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3290 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3292 redraw_mask |= REDRAW_FIELD;
3296 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3299 ClearAutoRepeatKeyEvents();
3302 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3304 int graphic = IMG_BACKGROUND_REQUEST;
3305 int sound_opening = SND_REQUEST_OPENING;
3306 int sound_closing = SND_REQUEST_CLOSING;
3307 int anim_mode_1 = request.anim_mode; // (higher priority)
3308 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3309 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3310 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3311 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3313 if (game_status == GAME_MODE_PLAYING)
3314 BlitScreenToBitmap(backbuffer);
3316 SetDrawtoField(DRAW_TO_BACKBUFFER);
3318 // SetDrawBackgroundMask(REDRAW_NONE);
3320 if (action == ACTION_OPENING)
3322 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3324 if (req_state & REQ_ASK)
3326 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3327 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3328 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3329 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3331 else if (req_state & REQ_CONFIRM)
3333 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3334 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3336 else if (req_state & REQ_PLAYER)
3338 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3339 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3340 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3341 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3344 DrawEnvelopeRequest(text);
3347 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3349 if (action == ACTION_OPENING)
3351 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3353 if (anim_mode == ANIM_DEFAULT)
3354 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3356 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3360 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3362 if (anim_mode != ANIM_NONE)
3363 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3365 if (anim_mode == ANIM_DEFAULT)
3366 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3369 game.envelope_active = FALSE;
3371 if (action == ACTION_CLOSING)
3372 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3374 // SetDrawBackgroundMask(last_draw_background_mask);
3376 redraw_mask |= REDRAW_FIELD;
3380 if (action == ACTION_CLOSING &&
3381 game_status == GAME_MODE_PLAYING &&
3382 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3383 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3386 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3388 if (IS_MM_WALL(element))
3390 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3396 int graphic = el2preimg(element);
3398 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3399 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3404 void DrawLevel(int draw_background_mask)
3408 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3409 SetDrawBackgroundMask(draw_background_mask);
3413 for (x = BX1; x <= BX2; x++)
3414 for (y = BY1; y <= BY2; y++)
3415 DrawScreenField(x, y);
3417 redraw_mask |= REDRAW_FIELD;
3420 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3425 for (x = 0; x < size_x; x++)
3426 for (y = 0; y < size_y; y++)
3427 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3429 redraw_mask |= REDRAW_FIELD;
3432 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3436 for (x = 0; x < size_x; x++)
3437 for (y = 0; y < size_y; y++)
3438 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3440 redraw_mask |= REDRAW_FIELD;
3443 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3445 boolean show_level_border = (BorderElement != EL_EMPTY);
3446 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3447 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3448 int tile_size = preview.tile_size;
3449 int preview_width = preview.xsize * tile_size;
3450 int preview_height = preview.ysize * tile_size;
3451 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3452 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3453 int real_preview_width = real_preview_xsize * tile_size;
3454 int real_preview_height = real_preview_ysize * tile_size;
3455 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3456 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3459 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3462 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3464 dst_x += (preview_width - real_preview_width) / 2;
3465 dst_y += (preview_height - real_preview_height) / 2;
3467 for (x = 0; x < real_preview_xsize; x++)
3469 for (y = 0; y < real_preview_ysize; y++)
3471 int lx = from_x + x + (show_level_border ? -1 : 0);
3472 int ly = from_y + y + (show_level_border ? -1 : 0);
3473 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3474 getBorderElement(lx, ly));
3476 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3477 element, tile_size);
3481 redraw_mask |= REDRAW_FIELD;
3484 #define MICROLABEL_EMPTY 0
3485 #define MICROLABEL_LEVEL_NAME 1
3486 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3487 #define MICROLABEL_LEVEL_AUTHOR 3
3488 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3489 #define MICROLABEL_IMPORTED_FROM 5
3490 #define MICROLABEL_IMPORTED_BY_HEAD 6
3491 #define MICROLABEL_IMPORTED_BY 7
3493 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3495 int max_text_width = SXSIZE;
3496 int font_width = getFontWidth(font_nr);
3498 if (pos->align == ALIGN_CENTER)
3499 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3500 else if (pos->align == ALIGN_RIGHT)
3501 max_text_width = pos->x;
3503 max_text_width = SXSIZE - pos->x;
3505 return max_text_width / font_width;
3508 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3510 char label_text[MAX_OUTPUT_LINESIZE + 1];
3511 int max_len_label_text;
3512 int font_nr = pos->font;
3515 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3518 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3519 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3520 mode == MICROLABEL_IMPORTED_BY_HEAD)
3521 font_nr = pos->font_alt;
3523 max_len_label_text = getMaxTextLength(pos, font_nr);
3525 if (pos->size != -1)
3526 max_len_label_text = pos->size;
3528 for (i = 0; i < max_len_label_text; i++)
3529 label_text[i] = ' ';
3530 label_text[max_len_label_text] = '\0';
3532 if (strlen(label_text) > 0)
3533 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3536 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3537 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3538 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3539 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3540 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3541 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3542 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3543 max_len_label_text);
3544 label_text[max_len_label_text] = '\0';
3546 if (strlen(label_text) > 0)
3547 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3549 redraw_mask |= REDRAW_FIELD;
3552 static void DrawPreviewLevelLabel(int mode)
3554 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3557 static void DrawPreviewLevelInfo(int mode)
3559 if (mode == MICROLABEL_LEVEL_NAME)
3560 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3561 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3562 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3565 static void DrawPreviewLevelExt(boolean restart)
3567 static DelayCounter scroll_delay = { 0 };
3568 static DelayCounter label_delay = { 0 };
3569 static int from_x, from_y, scroll_direction;
3570 static int label_state, label_counter;
3571 boolean show_level_border = (BorderElement != EL_EMPTY);
3572 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3573 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3575 scroll_delay.value = preview.step_delay;
3576 label_delay.value = MICROLEVEL_LABEL_DELAY;
3583 if (preview.anim_mode == ANIM_CENTERED)
3585 if (level_xsize > preview.xsize)
3586 from_x = (level_xsize - preview.xsize) / 2;
3587 if (level_ysize > preview.ysize)
3588 from_y = (level_ysize - preview.ysize) / 2;
3591 from_x += preview.xoffset;
3592 from_y += preview.yoffset;
3594 scroll_direction = MV_RIGHT;
3598 DrawPreviewLevelPlayfield(from_x, from_y);
3599 DrawPreviewLevelLabel(label_state);
3601 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3602 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3604 // initialize delay counters
3605 ResetDelayCounter(&scroll_delay);
3606 ResetDelayCounter(&label_delay);
3608 if (leveldir_current->name)
3610 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3611 char label_text[MAX_OUTPUT_LINESIZE + 1];
3612 int font_nr = pos->font;
3613 int max_len_label_text = getMaxTextLength(pos, font_nr);
3615 if (pos->size != -1)
3616 max_len_label_text = pos->size;
3618 strncpy(label_text, leveldir_current->name, max_len_label_text);
3619 label_text[max_len_label_text] = '\0';
3621 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3622 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3628 // scroll preview level, if needed
3629 if (preview.anim_mode != ANIM_NONE &&
3630 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3631 DelayReached(&scroll_delay))
3633 switch (scroll_direction)
3638 from_x -= preview.step_offset;
3639 from_x = (from_x < 0 ? 0 : from_x);
3642 scroll_direction = MV_UP;
3646 if (from_x < level_xsize - preview.xsize)
3648 from_x += preview.step_offset;
3649 from_x = (from_x > level_xsize - preview.xsize ?
3650 level_xsize - preview.xsize : from_x);
3653 scroll_direction = MV_DOWN;
3659 from_y -= preview.step_offset;
3660 from_y = (from_y < 0 ? 0 : from_y);
3663 scroll_direction = MV_RIGHT;
3667 if (from_y < level_ysize - preview.ysize)
3669 from_y += preview.step_offset;
3670 from_y = (from_y > level_ysize - preview.ysize ?
3671 level_ysize - preview.ysize : from_y);
3674 scroll_direction = MV_LEFT;
3681 DrawPreviewLevelPlayfield(from_x, from_y);
3684 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3685 // redraw micro level label, if needed
3686 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3687 !strEqual(level.author, ANONYMOUS_NAME) &&
3688 !strEqual(level.author, leveldir_current->name) &&
3689 DelayReached(&label_delay))
3691 int max_label_counter = 23;
3693 if (leveldir_current->imported_from != NULL &&
3694 strlen(leveldir_current->imported_from) > 0)
3695 max_label_counter += 14;
3696 if (leveldir_current->imported_by != NULL &&
3697 strlen(leveldir_current->imported_by) > 0)
3698 max_label_counter += 14;
3700 label_counter = (label_counter + 1) % max_label_counter;
3701 label_state = (label_counter >= 0 && label_counter <= 7 ?
3702 MICROLABEL_LEVEL_NAME :
3703 label_counter >= 9 && label_counter <= 12 ?
3704 MICROLABEL_LEVEL_AUTHOR_HEAD :
3705 label_counter >= 14 && label_counter <= 21 ?
3706 MICROLABEL_LEVEL_AUTHOR :
3707 label_counter >= 23 && label_counter <= 26 ?
3708 MICROLABEL_IMPORTED_FROM_HEAD :
3709 label_counter >= 28 && label_counter <= 35 ?
3710 MICROLABEL_IMPORTED_FROM :
3711 label_counter >= 37 && label_counter <= 40 ?
3712 MICROLABEL_IMPORTED_BY_HEAD :
3713 label_counter >= 42 && label_counter <= 49 ?
3714 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3716 if (leveldir_current->imported_from == NULL &&
3717 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3718 label_state == MICROLABEL_IMPORTED_FROM))
3719 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3720 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3722 DrawPreviewLevelLabel(label_state);
3726 void DrawPreviewPlayers(void)
3728 if (game_status != GAME_MODE_MAIN)
3731 // do not draw preview players if level preview redefined, but players aren't
3732 if (preview.redefined && !menu.main.preview_players.redefined)
3735 boolean player_found[MAX_PLAYERS];
3736 int num_players = 0;
3739 for (i = 0; i < MAX_PLAYERS; i++)
3740 player_found[i] = FALSE;
3742 // check which players can be found in the level (simple approach)
3743 for (x = 0; x < lev_fieldx; x++)
3745 for (y = 0; y < lev_fieldy; y++)
3747 int element = level.field[x][y];
3749 if (IS_PLAYER_ELEMENT(element))
3751 int player_nr = GET_PLAYER_NR(element);
3753 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3755 if (!player_found[player_nr])
3758 player_found[player_nr] = TRUE;
3763 struct TextPosInfo *pos = &menu.main.preview_players;
3764 int tile_size = pos->tile_size;
3765 int border_size = pos->border_size;
3766 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3767 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3768 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3769 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3770 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3771 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3772 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3773 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3774 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3775 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3776 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3777 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3779 // clear area in which the players will be drawn
3780 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3781 max_players_width, max_players_height);
3783 if (!network.enabled && !setup.team_mode)
3786 // only draw players if level is suited for team mode
3787 if (num_players < 2)
3790 // draw all players that were found in the level
3791 for (i = 0; i < MAX_PLAYERS; i++)
3793 if (player_found[i])
3795 int graphic = el2img(EL_PLAYER_1 + i);
3797 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3799 xpos += player_xoffset;
3800 ypos += player_yoffset;
3805 void DrawPreviewLevelInitial(void)
3807 DrawPreviewLevelExt(TRUE);
3808 DrawPreviewPlayers();
3811 void DrawPreviewLevelAnimation(void)
3813 DrawPreviewLevelExt(FALSE);
3816 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3817 int border_size, int font_nr)
3819 int graphic = el2img(EL_PLAYER_1 + player_nr);
3820 int font_height = getFontHeight(font_nr);
3821 int player_height = MAX(tile_size, font_height);
3822 int xoffset_text = tile_size + border_size;
3823 int yoffset_text = (player_height - font_height) / 2;
3824 int yoffset_graphic = (player_height - tile_size) / 2;
3825 char *player_name = getNetworkPlayerName(player_nr + 1);
3827 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3829 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3832 static void DrawNetworkPlayersExt(boolean force)
3834 if (game_status != GAME_MODE_MAIN)
3837 if (!network.connected && !force)
3840 // do not draw network players if level preview redefined, but players aren't
3841 if (preview.redefined && !menu.main.network_players.redefined)
3844 int num_players = 0;
3847 for (i = 0; i < MAX_PLAYERS; i++)
3848 if (stored_player[i].connected_network)
3851 struct TextPosInfo *pos = &menu.main.network_players;
3852 int tile_size = pos->tile_size;
3853 int border_size = pos->border_size;
3854 int xoffset_text = tile_size + border_size;
3855 int font_nr = pos->font;
3856 int font_width = getFontWidth(font_nr);
3857 int font_height = getFontHeight(font_nr);
3858 int player_height = MAX(tile_size, font_height);
3859 int player_yoffset = player_height + border_size;
3860 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3861 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3862 int all_players_height = num_players * player_yoffset - border_size;
3863 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3864 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3865 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3867 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3868 max_players_width, max_players_height);
3870 // first draw local network player ...
3871 for (i = 0; i < MAX_PLAYERS; i++)
3873 if (stored_player[i].connected_network &&
3874 stored_player[i].connected_locally)
3876 char *player_name = getNetworkPlayerName(i + 1);
3877 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3878 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3880 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3882 ypos += player_yoffset;
3886 // ... then draw all other network players
3887 for (i = 0; i < MAX_PLAYERS; i++)
3889 if (stored_player[i].connected_network &&
3890 !stored_player[i].connected_locally)
3892 char *player_name = getNetworkPlayerName(i + 1);
3893 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3894 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3896 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3898 ypos += player_yoffset;
3903 void DrawNetworkPlayers(void)
3905 DrawNetworkPlayersExt(FALSE);
3908 void ClearNetworkPlayers(void)
3910 DrawNetworkPlayersExt(TRUE);
3913 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3914 int graphic, int lx, int ly,
3917 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3919 if (mask_mode == USE_MASKING)
3920 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3922 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3925 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3926 int graphic, int sync_frame, int mask_mode)
3928 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3930 if (mask_mode == USE_MASKING)
3931 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3933 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3936 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3937 int graphic, int sync_frame, int tilesize,
3940 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3942 if (mask_mode == USE_MASKING)
3943 DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
3945 DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
3948 static void DrawGraphicAnimation(int x, int y, int graphic)
3950 int lx = LEVELX(x), ly = LEVELY(y);
3951 int mask_mode = NO_MASKING;
3953 if (!IN_SCR_FIELD(x, y))
3956 if (game.use_masked_elements)
3958 if (Tile[lx][ly] != EL_EMPTY)
3960 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3962 mask_mode = USE_MASKING;
3966 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3967 graphic, lx, ly, mask_mode);
3969 MarkTileDirty(x, y);
3972 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3974 int lx = LEVELX(x), ly = LEVELY(y);
3975 int mask_mode = NO_MASKING;
3977 if (!IN_SCR_FIELD(x, y))
3980 if (game.use_masked_elements)
3982 if (Tile[lx][ly] != EL_EMPTY)
3984 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3986 mask_mode = USE_MASKING;
3990 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3991 graphic, lx, ly, mask_mode);
3993 MarkTileDirty(x, y);
3996 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3998 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4001 void DrawLevelElementAnimation(int x, int y, int element)
4003 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4005 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4008 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
4010 int sx = SCREENX(x), sy = SCREENY(y);
4012 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4015 if (Tile[x][y] == EL_EMPTY)
4016 graphic = el2img(GfxElementEmpty[x][y]);
4018 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4021 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4024 DrawGraphicAnimation(sx, sy, graphic);
4027 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4028 DrawLevelFieldCrumbled(x, y);
4030 if (GFX_CRUMBLED(Tile[x][y]))
4031 DrawLevelFieldCrumbled(x, y);
4035 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4037 int sx = SCREENX(x), sy = SCREENY(y);
4040 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4043 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4045 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4048 DrawGraphicAnimation(sx, sy, graphic);
4050 if (GFX_CRUMBLED(element))
4051 DrawLevelFieldCrumbled(x, y);
4054 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4056 if (player->use_murphy)
4058 // this works only because currently only one player can be "murphy" ...
4059 static int last_horizontal_dir = MV_LEFT;
4060 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4062 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4063 last_horizontal_dir = move_dir;
4065 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4067 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4069 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4075 return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4078 static boolean equalGraphics(int graphic1, int graphic2)
4080 struct GraphicInfo *g1 = &graphic_info[graphic1];
4081 struct GraphicInfo *g2 = &graphic_info[graphic2];
4083 return (g1->bitmap == g2->bitmap &&
4084 g1->src_x == g2->src_x &&
4085 g1->src_y == g2->src_y &&
4086 g1->anim_frames == g2->anim_frames &&
4087 g1->anim_delay == g2->anim_delay &&
4088 g1->anim_mode == g2->anim_mode);
4091 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4095 DRAW_PLAYER_STAGE_INIT = 0,
4096 DRAW_PLAYER_STAGE_LAST_FIELD,
4097 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4098 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4099 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4100 DRAW_PLAYER_STAGE_PLAYER,
4102 DRAW_PLAYER_STAGE_PLAYER,
4103 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4105 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4106 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4108 NUM_DRAW_PLAYER_STAGES
4111 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4113 static int static_last_player_graphic[MAX_PLAYERS];
4114 static int static_last_player_frame[MAX_PLAYERS];
4115 static boolean static_player_is_opaque[MAX_PLAYERS];
4116 static boolean draw_player[MAX_PLAYERS];
4117 int pnr = player->index_nr;
4119 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4121 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4122 static_last_player_frame[pnr] = player->Frame;
4123 static_player_is_opaque[pnr] = FALSE;
4125 draw_player[pnr] = TRUE;
4128 if (!draw_player[pnr])
4132 if (!IN_LEV_FIELD(player->jx, player->jy))
4134 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4135 Debug("draw:DrawPlayerExt", "This should never happen!");
4137 draw_player[pnr] = FALSE;
4143 int last_player_graphic = static_last_player_graphic[pnr];
4144 int last_player_frame = static_last_player_frame[pnr];
4145 boolean player_is_opaque = static_player_is_opaque[pnr];
4147 int jx = player->jx;
4148 int jy = player->jy;
4149 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4150 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4151 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4152 int last_jx = (player->is_moving ? jx - dx : jx);
4153 int last_jy = (player->is_moving ? jy - dy : jy);
4154 int next_jx = jx + dx;
4155 int next_jy = jy + dy;
4156 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4157 int sx = SCREENX(jx);
4158 int sy = SCREENY(jy);
4159 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4160 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4161 int element = Tile[jx][jy];
4162 int last_element = Tile[last_jx][last_jy];
4163 int action = (player->is_pushing ? ACTION_PUSHING :
4164 player->is_digging ? ACTION_DIGGING :
4165 player->is_collecting ? ACTION_COLLECTING :
4166 player->is_moving ? ACTION_MOVING :
4167 player->is_snapping ? ACTION_SNAPPING :
4168 player->is_dropping ? ACTION_DROPPING :
4169 player->is_waiting ? player->action_waiting :
4172 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4174 // ------------------------------------------------------------------------
4175 // initialize drawing the player
4176 // ------------------------------------------------------------------------
4178 draw_player[pnr] = FALSE;
4180 // GfxElement[][] is set to the element the player is digging or collecting;
4181 // remove also for off-screen player if the player is not moving anymore
4182 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4183 GfxElement[jx][jy] = EL_UNDEFINED;
4185 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4188 if (element == EL_EXPLOSION)
4191 InitPlayerGfxAnimation(player, action, move_dir);
4193 draw_player[pnr] = TRUE;
4195 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4197 // ------------------------------------------------------------------------
4198 // draw things in the field the player is leaving, if needed
4199 // ------------------------------------------------------------------------
4201 if (!IN_SCR_FIELD(sx, sy))
4202 draw_player[pnr] = FALSE;
4204 if (!player->is_moving)
4207 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4209 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4211 if (last_element == EL_DYNAMITE_ACTIVE ||
4212 last_element == EL_EM_DYNAMITE_ACTIVE ||
4213 last_element == EL_SP_DISK_RED_ACTIVE)
4214 DrawDynamite(last_jx, last_jy);
4216 DrawLevelFieldThruMask(last_jx, last_jy);
4218 else if (last_element == EL_DYNAMITE_ACTIVE ||
4219 last_element == EL_EM_DYNAMITE_ACTIVE ||
4220 last_element == EL_SP_DISK_RED_ACTIVE)
4221 DrawDynamite(last_jx, last_jy);
4223 DrawLevelField(last_jx, last_jy);
4225 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4227 // ------------------------------------------------------------------------
4228 // draw things behind the player, if needed
4229 // ------------------------------------------------------------------------
4233 DrawLevelElement(jx, jy, Back[jx][jy]);
4238 if (IS_ACTIVE_BOMB(element))
4240 DrawLevelElement(jx, jy, EL_EMPTY);
4245 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4247 int old_element = GfxElement[jx][jy];
4248 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4249 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4251 if (GFX_CRUMBLED(old_element))
4252 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4254 DrawScreenGraphic(sx, sy, old_graphic, frame);
4256 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4257 static_player_is_opaque[pnr] = TRUE;
4261 GfxElement[jx][jy] = EL_UNDEFINED;
4263 // make sure that pushed elements are drawn with correct frame rate
4264 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4266 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4267 GfxFrame[jx][jy] = player->StepFrame;
4269 DrawLevelField(jx, jy);
4272 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4274 // ------------------------------------------------------------------------
4275 // draw things the player is pushing, if needed
4276 // ------------------------------------------------------------------------
4278 if (!player->is_pushing || !player->is_moving)
4281 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4284 int gfx_frame = GfxFrame[jx][jy];
4286 if (!IS_MOVING(jx, jy)) // push movement already finished
4288 element = Tile[next_jx][next_jy];
4289 gfx_frame = GfxFrame[next_jx][next_jy];
4292 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4293 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4294 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4296 // draw background element under pushed element (like the Sokoban field)
4297 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4299 // this allows transparent pushing animation over non-black background
4302 DrawLevelElement(jx, jy, Back[jx][jy]);
4304 DrawLevelElement(jx, jy, EL_EMPTY);
4307 if (Back[next_jx][next_jy])
4308 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4310 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4312 int px = SCREENX(jx), py = SCREENY(jy);
4313 int pxx = (TILEX - ABS(sxx)) * dx;
4314 int pyy = (TILEY - ABS(syy)) * dy;
4317 // do not draw (EM style) pushing animation when pushing is finished
4318 // (two-tile animations usually do not contain start and end frame)
4319 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4320 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4322 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4324 // masked drawing is needed for EMC style (double) movement graphics
4325 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4326 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4329 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4331 // ------------------------------------------------------------------------
4332 // draw player himself
4333 // ------------------------------------------------------------------------
4335 int graphic = getPlayerGraphic(player, move_dir);
4337 // in the case of changed player action or direction, prevent the current
4338 // animation frame from being restarted for identical animations
4339 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4340 player->Frame = last_player_frame;
4342 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4344 if (player_is_opaque)
4345 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4347 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4349 if (SHIELD_ON(player))
4351 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4352 IMG_SHIELD_NORMAL_ACTIVE);
4353 frame = getGraphicAnimationFrame(graphic, -1);
4355 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4358 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4360 // ------------------------------------------------------------------------
4361 // draw things in front of player (active dynamite or dynabombs)
4362 // ------------------------------------------------------------------------
4364 if (IS_ACTIVE_BOMB(element))
4366 int graphic = el2img(element);
4367 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4369 if (game.emulation == EMU_SUPAPLEX)
4370 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4372 DrawGraphicThruMask(sx, sy, graphic, frame);
4375 if (player_is_moving && last_element == EL_EXPLOSION)
4377 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4378 GfxElement[last_jx][last_jy] : EL_EMPTY);
4379 int graphic = el_act2img(element, ACTION_EXPLODING);
4380 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4381 int phase = ExplodePhase[last_jx][last_jy] - 1;
4382 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4385 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4388 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4390 // ------------------------------------------------------------------------
4391 // draw elements the player is just walking/passing through/under
4392 // ------------------------------------------------------------------------
4394 if (player_is_moving)
4396 // handle the field the player is leaving ...
4397 if (IS_ACCESSIBLE_INSIDE(last_element))
4398 DrawLevelField(last_jx, last_jy);
4399 else if (IS_ACCESSIBLE_UNDER(last_element))
4400 DrawLevelFieldThruMask(last_jx, last_jy);
4403 // do not redraw accessible elements if the player is just pushing them
4404 if (!player_is_moving || !player->is_pushing)
4406 // ... and the field the player is entering
4407 if (IS_ACCESSIBLE_INSIDE(element))
4408 DrawLevelField(jx, jy);
4409 else if (IS_ACCESSIBLE_UNDER(element))
4410 DrawLevelFieldThruMask(jx, jy);
4413 MarkTileDirty(sx, sy);
4417 void DrawPlayer(struct PlayerInfo *player)
4421 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4422 DrawPlayerExt(player, i);
4425 void DrawAllPlayers(void)
4429 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4430 for (j = 0; j < MAX_PLAYERS; j++)
4431 if (stored_player[j].active)
4432 DrawPlayerExt(&stored_player[j], i);
4435 void DrawPlayerField(int x, int y)
4437 if (!IS_PLAYER(x, y))
4440 DrawPlayer(PLAYERINFO(x, y));
4443 // ----------------------------------------------------------------------------
4445 void WaitForEventToContinue(void)
4447 boolean first_wait = TRUE;
4448 boolean still_wait = TRUE;
4450 if (program.headless)
4453 // simulate releasing mouse button over last gadget, if still pressed
4455 HandleGadgets(-1, -1, 0);
4457 button_status = MB_RELEASED;
4460 ClearPlayerAction();
4466 if (NextValidEvent(&event))
4470 case EVENT_BUTTONPRESS:
4471 case EVENT_FINGERPRESS:
4475 case EVENT_BUTTONRELEASE:
4476 case EVENT_FINGERRELEASE:
4477 still_wait = first_wait;
4480 case EVENT_KEYPRESS:
4481 case SDL_CONTROLLERBUTTONDOWN:
4482 case SDL_JOYBUTTONDOWN:
4487 HandleOtherEvents(&event);
4491 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4496 if (!PendingEvent())
4501 #define MAX_REQUEST_LINES 13
4502 #define MAX_REQUEST_LINE_FONT1_LEN 7
4503 #define MAX_REQUEST_LINE_FONT2_LEN 10
4505 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4507 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4509 int draw_buffer_last = GetDrawtoField();
4510 int width = request.width;
4511 int height = request.height;
4515 // when showing request dialog after game ended, deactivate game panel
4516 if (game_just_ended)
4517 game.panel.active = FALSE;
4519 game.request_active = TRUE;
4521 setRequestPosition(&sx, &sy, FALSE);
4523 button_status = MB_RELEASED;
4525 request_gadget_id = -1;
4530 boolean event_handled = FALSE;
4532 if (game_just_ended)
4534 SetDrawtoField(draw_buffer_game);
4536 HandleGameActions();
4538 SetDrawtoField(DRAW_TO_BACKBUFFER);
4540 if (global.use_envelope_request)
4542 // copy current state of request area to middle of playfield area
4543 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4551 while (NextValidEvent(&event))
4553 event_handled = TRUE;
4557 case EVENT_BUTTONPRESS:
4558 case EVENT_BUTTONRELEASE:
4559 case EVENT_MOTIONNOTIFY:
4563 if (event.type == EVENT_MOTIONNOTIFY)
4568 motion_status = TRUE;
4569 mx = ((MotionEvent *) &event)->x;
4570 my = ((MotionEvent *) &event)->y;
4574 motion_status = FALSE;
4575 mx = ((ButtonEvent *) &event)->x;
4576 my = ((ButtonEvent *) &event)->y;
4577 if (event.type == EVENT_BUTTONPRESS)
4578 button_status = ((ButtonEvent *) &event)->button;
4580 button_status = MB_RELEASED;
4583 // this sets 'request_gadget_id'
4584 HandleGadgets(mx, my, button_status);
4586 switch (request_gadget_id)
4588 case TOOL_CTRL_ID_YES:
4589 case TOOL_CTRL_ID_TOUCH_YES:
4592 case TOOL_CTRL_ID_NO:
4593 case TOOL_CTRL_ID_TOUCH_NO:
4596 case TOOL_CTRL_ID_CONFIRM:
4597 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4598 result = TRUE | FALSE;
4601 case TOOL_CTRL_ID_PLAYER_1:
4604 case TOOL_CTRL_ID_PLAYER_2:
4607 case TOOL_CTRL_ID_PLAYER_3:
4610 case TOOL_CTRL_ID_PLAYER_4:
4615 // only check clickable animations if no request gadget clicked
4616 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4623 case SDL_WINDOWEVENT:
4624 HandleWindowEvent((WindowEvent *) &event);
4627 case SDL_APP_WILLENTERBACKGROUND:
4628 case SDL_APP_DIDENTERBACKGROUND:
4629 case SDL_APP_WILLENTERFOREGROUND:
4630 case SDL_APP_DIDENTERFOREGROUND:
4631 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4634 case EVENT_KEYPRESS:
4636 Key key = GetEventKey((KeyEvent *)&event);
4641 if (req_state & REQ_CONFIRM)
4650 #if defined(KSYM_Rewind)
4651 case KSYM_Rewind: // for Amazon Fire TV remote
4660 #if defined(KSYM_FastForward)
4661 case KSYM_FastForward: // for Amazon Fire TV remote
4667 HandleKeysDebug(key, KEY_PRESSED);
4671 if (req_state & REQ_PLAYER)
4673 int old_player_nr = setup.network_player_nr;
4676 result = old_player_nr + 1;
4681 result = old_player_nr + 1;
4712 case EVENT_FINGERRELEASE:
4713 case EVENT_KEYRELEASE:
4714 ClearPlayerAction();
4717 case SDL_CONTROLLERBUTTONDOWN:
4718 switch (event.cbutton.button)
4720 case SDL_CONTROLLER_BUTTON_A:
4721 case SDL_CONTROLLER_BUTTON_X:
4722 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4723 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4727 case SDL_CONTROLLER_BUTTON_B:
4728 case SDL_CONTROLLER_BUTTON_Y:
4729 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4730 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4731 case SDL_CONTROLLER_BUTTON_BACK:
4736 if (req_state & REQ_PLAYER)
4738 int old_player_nr = setup.network_player_nr;
4741 result = old_player_nr + 1;
4743 switch (event.cbutton.button)
4745 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4746 case SDL_CONTROLLER_BUTTON_Y:
4750 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4751 case SDL_CONTROLLER_BUTTON_B:
4755 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4756 case SDL_CONTROLLER_BUTTON_A:
4760 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4761 case SDL_CONTROLLER_BUTTON_X:
4772 case SDL_CONTROLLERBUTTONUP:
4773 HandleJoystickEvent(&event);
4774 ClearPlayerAction();
4778 HandleOtherEvents(&event);
4783 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4785 int joy = AnyJoystick();
4787 if (joy & JOY_BUTTON_1)
4789 else if (joy & JOY_BUTTON_2)
4792 else if (AnyJoystick())
4794 int joy = AnyJoystick();
4796 if (req_state & REQ_PLAYER)
4800 else if (joy & JOY_RIGHT)
4802 else if (joy & JOY_DOWN)
4804 else if (joy & JOY_LEFT)
4811 if (game_just_ended)
4813 if (global.use_envelope_request)
4815 // copy back current state of pressed buttons inside request area
4816 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4820 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4826 SetDrawtoField(draw_buffer_last);
4828 game.request_active = FALSE;
4833 static boolean RequestDoor(char *text, unsigned int req_state)
4835 int draw_buffer_last = GetDrawtoField();
4836 unsigned int old_door_state;
4837 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4838 int font_nr = FONT_TEXT_2;
4843 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4845 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4846 font_nr = FONT_TEXT_1;
4849 if (game_status == GAME_MODE_PLAYING)
4850 BlitScreenToBitmap(backbuffer);
4852 // disable deactivated drawing when quick-loading level tape recording
4853 if (tape.playing && tape.deactivate_display)
4854 TapeDeactivateDisplayOff(TRUE);
4856 SetMouseCursor(CURSOR_DEFAULT);
4858 // pause network game while waiting for request to answer
4859 if (network.enabled &&
4860 game_status == GAME_MODE_PLAYING &&
4861 !game.all_players_gone &&
4862 req_state & REQUEST_WAIT_FOR_INPUT)
4863 SendToServer_PausePlaying();
4865 old_door_state = GetDoorState();
4867 // simulate releasing mouse button over last gadget, if still pressed
4869 HandleGadgets(-1, -1, 0);
4873 // draw released gadget before proceeding
4876 if (old_door_state & DOOR_OPEN_1)
4878 CloseDoor(DOOR_CLOSE_1);
4880 // save old door content
4881 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4882 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4885 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4886 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4888 // clear door drawing field
4889 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4891 // force DOOR font inside door area
4892 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4894 // write text for request
4895 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4897 char text_line[max_request_line_len + 1];
4903 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4905 tc = *(text_ptr + tx);
4906 // if (!tc || tc == ' ')
4907 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4911 if ((tc == '?' || tc == '!') && tl == 0)
4921 strncpy(text_line, text_ptr, tl);
4924 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4925 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4926 text_line, font_nr);
4928 text_ptr += tl + (tc == ' ' ? 1 : 0);
4929 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4934 if (req_state & REQ_ASK)
4936 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4937 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4938 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4939 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4941 else if (req_state & REQ_CONFIRM)
4943 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4944 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4946 else if (req_state & REQ_PLAYER)
4948 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4949 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4950 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4951 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4954 // copy request gadgets to door backbuffer
4955 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4957 OpenDoor(DOOR_OPEN_1);
4959 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4961 if (game_status == GAME_MODE_PLAYING)
4963 SetPanelBackground();
4964 SetDrawBackgroundMask(REDRAW_DOOR_1);
4968 SetDrawBackgroundMask(REDRAW_FIELD);
4974 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4976 // ---------- handle request buttons ----------
4977 result = RequestHandleEvents(req_state, draw_buffer_last);
4981 if (!(req_state & REQ_STAY_OPEN))
4983 CloseDoor(DOOR_CLOSE_1);
4985 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4986 (req_state & REQ_REOPEN))
4987 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4992 if (game_status == GAME_MODE_PLAYING)
4994 SetPanelBackground();
4995 SetDrawBackgroundMask(REDRAW_DOOR_1);
4999 SetDrawBackgroundMask(REDRAW_FIELD);
5002 // continue network game after request
5003 if (network.enabled &&
5004 game_status == GAME_MODE_PLAYING &&
5005 !game.all_players_gone &&
5006 req_state & REQUEST_WAIT_FOR_INPUT)
5007 SendToServer_ContinuePlaying();
5009 // restore deactivated drawing when quick-loading level tape recording
5010 if (tape.playing && tape.deactivate_display)
5011 TapeDeactivateDisplayOn();
5016 static boolean RequestEnvelope(char *text, unsigned int req_state)
5018 int draw_buffer_last = GetDrawtoField();
5021 if (game_status == GAME_MODE_PLAYING)
5022 BlitScreenToBitmap(backbuffer);
5024 // disable deactivated drawing when quick-loading level tape recording
5025 if (tape.playing && tape.deactivate_display)
5026 TapeDeactivateDisplayOff(TRUE);
5028 SetMouseCursor(CURSOR_DEFAULT);
5030 // pause network game while waiting for request to answer
5031 if (network.enabled &&
5032 game_status == GAME_MODE_PLAYING &&
5033 !game.all_players_gone &&
5034 req_state & REQUEST_WAIT_FOR_INPUT)
5035 SendToServer_PausePlaying();
5037 // simulate releasing mouse button over last gadget, if still pressed
5039 HandleGadgets(-1, -1, 0);
5043 // (replace with setting corresponding request background)
5044 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5045 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5047 // clear door drawing field
5048 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5050 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5052 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5054 if (game_status == GAME_MODE_PLAYING)
5056 SetPanelBackground();
5057 SetDrawBackgroundMask(REDRAW_DOOR_1);
5061 SetDrawBackgroundMask(REDRAW_FIELD);
5067 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5069 // ---------- handle request buttons ----------
5070 result = RequestHandleEvents(req_state, draw_buffer_last);
5074 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5078 if (game_status == GAME_MODE_PLAYING)
5080 SetPanelBackground();
5081 SetDrawBackgroundMask(REDRAW_DOOR_1);
5085 SetDrawBackgroundMask(REDRAW_FIELD);
5088 // continue network game after request
5089 if (network.enabled &&
5090 game_status == GAME_MODE_PLAYING &&
5091 !game.all_players_gone &&
5092 req_state & REQUEST_WAIT_FOR_INPUT)
5093 SendToServer_ContinuePlaying();
5095 // restore deactivated drawing when quick-loading level tape recording
5096 if (tape.playing && tape.deactivate_display)
5097 TapeDeactivateDisplayOn();
5102 boolean Request(char *text, unsigned int req_state)
5104 boolean overlay_enabled = GetOverlayEnabled();
5107 game.request_active_or_moving = TRUE;
5109 SetOverlayEnabled(FALSE);
5111 if (global.use_envelope_request)
5112 result = RequestEnvelope(text, req_state);
5114 result = RequestDoor(text, req_state);
5116 SetOverlayEnabled(overlay_enabled);
5118 game.request_active_or_moving = FALSE;
5123 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5125 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5126 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5129 if (dpo1->sort_priority != dpo2->sort_priority)
5130 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5132 compare_result = dpo1->nr - dpo2->nr;
5134 return compare_result;
5137 void InitGraphicCompatibilityInfo_Doors(void)
5143 struct DoorInfo *door;
5147 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5148 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5150 { -1, -1, -1, NULL }
5152 struct Rect door_rect_list[] =
5154 { DX, DY, DXSIZE, DYSIZE },
5155 { VX, VY, VXSIZE, VYSIZE }
5159 for (i = 0; doors[i].door_token != -1; i++)
5161 int door_token = doors[i].door_token;
5162 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5163 int part_1 = doors[i].part_1;
5164 int part_8 = doors[i].part_8;
5165 int part_2 = part_1 + 1;
5166 int part_3 = part_1 + 2;
5167 struct DoorInfo *door = doors[i].door;
5168 struct Rect *door_rect = &door_rect_list[door_index];
5169 boolean door_gfx_redefined = FALSE;
5171 // check if any door part graphic definitions have been redefined
5173 for (j = 0; door_part_controls[j].door_token != -1; j++)
5175 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5176 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5178 if (dpc->door_token == door_token && fi->redefined)
5179 door_gfx_redefined = TRUE;
5182 // check for old-style door graphic/animation modifications
5184 if (!door_gfx_redefined)
5186 if (door->anim_mode & ANIM_STATIC_PANEL)
5188 door->panel.step_xoffset = 0;
5189 door->panel.step_yoffset = 0;
5192 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5194 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5195 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5196 int num_door_steps, num_panel_steps;
5198 // remove door part graphics other than the two default wings
5200 for (j = 0; door_part_controls[j].door_token != -1; j++)
5202 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5203 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5205 if (dpc->graphic >= part_3 &&
5206 dpc->graphic <= part_8)
5210 // set graphics and screen positions of the default wings
5212 g_part_1->width = door_rect->width;
5213 g_part_1->height = door_rect->height;
5214 g_part_2->width = door_rect->width;
5215 g_part_2->height = door_rect->height;
5216 g_part_2->src_x = door_rect->width;
5217 g_part_2->src_y = g_part_1->src_y;
5219 door->part_2.x = door->part_1.x;
5220 door->part_2.y = door->part_1.y;
5222 if (door->width != -1)
5224 g_part_1->width = door->width;
5225 g_part_2->width = door->width;
5227 // special treatment for graphics and screen position of right wing
5228 g_part_2->src_x += door_rect->width - door->width;
5229 door->part_2.x += door_rect->width - door->width;
5232 if (door->height != -1)
5234 g_part_1->height = door->height;
5235 g_part_2->height = door->height;
5237 // special treatment for graphics and screen position of bottom wing
5238 g_part_2->src_y += door_rect->height - door->height;
5239 door->part_2.y += door_rect->height - door->height;
5242 // set animation delays for the default wings and panels
5244 door->part_1.step_delay = door->step_delay;
5245 door->part_2.step_delay = door->step_delay;
5246 door->panel.step_delay = door->step_delay;
5248 // set animation draw order for the default wings
5250 door->part_1.sort_priority = 2; // draw left wing over ...
5251 door->part_2.sort_priority = 1; // ... right wing
5253 // set animation draw offset for the default wings
5255 if (door->anim_mode & ANIM_HORIZONTAL)
5257 door->part_1.step_xoffset = door->step_offset;
5258 door->part_1.step_yoffset = 0;
5259 door->part_2.step_xoffset = door->step_offset * -1;
5260 door->part_2.step_yoffset = 0;
5262 num_door_steps = g_part_1->width / door->step_offset;
5264 else // ANIM_VERTICAL
5266 door->part_1.step_xoffset = 0;
5267 door->part_1.step_yoffset = door->step_offset;
5268 door->part_2.step_xoffset = 0;
5269 door->part_2.step_yoffset = door->step_offset * -1;
5271 num_door_steps = g_part_1->height / door->step_offset;
5274 // set animation draw offset for the default panels
5276 if (door->step_offset > 1)
5278 num_panel_steps = 2 * door_rect->height / door->step_offset;
5279 door->panel.start_step = num_panel_steps - num_door_steps;
5280 door->panel.start_step_closing = door->panel.start_step;
5284 num_panel_steps = door_rect->height / door->step_offset;
5285 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5286 door->panel.start_step_closing = door->panel.start_step;
5287 door->panel.step_delay *= 2;
5294 void InitDoors(void)
5298 for (i = 0; door_part_controls[i].door_token != -1; i++)
5300 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5301 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5303 // initialize "start_step_opening" and "start_step_closing", if needed
5304 if (dpc->pos->start_step_opening == 0 &&
5305 dpc->pos->start_step_closing == 0)
5307 // dpc->pos->start_step_opening = dpc->pos->start_step;
5308 dpc->pos->start_step_closing = dpc->pos->start_step;
5311 // fill structure for door part draw order (sorted below)
5313 dpo->sort_priority = dpc->pos->sort_priority;
5316 // sort door part controls according to sort_priority and graphic number
5317 qsort(door_part_order, MAX_DOOR_PARTS,
5318 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5321 unsigned int OpenDoor(unsigned int door_state)
5323 if (door_state & DOOR_COPY_BACK)
5325 if (door_state & DOOR_OPEN_1)
5326 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5327 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5329 if (door_state & DOOR_OPEN_2)
5330 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5331 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5333 door_state &= ~DOOR_COPY_BACK;
5336 return MoveDoor(door_state);
5339 unsigned int CloseDoor(unsigned int door_state)
5341 unsigned int old_door_state = GetDoorState();
5343 if (!(door_state & DOOR_NO_COPY_BACK))
5345 if (old_door_state & DOOR_OPEN_1)
5346 BlitBitmap(backbuffer, bitmap_db_door_1,
5347 DX, DY, DXSIZE, DYSIZE, 0, 0);
5349 if (old_door_state & DOOR_OPEN_2)
5350 BlitBitmap(backbuffer, bitmap_db_door_2,
5351 VX, VY, VXSIZE, VYSIZE, 0, 0);
5353 door_state &= ~DOOR_NO_COPY_BACK;
5356 return MoveDoor(door_state);
5359 unsigned int GetDoorState(void)
5361 return MoveDoor(DOOR_GET_STATE);
5364 unsigned int SetDoorState(unsigned int door_state)
5366 return MoveDoor(door_state | DOOR_SET_STATE);
5369 static int euclid(int a, int b)
5371 return (b ? euclid(b, a % b) : a);
5374 unsigned int MoveDoor(unsigned int door_state)
5376 struct Rect door_rect_list[] =
5378 { DX, DY, DXSIZE, DYSIZE },
5379 { VX, VY, VXSIZE, VYSIZE }
5381 static int door1 = DOOR_CLOSE_1;
5382 static int door2 = DOOR_CLOSE_2;
5383 DelayCounter door_delay = { 0 };
5386 if (door_state == DOOR_GET_STATE)
5387 return (door1 | door2);
5389 if (door_state & DOOR_SET_STATE)
5391 if (door_state & DOOR_ACTION_1)
5392 door1 = door_state & DOOR_ACTION_1;
5393 if (door_state & DOOR_ACTION_2)
5394 door2 = door_state & DOOR_ACTION_2;
5396 return (door1 | door2);
5399 if (!(door_state & DOOR_FORCE_REDRAW))
5401 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5402 door_state &= ~DOOR_OPEN_1;
5403 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5404 door_state &= ~DOOR_CLOSE_1;
5405 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5406 door_state &= ~DOOR_OPEN_2;
5407 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5408 door_state &= ~DOOR_CLOSE_2;
5411 if (global.autoplay_leveldir)
5413 door_state |= DOOR_NO_DELAY;
5414 door_state &= ~DOOR_CLOSE_ALL;
5417 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5418 door_state |= DOOR_NO_DELAY;
5420 if (door_state & DOOR_ACTION)
5422 boolean door_panel_drawn[NUM_DOORS];
5423 boolean panel_has_doors[NUM_DOORS];
5424 boolean door_part_skip[MAX_DOOR_PARTS];
5425 boolean door_part_done[MAX_DOOR_PARTS];
5426 boolean door_part_done_all;
5427 int num_steps[MAX_DOOR_PARTS];
5428 int max_move_delay = 0; // delay for complete animations of all doors
5429 int max_step_delay = 0; // delay (ms) between two animation frames
5430 int num_move_steps = 0; // number of animation steps for all doors
5431 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5432 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5436 for (i = 0; i < NUM_DOORS; i++)
5437 panel_has_doors[i] = FALSE;
5439 for (i = 0; i < MAX_DOOR_PARTS; i++)
5441 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5442 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5443 int door_token = dpc->door_token;
5445 door_part_done[i] = FALSE;
5446 door_part_skip[i] = (!(door_state & door_token) ||
5450 for (i = 0; i < MAX_DOOR_PARTS; i++)
5452 int nr = door_part_order[i].nr;
5453 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5454 struct DoorPartPosInfo *pos = dpc->pos;
5455 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5456 int door_token = dpc->door_token;
5457 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5458 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5459 int step_xoffset = ABS(pos->step_xoffset);
5460 int step_yoffset = ABS(pos->step_yoffset);
5461 int step_delay = pos->step_delay;
5462 int current_door_state = door_state & door_token;
5463 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5464 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5465 boolean part_opening = (is_panel ? door_closing : door_opening);
5466 int start_step = (part_opening ? pos->start_step_opening :
5467 pos->start_step_closing);
5468 float move_xsize = (step_xoffset ? g->width : 0);
5469 float move_ysize = (step_yoffset ? g->height : 0);
5470 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5471 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5472 int move_steps = (move_xsteps && move_ysteps ?
5473 MIN(move_xsteps, move_ysteps) :
5474 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5475 int move_delay = move_steps * step_delay;
5477 if (door_part_skip[nr])
5480 max_move_delay = MAX(max_move_delay, move_delay);
5481 max_step_delay = (max_step_delay == 0 ? step_delay :
5482 euclid(max_step_delay, step_delay));
5483 num_steps[nr] = move_steps;
5487 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5489 panel_has_doors[door_index] = TRUE;
5493 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5495 num_move_steps = max_move_delay / max_step_delay;
5496 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5498 door_delay.value = max_step_delay;
5500 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5502 start = num_move_steps - 1;
5506 // opening door sound has priority over simultaneously closing door
5507 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5509 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5511 if (door_state & DOOR_OPEN_1)
5512 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5513 if (door_state & DOOR_OPEN_2)
5514 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5516 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5518 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5520 if (door_state & DOOR_CLOSE_1)
5521 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5522 if (door_state & DOOR_CLOSE_2)
5523 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5527 for (k = start; k < num_move_steps; k++)
5529 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5531 door_part_done_all = TRUE;
5533 for (i = 0; i < NUM_DOORS; i++)
5534 door_panel_drawn[i] = FALSE;
5536 for (i = 0; i < MAX_DOOR_PARTS; i++)
5538 int nr = door_part_order[i].nr;
5539 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5540 struct DoorPartPosInfo *pos = dpc->pos;
5541 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5542 int door_token = dpc->door_token;
5543 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5544 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5545 boolean is_panel_and_door_has_closed = FALSE;
5546 struct Rect *door_rect = &door_rect_list[door_index];
5547 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5549 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5550 int current_door_state = door_state & door_token;
5551 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5552 boolean door_closing = !door_opening;
5553 boolean part_opening = (is_panel ? door_closing : door_opening);
5554 boolean part_closing = !part_opening;
5555 int start_step = (part_opening ? pos->start_step_opening :
5556 pos->start_step_closing);
5557 int step_delay = pos->step_delay;
5558 int step_factor = step_delay / max_step_delay;
5559 int k1 = (step_factor ? k / step_factor + 1 : k);
5560 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5561 int kk = MAX(0, k2);
5564 int src_x, src_y, src_xx, src_yy;
5565 int dst_x, dst_y, dst_xx, dst_yy;
5568 if (door_part_skip[nr])
5571 if (!(door_state & door_token))
5579 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5580 int kk_door = MAX(0, k2_door);
5581 int sync_frame = kk_door * door_delay.value;
5582 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5584 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5585 &g_src_x, &g_src_y);
5590 if (!door_panel_drawn[door_index])
5592 ClearRectangle(drawto, door_rect->x, door_rect->y,
5593 door_rect->width, door_rect->height);
5595 door_panel_drawn[door_index] = TRUE;
5598 // draw opening or closing door parts
5600 if (pos->step_xoffset < 0) // door part on right side
5603 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5606 if (dst_xx + width > door_rect->width)
5607 width = door_rect->width - dst_xx;
5609 else // door part on left side
5612 dst_xx = pos->x - kk * pos->step_xoffset;
5616 src_xx = ABS(dst_xx);
5620 width = g->width - src_xx;
5622 if (width > door_rect->width)
5623 width = door_rect->width;
5625 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5628 if (pos->step_yoffset < 0) // door part on bottom side
5631 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5634 if (dst_yy + height > door_rect->height)
5635 height = door_rect->height - dst_yy;
5637 else // door part on top side
5640 dst_yy = pos->y - kk * pos->step_yoffset;
5644 src_yy = ABS(dst_yy);
5648 height = g->height - src_yy;
5651 src_x = g_src_x + src_xx;
5652 src_y = g_src_y + src_yy;
5654 dst_x = door_rect->x + dst_xx;
5655 dst_y = door_rect->y + dst_yy;
5657 is_panel_and_door_has_closed =
5660 panel_has_doors[door_index] &&
5661 k >= num_move_steps_doors_only - 1);
5663 if (width >= 0 && width <= g->width &&
5664 height >= 0 && height <= g->height &&
5665 !is_panel_and_door_has_closed)
5667 if (is_panel || !pos->draw_masked)
5668 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5671 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5675 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5677 if ((part_opening && (width < 0 || height < 0)) ||
5678 (part_closing && (width >= g->width && height >= g->height)))
5679 door_part_done[nr] = TRUE;
5681 // continue door part animations, but not panel after door has closed
5682 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5683 door_part_done_all = FALSE;
5686 if (!(door_state & DOOR_NO_DELAY))
5690 SkipUntilDelayReached(&door_delay, &k, last_frame);
5692 // prevent OS (Windows) from complaining about program not responding
5696 if (door_part_done_all)
5700 if (!(door_state & DOOR_NO_DELAY))
5702 // wait for specified door action post delay
5703 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5704 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5705 else if (door_state & DOOR_ACTION_1)
5706 door_delay.value = door_1.post_delay;
5707 else if (door_state & DOOR_ACTION_2)
5708 door_delay.value = door_2.post_delay;
5710 while (!DelayReached(&door_delay))
5715 if (door_state & DOOR_ACTION_1)
5716 door1 = door_state & DOOR_ACTION_1;
5717 if (door_state & DOOR_ACTION_2)
5718 door2 = door_state & DOOR_ACTION_2;
5720 // draw masked border over door area
5721 DrawMaskedBorder(REDRAW_DOOR_1);
5722 DrawMaskedBorder(REDRAW_DOOR_2);
5724 ClearAutoRepeatKeyEvents();
5726 return (door1 | door2);
5729 static boolean useSpecialEditorDoor(void)
5731 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5732 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5734 // do not draw special editor door if editor border defined or redefined
5735 if (graphic_info[graphic].bitmap != NULL || redefined)
5738 // do not draw special editor door if global border defined to be empty
5739 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5742 // do not draw special editor door if viewport definitions do not match
5746 EY + EYSIZE != VY + VYSIZE)
5752 void DrawSpecialEditorDoor(void)
5754 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5755 int top_border_width = gfx1->width;
5756 int top_border_height = gfx1->height;
5757 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5758 int ex = EX - outer_border;
5759 int ey = EY - outer_border;
5760 int vy = VY - outer_border;
5761 int exsize = EXSIZE + 2 * outer_border;
5763 if (!useSpecialEditorDoor())
5766 // draw bigger level editor toolbox window
5767 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5768 top_border_width, top_border_height, ex, ey - top_border_height);
5769 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5770 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5772 redraw_mask |= REDRAW_ALL;
5775 void UndrawSpecialEditorDoor(void)
5777 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5778 int top_border_width = gfx1->width;
5779 int top_border_height = gfx1->height;
5780 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5781 int ex = EX - outer_border;
5782 int ey = EY - outer_border;
5783 int ey_top = ey - top_border_height;
5784 int exsize = EXSIZE + 2 * outer_border;
5785 int eysize = EYSIZE + 2 * outer_border;
5787 if (!useSpecialEditorDoor())
5790 // draw normal tape recorder window
5791 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5793 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5794 ex, ey_top, top_border_width, top_border_height,
5796 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5797 ex, ey, exsize, eysize, ex, ey);
5801 // if screen background is set to "[NONE]", clear editor toolbox window
5802 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5803 ClearRectangle(drawto, ex, ey, exsize, eysize);
5806 redraw_mask |= REDRAW_ALL;
5810 // ---------- new tool button stuff -------------------------------------------
5815 struct TextPosInfo *pos;
5817 boolean is_touch_button;
5819 } toolbutton_info[NUM_TOOL_BUTTONS] =
5822 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5823 TOOL_CTRL_ID_YES, FALSE, "yes"
5826 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5827 TOOL_CTRL_ID_NO, FALSE, "no"
5830 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5831 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5834 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5835 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5838 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5839 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5842 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5843 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5846 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5847 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5850 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5851 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5854 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5855 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5858 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5859 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5863 void CreateToolButtons(void)
5867 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5869 int graphic = toolbutton_info[i].graphic;
5870 struct GraphicInfo *gfx = &graphic_info[graphic];
5871 struct TextPosInfo *pos = toolbutton_info[i].pos;
5872 struct GadgetInfo *gi;
5873 Bitmap *deco_bitmap = None;
5874 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5875 unsigned int event_mask = GD_EVENT_RELEASED;
5876 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5877 int base_x = (is_touch_button ? 0 : DX);
5878 int base_y = (is_touch_button ? 0 : DY);
5879 int gd_x = gfx->src_x;
5880 int gd_y = gfx->src_y;
5881 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5882 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5887 // do not use touch buttons if overlay touch buttons are disabled
5888 if (is_touch_button && !setup.touch.overlay_buttons)
5891 if (global.use_envelope_request && !is_touch_button)
5893 setRequestPosition(&base_x, &base_y, TRUE);
5895 // check if request buttons are outside of envelope and fix, if needed
5896 if (x < 0 || x + gfx->width > request.width ||
5897 y < 0 || y + gfx->height > request.height)
5899 if (id == TOOL_CTRL_ID_YES)
5902 y = request.height - 2 * request.border_size - gfx->height;
5904 else if (id == TOOL_CTRL_ID_NO)
5906 x = request.width - 2 * request.border_size - gfx->width;
5907 y = request.height - 2 * request.border_size - gfx->height;
5909 else if (id == TOOL_CTRL_ID_CONFIRM)
5911 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5912 y = request.height - 2 * request.border_size - gfx->height;
5914 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5916 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5918 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5919 y = request.height - 2 * request.border_size - gfx->height * 2;
5921 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5922 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5927 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5930 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5932 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5933 pos->size, &deco_bitmap, &deco_x, &deco_y);
5934 deco_xpos = (gfx->width - pos->size) / 2;
5935 deco_ypos = (gfx->height - pos->size) / 2;
5938 gi = CreateGadget(GDI_CUSTOM_ID, id,
5939 GDI_IMAGE_ID, graphic,
5940 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5943 GDI_WIDTH, gfx->width,
5944 GDI_HEIGHT, gfx->height,
5945 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5946 GDI_STATE, GD_BUTTON_UNPRESSED,
5947 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5948 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5949 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5950 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5951 GDI_DECORATION_SIZE, pos->size, pos->size,
5952 GDI_DECORATION_SHIFTING, 1, 1,
5953 GDI_DIRECT_DRAW, FALSE,
5954 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5955 GDI_EVENT_MASK, event_mask,
5956 GDI_CALLBACK_ACTION, HandleToolButtons,
5960 Fail("cannot create gadget");
5962 tool_gadget[id] = gi;
5966 void FreeToolButtons(void)
5970 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5971 FreeGadget(tool_gadget[i]);
5974 static void UnmapToolButtons(void)
5978 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5979 UnmapGadget(tool_gadget[i]);
5982 static void HandleToolButtons(struct GadgetInfo *gi)
5984 request_gadget_id = gi->custom_id;
5987 static struct Mapping_EM_to_RND_object
5990 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5991 boolean is_backside; // backside of moving element
5997 em_object_mapping_list[GAME_TILE_MAX + 1] =
6000 Zborder, FALSE, FALSE,
6004 Zplayer, FALSE, FALSE,
6013 Ztank, FALSE, FALSE,
6017 Zeater, FALSE, FALSE,
6021 Zdynamite, FALSE, FALSE,
6025 Zboom, FALSE, FALSE,
6030 Xchain, FALSE, FALSE,
6031 EL_DEFAULT, ACTION_EXPLODING, -1
6034 Xboom_bug, FALSE, FALSE,
6035 EL_BUG, ACTION_EXPLODING, -1
6038 Xboom_tank, FALSE, FALSE,
6039 EL_SPACESHIP, ACTION_EXPLODING, -1
6042 Xboom_android, FALSE, FALSE,
6043 EL_EMC_ANDROID, ACTION_OTHER, -1
6046 Xboom_1, FALSE, FALSE,
6047 EL_DEFAULT, ACTION_EXPLODING, -1
6050 Xboom_2, FALSE, FALSE,
6051 EL_DEFAULT, ACTION_EXPLODING, -1
6055 Xblank, TRUE, FALSE,
6060 Xsplash_e, FALSE, FALSE,
6061 EL_ACID_SPLASH_RIGHT, -1, -1
6064 Xsplash_w, FALSE, FALSE,
6065 EL_ACID_SPLASH_LEFT, -1, -1
6069 Xplant, TRUE, FALSE,
6070 EL_EMC_PLANT, -1, -1
6073 Yplant, FALSE, FALSE,
6074 EL_EMC_PLANT, -1, -1
6078 Xacid_1, TRUE, FALSE,
6082 Xacid_2, FALSE, FALSE,
6086 Xacid_3, FALSE, FALSE,
6090 Xacid_4, FALSE, FALSE,
6094 Xacid_5, FALSE, FALSE,
6098 Xacid_6, FALSE, FALSE,
6102 Xacid_7, FALSE, FALSE,
6106 Xacid_8, FALSE, FALSE,
6111 Xfake_acid_1, TRUE, FALSE,
6112 EL_EMC_FAKE_ACID, -1, -1
6115 Xfake_acid_2, FALSE, FALSE,
6116 EL_EMC_FAKE_ACID, -1, -1
6119 Xfake_acid_3, FALSE, FALSE,
6120 EL_EMC_FAKE_ACID, -1, -1
6123 Xfake_acid_4, FALSE, FALSE,
6124 EL_EMC_FAKE_ACID, -1, -1
6127 Xfake_acid_5, FALSE, FALSE,
6128 EL_EMC_FAKE_ACID, -1, -1
6131 Xfake_acid_6, FALSE, FALSE,
6132 EL_EMC_FAKE_ACID, -1, -1
6135 Xfake_acid_7, FALSE, FALSE,
6136 EL_EMC_FAKE_ACID, -1, -1
6139 Xfake_acid_8, FALSE, FALSE,
6140 EL_EMC_FAKE_ACID, -1, -1
6144 Xfake_acid_1_player, FALSE, FALSE,
6145 EL_EMC_FAKE_ACID, -1, -1
6148 Xfake_acid_2_player, FALSE, FALSE,
6149 EL_EMC_FAKE_ACID, -1, -1
6152 Xfake_acid_3_player, FALSE, FALSE,
6153 EL_EMC_FAKE_ACID, -1, -1
6156 Xfake_acid_4_player, FALSE, FALSE,
6157 EL_EMC_FAKE_ACID, -1, -1
6160 Xfake_acid_5_player, FALSE, FALSE,
6161 EL_EMC_FAKE_ACID, -1, -1
6164 Xfake_acid_6_player, FALSE, FALSE,
6165 EL_EMC_FAKE_ACID, -1, -1
6168 Xfake_acid_7_player, FALSE, FALSE,
6169 EL_EMC_FAKE_ACID, -1, -1
6172 Xfake_acid_8_player, FALSE, FALSE,
6173 EL_EMC_FAKE_ACID, -1, -1
6177 Xgrass, TRUE, FALSE,
6178 EL_EMC_GRASS, -1, -1
6181 Ygrass_nB, FALSE, FALSE,
6182 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6185 Ygrass_eB, FALSE, FALSE,
6186 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6189 Ygrass_sB, FALSE, FALSE,
6190 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6193 Ygrass_wB, FALSE, FALSE,
6194 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6202 Ydirt_nB, FALSE, FALSE,
6203 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6206 Ydirt_eB, FALSE, FALSE,
6207 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6210 Ydirt_sB, FALSE, FALSE,
6211 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6214 Ydirt_wB, FALSE, FALSE,
6215 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6219 Xandroid, TRUE, FALSE,
6220 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6223 Xandroid_1_n, FALSE, FALSE,
6224 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6227 Xandroid_2_n, FALSE, FALSE,
6228 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6231 Xandroid_1_e, FALSE, FALSE,
6232 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6235 Xandroid_2_e, FALSE, FALSE,
6236 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6239 Xandroid_1_w, FALSE, FALSE,
6240 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6243 Xandroid_2_w, FALSE, FALSE,
6244 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6247 Xandroid_1_s, FALSE, FALSE,
6248 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6251 Xandroid_2_s, FALSE, FALSE,
6252 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6255 Yandroid_n, FALSE, FALSE,
6256 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6259 Yandroid_nB, FALSE, TRUE,
6260 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6263 Yandroid_ne, FALSE, FALSE,
6264 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6267 Yandroid_neB, FALSE, TRUE,
6268 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6271 Yandroid_e, FALSE, FALSE,
6272 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6275 Yandroid_eB, FALSE, TRUE,
6276 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6279 Yandroid_se, FALSE, FALSE,
6280 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6283 Yandroid_seB, FALSE, TRUE,
6284 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6287 Yandroid_s, FALSE, FALSE,
6288 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6291 Yandroid_sB, FALSE, TRUE,
6292 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6295 Yandroid_sw, FALSE, FALSE,
6296 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6299 Yandroid_swB, FALSE, TRUE,
6300 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6303 Yandroid_w, FALSE, FALSE,
6304 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6307 Yandroid_wB, FALSE, TRUE,
6308 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6311 Yandroid_nw, FALSE, FALSE,
6312 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6315 Yandroid_nwB, FALSE, TRUE,
6316 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6320 Xeater_n, TRUE, FALSE,
6321 EL_YAMYAM_UP, -1, -1
6324 Xeater_e, TRUE, FALSE,
6325 EL_YAMYAM_RIGHT, -1, -1
6328 Xeater_w, TRUE, FALSE,
6329 EL_YAMYAM_LEFT, -1, -1
6332 Xeater_s, TRUE, FALSE,
6333 EL_YAMYAM_DOWN, -1, -1
6336 Yeater_n, FALSE, FALSE,
6337 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6340 Yeater_nB, FALSE, TRUE,
6341 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6344 Yeater_e, FALSE, FALSE,
6345 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6348 Yeater_eB, FALSE, TRUE,
6349 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6352 Yeater_s, FALSE, FALSE,
6353 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6356 Yeater_sB, FALSE, TRUE,
6357 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6360 Yeater_w, FALSE, FALSE,
6361 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6364 Yeater_wB, FALSE, TRUE,
6365 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6368 Yeater_stone, FALSE, FALSE,
6369 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6372 Yeater_spring, FALSE, FALSE,
6373 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6377 Xalien, TRUE, FALSE,
6381 Xalien_pause, FALSE, FALSE,
6385 Yalien_n, FALSE, FALSE,
6386 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6389 Yalien_nB, FALSE, TRUE,
6390 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6393 Yalien_e, FALSE, FALSE,
6394 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6397 Yalien_eB, FALSE, TRUE,
6398 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6401 Yalien_s, FALSE, FALSE,
6402 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6405 Yalien_sB, FALSE, TRUE,
6406 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6409 Yalien_w, FALSE, FALSE,
6410 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6413 Yalien_wB, FALSE, TRUE,
6414 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6417 Yalien_stone, FALSE, FALSE,
6418 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6421 Yalien_spring, FALSE, FALSE,
6422 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6426 Xbug_1_n, TRUE, FALSE,
6430 Xbug_1_e, TRUE, FALSE,
6431 EL_BUG_RIGHT, -1, -1
6434 Xbug_1_s, TRUE, FALSE,
6438 Xbug_1_w, TRUE, FALSE,
6442 Xbug_2_n, FALSE, FALSE,
6446 Xbug_2_e, FALSE, FALSE,
6447 EL_BUG_RIGHT, -1, -1
6450 Xbug_2_s, FALSE, FALSE,
6454 Xbug_2_w, FALSE, FALSE,
6458 Ybug_n, FALSE, FALSE,
6459 EL_BUG, ACTION_MOVING, MV_BIT_UP
6462 Ybug_nB, FALSE, TRUE,
6463 EL_BUG, ACTION_MOVING, MV_BIT_UP
6466 Ybug_e, FALSE, FALSE,
6467 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6470 Ybug_eB, FALSE, TRUE,
6471 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6474 Ybug_s, FALSE, FALSE,
6475 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6478 Ybug_sB, FALSE, TRUE,
6479 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6482 Ybug_w, FALSE, FALSE,
6483 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6486 Ybug_wB, FALSE, TRUE,
6487 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6490 Ybug_w_n, FALSE, FALSE,
6491 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6494 Ybug_n_e, FALSE, FALSE,
6495 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6498 Ybug_e_s, FALSE, FALSE,
6499 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6502 Ybug_s_w, FALSE, FALSE,
6503 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6506 Ybug_e_n, FALSE, FALSE,
6507 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6510 Ybug_s_e, FALSE, FALSE,
6511 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6514 Ybug_w_s, FALSE, FALSE,
6515 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6518 Ybug_n_w, FALSE, FALSE,
6519 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6522 Ybug_stone, FALSE, FALSE,
6523 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6526 Ybug_spring, FALSE, FALSE,
6527 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6531 Xtank_1_n, TRUE, FALSE,
6532 EL_SPACESHIP_UP, -1, -1
6535 Xtank_1_e, TRUE, FALSE,
6536 EL_SPACESHIP_RIGHT, -1, -1
6539 Xtank_1_s, TRUE, FALSE,
6540 EL_SPACESHIP_DOWN, -1, -1
6543 Xtank_1_w, TRUE, FALSE,
6544 EL_SPACESHIP_LEFT, -1, -1
6547 Xtank_2_n, FALSE, FALSE,
6548 EL_SPACESHIP_UP, -1, -1
6551 Xtank_2_e, FALSE, FALSE,
6552 EL_SPACESHIP_RIGHT, -1, -1
6555 Xtank_2_s, FALSE, FALSE,
6556 EL_SPACESHIP_DOWN, -1, -1
6559 Xtank_2_w, FALSE, FALSE,
6560 EL_SPACESHIP_LEFT, -1, -1
6563 Ytank_n, FALSE, FALSE,
6564 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6567 Ytank_nB, FALSE, TRUE,
6568 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6571 Ytank_e, FALSE, FALSE,
6572 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6575 Ytank_eB, FALSE, TRUE,
6576 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6579 Ytank_s, FALSE, FALSE,
6580 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6583 Ytank_sB, FALSE, TRUE,
6584 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6587 Ytank_w, FALSE, FALSE,
6588 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6591 Ytank_wB, FALSE, TRUE,
6592 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6595 Ytank_w_n, FALSE, FALSE,
6596 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6599 Ytank_n_e, FALSE, FALSE,
6600 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6603 Ytank_e_s, FALSE, FALSE,
6604 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6607 Ytank_s_w, FALSE, FALSE,
6608 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6611 Ytank_e_n, FALSE, FALSE,
6612 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6615 Ytank_s_e, FALSE, FALSE,
6616 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6619 Ytank_w_s, FALSE, FALSE,
6620 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6623 Ytank_n_w, FALSE, FALSE,
6624 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6627 Ytank_stone, FALSE, FALSE,
6628 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6631 Ytank_spring, FALSE, FALSE,
6632 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6636 Xemerald, TRUE, FALSE,
6640 Xemerald_pause, FALSE, FALSE,
6644 Xemerald_fall, FALSE, FALSE,
6648 Xemerald_shine, FALSE, FALSE,
6649 EL_EMERALD, ACTION_TWINKLING, -1
6652 Yemerald_s, FALSE, FALSE,
6653 EL_EMERALD, ACTION_FALLING, -1
6656 Yemerald_sB, FALSE, TRUE,
6657 EL_EMERALD, ACTION_FALLING, -1
6660 Yemerald_e, FALSE, FALSE,
6661 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6664 Yemerald_eB, FALSE, TRUE,
6665 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6668 Yemerald_w, FALSE, FALSE,
6669 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6672 Yemerald_wB, FALSE, TRUE,
6673 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6676 Yemerald_blank, FALSE, FALSE,
6677 EL_EMERALD, ACTION_COLLECTING, -1
6681 Xdiamond, TRUE, FALSE,
6685 Xdiamond_pause, FALSE, FALSE,
6689 Xdiamond_fall, FALSE, FALSE,
6693 Xdiamond_shine, FALSE, FALSE,
6694 EL_DIAMOND, ACTION_TWINKLING, -1
6697 Ydiamond_s, FALSE, FALSE,
6698 EL_DIAMOND, ACTION_FALLING, -1
6701 Ydiamond_sB, FALSE, TRUE,
6702 EL_DIAMOND, ACTION_FALLING, -1
6705 Ydiamond_e, FALSE, FALSE,
6706 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6709 Ydiamond_eB, FALSE, TRUE,
6710 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6713 Ydiamond_w, FALSE, FALSE,
6714 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6717 Ydiamond_wB, FALSE, TRUE,
6718 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6721 Ydiamond_blank, FALSE, FALSE,
6722 EL_DIAMOND, ACTION_COLLECTING, -1
6725 Ydiamond_stone, FALSE, FALSE,
6726 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6730 Xstone, TRUE, FALSE,
6734 Xstone_pause, FALSE, FALSE,
6738 Xstone_fall, FALSE, FALSE,
6742 Ystone_s, FALSE, FALSE,
6743 EL_ROCK, ACTION_FALLING, -1
6746 Ystone_sB, FALSE, TRUE,
6747 EL_ROCK, ACTION_FALLING, -1
6750 Ystone_e, FALSE, FALSE,
6751 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6754 Ystone_eB, FALSE, TRUE,
6755 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6758 Ystone_w, FALSE, FALSE,
6759 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6762 Ystone_wB, FALSE, TRUE,
6763 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6771 Xbomb_pause, FALSE, FALSE,
6775 Xbomb_fall, FALSE, FALSE,
6779 Ybomb_s, FALSE, FALSE,
6780 EL_BOMB, ACTION_FALLING, -1
6783 Ybomb_sB, FALSE, TRUE,
6784 EL_BOMB, ACTION_FALLING, -1
6787 Ybomb_e, FALSE, FALSE,
6788 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6791 Ybomb_eB, FALSE, TRUE,
6792 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6795 Ybomb_w, FALSE, FALSE,
6796 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6799 Ybomb_wB, FALSE, TRUE,
6800 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6803 Ybomb_blank, FALSE, FALSE,
6804 EL_BOMB, ACTION_ACTIVATING, -1
6812 Xnut_pause, FALSE, FALSE,
6816 Xnut_fall, FALSE, FALSE,
6820 Ynut_s, FALSE, FALSE,
6821 EL_NUT, ACTION_FALLING, -1
6824 Ynut_sB, FALSE, TRUE,
6825 EL_NUT, ACTION_FALLING, -1
6828 Ynut_e, FALSE, FALSE,
6829 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6832 Ynut_eB, FALSE, TRUE,
6833 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6836 Ynut_w, FALSE, FALSE,
6837 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6840 Ynut_wB, FALSE, TRUE,
6841 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6844 Ynut_stone, FALSE, FALSE,
6845 EL_NUT, ACTION_BREAKING, -1
6849 Xspring, TRUE, FALSE,
6853 Xspring_pause, FALSE, FALSE,
6857 Xspring_e, TRUE, FALSE,
6858 EL_SPRING_RIGHT, -1, -1
6861 Xspring_w, TRUE, FALSE,
6862 EL_SPRING_LEFT, -1, -1
6865 Xspring_fall, FALSE, FALSE,
6869 Yspring_s, FALSE, FALSE,
6870 EL_SPRING, ACTION_FALLING, -1
6873 Yspring_sB, FALSE, TRUE,
6874 EL_SPRING, ACTION_FALLING, -1
6877 Yspring_e, FALSE, FALSE,
6878 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6881 Yspring_eB, FALSE, TRUE,
6882 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6885 Yspring_w, FALSE, FALSE,
6886 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6889 Yspring_wB, FALSE, TRUE,
6890 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6893 Yspring_alien_e, FALSE, FALSE,
6894 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6897 Yspring_alien_eB, FALSE, TRUE,
6898 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6901 Yspring_alien_w, FALSE, FALSE,
6902 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6905 Yspring_alien_wB, FALSE, TRUE,
6906 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6910 Xpush_emerald_e, FALSE, FALSE,
6911 EL_EMERALD, -1, MV_BIT_RIGHT
6914 Xpush_emerald_w, FALSE, FALSE,
6915 EL_EMERALD, -1, MV_BIT_LEFT
6918 Xpush_diamond_e, FALSE, FALSE,
6919 EL_DIAMOND, -1, MV_BIT_RIGHT
6922 Xpush_diamond_w, FALSE, FALSE,
6923 EL_DIAMOND, -1, MV_BIT_LEFT
6926 Xpush_stone_e, FALSE, FALSE,
6927 EL_ROCK, -1, MV_BIT_RIGHT
6930 Xpush_stone_w, FALSE, FALSE,
6931 EL_ROCK, -1, MV_BIT_LEFT
6934 Xpush_bomb_e, FALSE, FALSE,
6935 EL_BOMB, -1, MV_BIT_RIGHT
6938 Xpush_bomb_w, FALSE, FALSE,
6939 EL_BOMB, -1, MV_BIT_LEFT
6942 Xpush_nut_e, FALSE, FALSE,
6943 EL_NUT, -1, MV_BIT_RIGHT
6946 Xpush_nut_w, FALSE, FALSE,
6947 EL_NUT, -1, MV_BIT_LEFT
6950 Xpush_spring_e, FALSE, FALSE,
6951 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6954 Xpush_spring_w, FALSE, FALSE,
6955 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6959 Xdynamite, TRUE, FALSE,
6960 EL_EM_DYNAMITE, -1, -1
6963 Ydynamite_blank, FALSE, FALSE,
6964 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6967 Xdynamite_1, TRUE, FALSE,
6968 EL_EM_DYNAMITE_ACTIVE, -1, -1
6971 Xdynamite_2, FALSE, FALSE,
6972 EL_EM_DYNAMITE_ACTIVE, -1, -1
6975 Xdynamite_3, FALSE, FALSE,
6976 EL_EM_DYNAMITE_ACTIVE, -1, -1
6979 Xdynamite_4, FALSE, FALSE,
6980 EL_EM_DYNAMITE_ACTIVE, -1, -1
6984 Xkey_1, TRUE, FALSE,
6988 Xkey_2, TRUE, FALSE,
6992 Xkey_3, TRUE, FALSE,
6996 Xkey_4, TRUE, FALSE,
7000 Xkey_5, TRUE, FALSE,
7001 EL_EMC_KEY_5, -1, -1
7004 Xkey_6, TRUE, FALSE,
7005 EL_EMC_KEY_6, -1, -1
7008 Xkey_7, TRUE, FALSE,
7009 EL_EMC_KEY_7, -1, -1
7012 Xkey_8, TRUE, FALSE,
7013 EL_EMC_KEY_8, -1, -1
7017 Xdoor_1, TRUE, FALSE,
7018 EL_EM_GATE_1, -1, -1
7021 Xdoor_2, TRUE, FALSE,
7022 EL_EM_GATE_2, -1, -1
7025 Xdoor_3, TRUE, FALSE,
7026 EL_EM_GATE_3, -1, -1
7029 Xdoor_4, TRUE, FALSE,
7030 EL_EM_GATE_4, -1, -1
7033 Xdoor_5, TRUE, FALSE,
7034 EL_EMC_GATE_5, -1, -1
7037 Xdoor_6, TRUE, FALSE,
7038 EL_EMC_GATE_6, -1, -1
7041 Xdoor_7, TRUE, FALSE,
7042 EL_EMC_GATE_7, -1, -1
7045 Xdoor_8, TRUE, FALSE,
7046 EL_EMC_GATE_8, -1, -1
7050 Xfake_door_1, TRUE, FALSE,
7051 EL_EM_GATE_1_GRAY, -1, -1
7054 Xfake_door_2, TRUE, FALSE,
7055 EL_EM_GATE_2_GRAY, -1, -1
7058 Xfake_door_3, TRUE, FALSE,
7059 EL_EM_GATE_3_GRAY, -1, -1
7062 Xfake_door_4, TRUE, FALSE,
7063 EL_EM_GATE_4_GRAY, -1, -1
7066 Xfake_door_5, TRUE, FALSE,
7067 EL_EMC_GATE_5_GRAY, -1, -1
7070 Xfake_door_6, TRUE, FALSE,
7071 EL_EMC_GATE_6_GRAY, -1, -1
7074 Xfake_door_7, TRUE, FALSE,
7075 EL_EMC_GATE_7_GRAY, -1, -1
7078 Xfake_door_8, TRUE, FALSE,
7079 EL_EMC_GATE_8_GRAY, -1, -1
7083 Xballoon, TRUE, FALSE,
7087 Yballoon_n, FALSE, FALSE,
7088 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7091 Yballoon_nB, FALSE, TRUE,
7092 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7095 Yballoon_e, FALSE, FALSE,
7096 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7099 Yballoon_eB, FALSE, TRUE,
7100 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7103 Yballoon_s, FALSE, FALSE,
7104 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7107 Yballoon_sB, FALSE, TRUE,
7108 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7111 Yballoon_w, FALSE, FALSE,
7112 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7115 Yballoon_wB, FALSE, TRUE,
7116 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7120 Xball_1, TRUE, FALSE,
7121 EL_EMC_MAGIC_BALL, -1, -1
7124 Yball_1, FALSE, FALSE,
7125 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7128 Xball_2, FALSE, FALSE,
7129 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7132 Yball_2, FALSE, FALSE,
7133 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7136 Yball_blank, FALSE, FALSE,
7137 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7141 Xamoeba_1, TRUE, FALSE,
7142 EL_AMOEBA_DRY, ACTION_OTHER, -1
7145 Xamoeba_2, FALSE, FALSE,
7146 EL_AMOEBA_DRY, ACTION_OTHER, -1
7149 Xamoeba_3, FALSE, FALSE,
7150 EL_AMOEBA_DRY, ACTION_OTHER, -1
7153 Xamoeba_4, FALSE, FALSE,
7154 EL_AMOEBA_DRY, ACTION_OTHER, -1
7157 Xamoeba_5, TRUE, FALSE,
7158 EL_AMOEBA_WET, ACTION_OTHER, -1
7161 Xamoeba_6, FALSE, FALSE,
7162 EL_AMOEBA_WET, ACTION_OTHER, -1
7165 Xamoeba_7, FALSE, FALSE,
7166 EL_AMOEBA_WET, ACTION_OTHER, -1
7169 Xamoeba_8, FALSE, FALSE,
7170 EL_AMOEBA_WET, ACTION_OTHER, -1
7175 EL_AMOEBA_DROP, ACTION_GROWING, -1
7178 Xdrip_fall, FALSE, FALSE,
7179 EL_AMOEBA_DROP, -1, -1
7182 Xdrip_stretch, FALSE, FALSE,
7183 EL_AMOEBA_DROP, ACTION_FALLING, -1
7186 Xdrip_stretchB, FALSE, TRUE,
7187 EL_AMOEBA_DROP, ACTION_FALLING, -1
7190 Ydrip_1_s, FALSE, FALSE,
7191 EL_AMOEBA_DROP, ACTION_FALLING, -1
7194 Ydrip_1_sB, FALSE, TRUE,
7195 EL_AMOEBA_DROP, ACTION_FALLING, -1
7198 Ydrip_2_s, FALSE, FALSE,
7199 EL_AMOEBA_DROP, ACTION_FALLING, -1
7202 Ydrip_2_sB, FALSE, TRUE,
7203 EL_AMOEBA_DROP, ACTION_FALLING, -1
7207 Xwonderwall, TRUE, FALSE,
7208 EL_MAGIC_WALL, -1, -1
7211 Ywonderwall, FALSE, FALSE,
7212 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7216 Xwheel, TRUE, FALSE,
7217 EL_ROBOT_WHEEL, -1, -1
7220 Ywheel, FALSE, FALSE,
7221 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7225 Xswitch, TRUE, FALSE,
7226 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7229 Yswitch, FALSE, FALSE,
7230 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7234 Xbumper, TRUE, FALSE,
7235 EL_EMC_SPRING_BUMPER, -1, -1
7238 Ybumper, FALSE, FALSE,
7239 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7243 Xacid_nw, TRUE, FALSE,
7244 EL_ACID_POOL_TOPLEFT, -1, -1
7247 Xacid_ne, TRUE, FALSE,
7248 EL_ACID_POOL_TOPRIGHT, -1, -1
7251 Xacid_sw, TRUE, FALSE,
7252 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7255 Xacid_s, TRUE, FALSE,
7256 EL_ACID_POOL_BOTTOM, -1, -1
7259 Xacid_se, TRUE, FALSE,
7260 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7264 Xfake_blank, TRUE, FALSE,
7265 EL_INVISIBLE_WALL, -1, -1
7268 Yfake_blank, FALSE, FALSE,
7269 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7273 Xfake_grass, TRUE, FALSE,
7274 EL_EMC_FAKE_GRASS, -1, -1
7277 Yfake_grass, FALSE, FALSE,
7278 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7282 Xfake_amoeba, TRUE, FALSE,
7283 EL_EMC_DRIPPER, -1, -1
7286 Yfake_amoeba, FALSE, FALSE,
7287 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7291 Xlenses, TRUE, FALSE,
7292 EL_EMC_LENSES, -1, -1
7296 Xmagnify, TRUE, FALSE,
7297 EL_EMC_MAGNIFIER, -1, -1
7302 EL_QUICKSAND_EMPTY, -1, -1
7305 Xsand_stone, TRUE, FALSE,
7306 EL_QUICKSAND_FULL, -1, -1
7309 Xsand_stonein_1, FALSE, TRUE,
7310 EL_ROCK, ACTION_FILLING, -1
7313 Xsand_stonein_2, FALSE, TRUE,
7314 EL_ROCK, ACTION_FILLING, -1
7317 Xsand_stonein_3, FALSE, TRUE,
7318 EL_ROCK, ACTION_FILLING, -1
7321 Xsand_stonein_4, FALSE, TRUE,
7322 EL_ROCK, ACTION_FILLING, -1
7325 Xsand_sandstone_1, FALSE, FALSE,
7326 EL_QUICKSAND_FILLING, -1, -1
7329 Xsand_sandstone_2, FALSE, FALSE,
7330 EL_QUICKSAND_FILLING, -1, -1
7333 Xsand_sandstone_3, FALSE, FALSE,
7334 EL_QUICKSAND_FILLING, -1, -1
7337 Xsand_sandstone_4, FALSE, FALSE,
7338 EL_QUICKSAND_FILLING, -1, -1
7341 Xsand_stonesand_1, FALSE, FALSE,
7342 EL_QUICKSAND_EMPTYING, -1, -1
7345 Xsand_stonesand_2, FALSE, FALSE,
7346 EL_QUICKSAND_EMPTYING, -1, -1
7349 Xsand_stonesand_3, FALSE, FALSE,
7350 EL_QUICKSAND_EMPTYING, -1, -1
7353 Xsand_stonesand_4, FALSE, FALSE,
7354 EL_QUICKSAND_EMPTYING, -1, -1
7357 Xsand_stoneout_1, FALSE, FALSE,
7358 EL_ROCK, ACTION_EMPTYING, -1
7361 Xsand_stoneout_2, FALSE, FALSE,
7362 EL_ROCK, ACTION_EMPTYING, -1
7365 Xsand_stonesand_quickout_1, FALSE, FALSE,
7366 EL_QUICKSAND_EMPTYING, -1, -1
7369 Xsand_stonesand_quickout_2, FALSE, FALSE,
7370 EL_QUICKSAND_EMPTYING, -1, -1
7374 Xslide_ns, TRUE, FALSE,
7375 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7378 Yslide_ns_blank, FALSE, FALSE,
7379 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7382 Xslide_ew, TRUE, FALSE,
7383 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7386 Yslide_ew_blank, FALSE, FALSE,
7387 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7391 Xwind_n, TRUE, FALSE,
7392 EL_BALLOON_SWITCH_UP, -1, -1
7395 Xwind_e, TRUE, FALSE,
7396 EL_BALLOON_SWITCH_RIGHT, -1, -1
7399 Xwind_s, TRUE, FALSE,
7400 EL_BALLOON_SWITCH_DOWN, -1, -1
7403 Xwind_w, TRUE, FALSE,
7404 EL_BALLOON_SWITCH_LEFT, -1, -1
7407 Xwind_any, TRUE, FALSE,
7408 EL_BALLOON_SWITCH_ANY, -1, -1
7411 Xwind_stop, TRUE, FALSE,
7412 EL_BALLOON_SWITCH_NONE, -1, -1
7417 EL_EM_EXIT_CLOSED, -1, -1
7420 Xexit_1, TRUE, FALSE,
7421 EL_EM_EXIT_OPEN, -1, -1
7424 Xexit_2, FALSE, FALSE,
7425 EL_EM_EXIT_OPEN, -1, -1
7428 Xexit_3, FALSE, FALSE,
7429 EL_EM_EXIT_OPEN, -1, -1
7433 Xpause, FALSE, FALSE,
7438 Xwall_1, TRUE, FALSE,
7442 Xwall_2, TRUE, FALSE,
7443 EL_EMC_WALL_14, -1, -1
7446 Xwall_3, TRUE, FALSE,
7447 EL_EMC_WALL_15, -1, -1
7450 Xwall_4, TRUE, FALSE,
7451 EL_EMC_WALL_16, -1, -1
7455 Xroundwall_1, TRUE, FALSE,
7456 EL_WALL_SLIPPERY, -1, -1
7459 Xroundwall_2, TRUE, FALSE,
7460 EL_EMC_WALL_SLIPPERY_2, -1, -1
7463 Xroundwall_3, TRUE, FALSE,
7464 EL_EMC_WALL_SLIPPERY_3, -1, -1
7467 Xroundwall_4, TRUE, FALSE,
7468 EL_EMC_WALL_SLIPPERY_4, -1, -1
7472 Xsteel_1, TRUE, FALSE,
7473 EL_STEELWALL, -1, -1
7476 Xsteel_2, TRUE, FALSE,
7477 EL_EMC_STEELWALL_2, -1, -1
7480 Xsteel_3, TRUE, FALSE,
7481 EL_EMC_STEELWALL_3, -1, -1
7484 Xsteel_4, TRUE, FALSE,
7485 EL_EMC_STEELWALL_4, -1, -1
7489 Xdecor_1, TRUE, FALSE,
7490 EL_EMC_WALL_8, -1, -1
7493 Xdecor_2, TRUE, FALSE,
7494 EL_EMC_WALL_6, -1, -1
7497 Xdecor_3, TRUE, FALSE,
7498 EL_EMC_WALL_4, -1, -1
7501 Xdecor_4, TRUE, FALSE,
7502 EL_EMC_WALL_7, -1, -1
7505 Xdecor_5, TRUE, FALSE,
7506 EL_EMC_WALL_5, -1, -1
7509 Xdecor_6, TRUE, FALSE,
7510 EL_EMC_WALL_9, -1, -1
7513 Xdecor_7, TRUE, FALSE,
7514 EL_EMC_WALL_10, -1, -1
7517 Xdecor_8, TRUE, FALSE,
7518 EL_EMC_WALL_1, -1, -1
7521 Xdecor_9, TRUE, FALSE,
7522 EL_EMC_WALL_2, -1, -1
7525 Xdecor_10, TRUE, FALSE,
7526 EL_EMC_WALL_3, -1, -1
7529 Xdecor_11, TRUE, FALSE,
7530 EL_EMC_WALL_11, -1, -1
7533 Xdecor_12, TRUE, FALSE,
7534 EL_EMC_WALL_12, -1, -1
7538 Xalpha_0, TRUE, FALSE,
7539 EL_CHAR('0'), -1, -1
7542 Xalpha_1, TRUE, FALSE,
7543 EL_CHAR('1'), -1, -1
7546 Xalpha_2, TRUE, FALSE,
7547 EL_CHAR('2'), -1, -1
7550 Xalpha_3, TRUE, FALSE,
7551 EL_CHAR('3'), -1, -1
7554 Xalpha_4, TRUE, FALSE,
7555 EL_CHAR('4'), -1, -1
7558 Xalpha_5, TRUE, FALSE,
7559 EL_CHAR('5'), -1, -1
7562 Xalpha_6, TRUE, FALSE,
7563 EL_CHAR('6'), -1, -1
7566 Xalpha_7, TRUE, FALSE,
7567 EL_CHAR('7'), -1, -1
7570 Xalpha_8, TRUE, FALSE,
7571 EL_CHAR('8'), -1, -1
7574 Xalpha_9, TRUE, FALSE,
7575 EL_CHAR('9'), -1, -1
7578 Xalpha_excla, TRUE, FALSE,
7579 EL_CHAR('!'), -1, -1
7582 Xalpha_apost, TRUE, FALSE,
7583 EL_CHAR('\''), -1, -1
7586 Xalpha_comma, TRUE, FALSE,
7587 EL_CHAR(','), -1, -1
7590 Xalpha_minus, TRUE, FALSE,
7591 EL_CHAR('-'), -1, -1
7594 Xalpha_perio, TRUE, FALSE,
7595 EL_CHAR('.'), -1, -1
7598 Xalpha_colon, TRUE, FALSE,
7599 EL_CHAR(':'), -1, -1
7602 Xalpha_quest, TRUE, FALSE,
7603 EL_CHAR('?'), -1, -1
7606 Xalpha_a, TRUE, FALSE,
7607 EL_CHAR('A'), -1, -1
7610 Xalpha_b, TRUE, FALSE,
7611 EL_CHAR('B'), -1, -1
7614 Xalpha_c, TRUE, FALSE,
7615 EL_CHAR('C'), -1, -1
7618 Xalpha_d, TRUE, FALSE,
7619 EL_CHAR('D'), -1, -1
7622 Xalpha_e, TRUE, FALSE,
7623 EL_CHAR('E'), -1, -1
7626 Xalpha_f, TRUE, FALSE,
7627 EL_CHAR('F'), -1, -1
7630 Xalpha_g, TRUE, FALSE,
7631 EL_CHAR('G'), -1, -1
7634 Xalpha_h, TRUE, FALSE,
7635 EL_CHAR('H'), -1, -1
7638 Xalpha_i, TRUE, FALSE,
7639 EL_CHAR('I'), -1, -1
7642 Xalpha_j, TRUE, FALSE,
7643 EL_CHAR('J'), -1, -1
7646 Xalpha_k, TRUE, FALSE,
7647 EL_CHAR('K'), -1, -1
7650 Xalpha_l, TRUE, FALSE,
7651 EL_CHAR('L'), -1, -1
7654 Xalpha_m, TRUE, FALSE,
7655 EL_CHAR('M'), -1, -1
7658 Xalpha_n, TRUE, FALSE,
7659 EL_CHAR('N'), -1, -1
7662 Xalpha_o, TRUE, FALSE,
7663 EL_CHAR('O'), -1, -1
7666 Xalpha_p, TRUE, FALSE,
7667 EL_CHAR('P'), -1, -1
7670 Xalpha_q, TRUE, FALSE,
7671 EL_CHAR('Q'), -1, -1
7674 Xalpha_r, TRUE, FALSE,
7675 EL_CHAR('R'), -1, -1
7678 Xalpha_s, TRUE, FALSE,
7679 EL_CHAR('S'), -1, -1
7682 Xalpha_t, TRUE, FALSE,
7683 EL_CHAR('T'), -1, -1
7686 Xalpha_u, TRUE, FALSE,
7687 EL_CHAR('U'), -1, -1
7690 Xalpha_v, TRUE, FALSE,
7691 EL_CHAR('V'), -1, -1
7694 Xalpha_w, TRUE, FALSE,
7695 EL_CHAR('W'), -1, -1
7698 Xalpha_x, TRUE, FALSE,
7699 EL_CHAR('X'), -1, -1
7702 Xalpha_y, TRUE, FALSE,
7703 EL_CHAR('Y'), -1, -1
7706 Xalpha_z, TRUE, FALSE,
7707 EL_CHAR('Z'), -1, -1
7710 Xalpha_arrow_e, TRUE, FALSE,
7711 EL_CHAR('>'), -1, -1
7714 Xalpha_arrow_w, TRUE, FALSE,
7715 EL_CHAR('<'), -1, -1
7718 Xalpha_copyr, TRUE, FALSE,
7719 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7723 Ykey_1_blank, FALSE, FALSE,
7724 EL_EM_KEY_1, ACTION_COLLECTING, -1
7727 Ykey_2_blank, FALSE, FALSE,
7728 EL_EM_KEY_2, ACTION_COLLECTING, -1
7731 Ykey_3_blank, FALSE, FALSE,
7732 EL_EM_KEY_3, ACTION_COLLECTING, -1
7735 Ykey_4_blank, FALSE, FALSE,
7736 EL_EM_KEY_4, ACTION_COLLECTING, -1
7739 Ykey_5_blank, FALSE, FALSE,
7740 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7743 Ykey_6_blank, FALSE, FALSE,
7744 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7747 Ykey_7_blank, FALSE, FALSE,
7748 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7751 Ykey_8_blank, FALSE, FALSE,
7752 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7755 Ylenses_blank, FALSE, FALSE,
7756 EL_EMC_LENSES, ACTION_COLLECTING, -1
7759 Ymagnify_blank, FALSE, FALSE,
7760 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7763 Ygrass_blank, FALSE, FALSE,
7764 EL_EMC_GRASS, ACTION_SNAPPING, -1
7767 Ydirt_blank, FALSE, FALSE,
7768 EL_SAND, ACTION_SNAPPING, -1
7777 static struct Mapping_EM_to_RND_player
7786 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7790 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7794 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7798 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7802 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7806 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7810 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7814 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7818 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7822 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7826 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7830 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7834 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7838 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7842 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7846 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7850 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7854 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7858 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7862 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7866 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7870 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7874 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7878 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7882 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7886 EL_PLAYER_1, ACTION_DEFAULT, -1,
7890 EL_PLAYER_2, ACTION_DEFAULT, -1,
7894 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7898 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7902 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7906 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7910 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7914 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7918 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7922 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7926 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7930 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7934 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7938 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7942 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7946 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7950 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7954 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7958 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7962 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7966 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7970 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7974 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7978 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7982 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7986 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7990 EL_PLAYER_3, ACTION_DEFAULT, -1,
7994 EL_PLAYER_4, ACTION_DEFAULT, -1,
8003 int map_element_RND_to_EM_cave(int element_rnd)
8005 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
8006 static boolean mapping_initialized = FALSE;
8008 if (!mapping_initialized)
8012 // return "Xalpha_quest" for all undefined elements in mapping array
8013 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8014 mapping_RND_to_EM[i] = Xalpha_quest;
8016 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8017 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
8018 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
8019 em_object_mapping_list[i].element_em;
8021 mapping_initialized = TRUE;
8024 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
8026 Warn("invalid RND level element %d", element_rnd);
8031 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
8034 int map_element_EM_to_RND_cave(int element_em_cave)
8036 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8037 static boolean mapping_initialized = FALSE;
8039 if (!mapping_initialized)
8043 // return "EL_UNKNOWN" for all undefined elements in mapping array
8044 for (i = 0; i < GAME_TILE_MAX; i++)
8045 mapping_EM_to_RND[i] = EL_UNKNOWN;
8047 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8048 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8049 em_object_mapping_list[i].element_rnd;
8051 mapping_initialized = TRUE;
8054 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8056 Warn("invalid EM cave element %d", element_em_cave);
8061 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8064 int map_element_EM_to_RND_game(int element_em_game)
8066 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8067 static boolean mapping_initialized = FALSE;
8069 if (!mapping_initialized)
8073 // return "EL_UNKNOWN" for all undefined elements in mapping array
8074 for (i = 0; i < GAME_TILE_MAX; i++)
8075 mapping_EM_to_RND[i] = EL_UNKNOWN;
8077 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8078 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8079 em_object_mapping_list[i].element_rnd;
8081 mapping_initialized = TRUE;
8084 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8086 Warn("invalid EM game element %d", element_em_game);
8091 return mapping_EM_to_RND[element_em_game];
8094 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8096 struct LevelInfo_EM *level_em = level->native_em_level;
8097 struct CAVE *cav = level_em->cav;
8100 for (i = 0; i < GAME_TILE_MAX; i++)
8101 cav->android_array[i] = Cblank;
8103 for (i = 0; i < level->num_android_clone_elements; i++)
8105 int element_rnd = level->android_clone_element[i];
8106 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8108 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8109 if (em_object_mapping_list[j].element_rnd == element_rnd)
8110 cav->android_array[em_object_mapping_list[j].element_em] =
8115 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8117 struct LevelInfo_EM *level_em = level->native_em_level;
8118 struct CAVE *cav = level_em->cav;
8121 level->num_android_clone_elements = 0;
8123 for (i = 0; i < GAME_TILE_MAX; i++)
8125 int element_em_cave = cav->android_array[i];
8127 boolean element_found = FALSE;
8129 if (element_em_cave == Cblank)
8132 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8134 for (j = 0; j < level->num_android_clone_elements; j++)
8135 if (level->android_clone_element[j] == element_rnd)
8136 element_found = TRUE;
8140 level->android_clone_element[level->num_android_clone_elements++] =
8143 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8148 if (level->num_android_clone_elements == 0)
8150 level->num_android_clone_elements = 1;
8151 level->android_clone_element[0] = EL_EMPTY;
8155 int map_direction_RND_to_EM(int direction)
8157 return (direction == MV_UP ? 0 :
8158 direction == MV_RIGHT ? 1 :
8159 direction == MV_DOWN ? 2 :
8160 direction == MV_LEFT ? 3 :
8164 int map_direction_EM_to_RND(int direction)
8166 return (direction == 0 ? MV_UP :
8167 direction == 1 ? MV_RIGHT :
8168 direction == 2 ? MV_DOWN :
8169 direction == 3 ? MV_LEFT :
8173 int map_element_RND_to_SP(int element_rnd)
8175 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8177 if (element_rnd >= EL_SP_START &&
8178 element_rnd <= EL_SP_END)
8179 element_sp = element_rnd - EL_SP_START;
8180 else if (element_rnd == EL_EMPTY_SPACE)
8182 else if (element_rnd == EL_INVISIBLE_WALL)
8188 int map_element_SP_to_RND(int element_sp)
8190 int element_rnd = EL_UNKNOWN;
8192 if (element_sp >= 0x00 &&
8194 element_rnd = EL_SP_START + element_sp;
8195 else if (element_sp == 0x28)
8196 element_rnd = EL_INVISIBLE_WALL;
8201 int map_action_SP_to_RND(int action_sp)
8205 case actActive: return ACTION_ACTIVE;
8206 case actImpact: return ACTION_IMPACT;
8207 case actExploding: return ACTION_EXPLODING;
8208 case actDigging: return ACTION_DIGGING;
8209 case actSnapping: return ACTION_SNAPPING;
8210 case actCollecting: return ACTION_COLLECTING;
8211 case actPassing: return ACTION_PASSING;
8212 case actPushing: return ACTION_PUSHING;
8213 case actDropping: return ACTION_DROPPING;
8215 default: return ACTION_DEFAULT;
8219 int map_element_RND_to_MM(int element_rnd)
8221 return (element_rnd >= EL_MM_START_1 &&
8222 element_rnd <= EL_MM_END_1 ?
8223 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8225 element_rnd >= EL_MM_START_2 &&
8226 element_rnd <= EL_MM_END_2 ?
8227 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8229 element_rnd >= EL_MM_START_3 &&
8230 element_rnd <= EL_MM_END_3 ?
8231 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
8233 element_rnd >= EL_CHAR_START &&
8234 element_rnd <= EL_CHAR_END ?
8235 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8237 element_rnd >= EL_MM_RUNTIME_START &&
8238 element_rnd <= EL_MM_RUNTIME_END ?
8239 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8241 EL_MM_EMPTY_NATIVE);
8244 int map_element_MM_to_RND(int element_mm)
8246 return (element_mm == EL_MM_EMPTY_NATIVE ||
8247 element_mm == EL_DF_EMPTY_NATIVE ?
8250 element_mm >= EL_MM_START_1_NATIVE &&
8251 element_mm <= EL_MM_END_1_NATIVE ?
8252 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8254 element_mm >= EL_MM_START_2_NATIVE &&
8255 element_mm <= EL_MM_END_2_NATIVE ?
8256 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8258 element_mm >= EL_MM_START_3_NATIVE &&
8259 element_mm <= EL_MM_END_3_NATIVE ?
8260 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
8262 element_mm >= EL_MM_CHAR_START_NATIVE &&
8263 element_mm <= EL_MM_CHAR_END_NATIVE ?
8264 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8266 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8267 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8268 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8273 int map_action_MM_to_RND(int action_mm)
8275 // all MM actions are defined to exactly match their RND counterparts
8279 int map_sound_MM_to_RND(int sound_mm)
8283 case SND_MM_GAME_LEVELTIME_CHARGING:
8284 return SND_GAME_LEVELTIME_CHARGING;
8286 case SND_MM_GAME_HEALTH_CHARGING:
8287 return SND_GAME_HEALTH_CHARGING;
8290 return SND_UNDEFINED;
8294 int map_mm_wall_element(int element)
8296 return (element >= EL_MM_STEEL_WALL_START &&
8297 element <= EL_MM_STEEL_WALL_END ?
8300 element >= EL_MM_WOODEN_WALL_START &&
8301 element <= EL_MM_WOODEN_WALL_END ?
8304 element >= EL_MM_ICE_WALL_START &&
8305 element <= EL_MM_ICE_WALL_END ?
8308 element >= EL_MM_AMOEBA_WALL_START &&
8309 element <= EL_MM_AMOEBA_WALL_END ?
8312 element >= EL_DF_STEEL_WALL_START &&
8313 element <= EL_DF_STEEL_WALL_END ?
8316 element >= EL_DF_WOODEN_WALL_START &&
8317 element <= EL_DF_WOODEN_WALL_END ?
8323 int map_mm_wall_element_editor(int element)
8327 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8328 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8329 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8330 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8331 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8332 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8334 default: return element;
8338 int get_next_element(int element)
8342 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8343 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8344 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8345 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8346 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8347 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8348 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8349 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8350 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8351 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8352 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8354 default: return element;
8358 int el2img_mm(int element_mm)
8360 return el2img(map_element_MM_to_RND(element_mm));
8363 int el_act2img_mm(int element_mm, int action)
8365 return el_act2img(map_element_MM_to_RND(element_mm), action);
8368 int el_act_dir2img(int element, int action, int direction)
8370 element = GFX_ELEMENT(element);
8371 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8373 // direction_graphic[][] == graphic[] for undefined direction graphics
8374 return element_info[element].direction_graphic[action][direction];
8377 static int el_act_dir2crm(int element, int action, int direction)
8379 element = GFX_ELEMENT(element);
8380 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8382 // direction_graphic[][] == graphic[] for undefined direction graphics
8383 return element_info[element].direction_crumbled[action][direction];
8386 int el_act2img(int element, int action)
8388 element = GFX_ELEMENT(element);
8390 return element_info[element].graphic[action];
8393 int el_act2crm(int element, int action)
8395 element = GFX_ELEMENT(element);
8397 return element_info[element].crumbled[action];
8400 int el_dir2img(int element, int direction)
8402 element = GFX_ELEMENT(element);
8404 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8407 int el2baseimg(int element)
8409 return element_info[element].graphic[ACTION_DEFAULT];
8412 int el2img(int element)
8414 element = GFX_ELEMENT(element);
8416 return element_info[element].graphic[ACTION_DEFAULT];
8419 int el2edimg(int element)
8421 element = GFX_ELEMENT(element);
8423 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8426 int el2preimg(int element)
8428 element = GFX_ELEMENT(element);
8430 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8433 int el2panelimg(int element)
8435 element = GFX_ELEMENT(element);
8437 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8440 int font2baseimg(int font_nr)
8442 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8445 int getBeltNrFromBeltElement(int element)
8447 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8448 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8449 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8452 int getBeltNrFromBeltActiveElement(int element)
8454 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8455 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8456 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8459 int getBeltNrFromBeltSwitchElement(int element)
8461 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8462 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8463 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8466 int getBeltDirNrFromBeltElement(int element)
8468 static int belt_base_element[4] =
8470 EL_CONVEYOR_BELT_1_LEFT,
8471 EL_CONVEYOR_BELT_2_LEFT,
8472 EL_CONVEYOR_BELT_3_LEFT,
8473 EL_CONVEYOR_BELT_4_LEFT
8476 int belt_nr = getBeltNrFromBeltElement(element);
8477 int belt_dir_nr = element - belt_base_element[belt_nr];
8479 return (belt_dir_nr % 3);
8482 int getBeltDirNrFromBeltSwitchElement(int element)
8484 static int belt_base_element[4] =
8486 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8487 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8488 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8489 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8492 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8493 int belt_dir_nr = element - belt_base_element[belt_nr];
8495 return (belt_dir_nr % 3);
8498 int getBeltDirFromBeltElement(int element)
8500 static int belt_move_dir[3] =
8507 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8509 return belt_move_dir[belt_dir_nr];
8512 int getBeltDirFromBeltSwitchElement(int element)
8514 static int belt_move_dir[3] =
8521 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8523 return belt_move_dir[belt_dir_nr];
8526 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8528 static int belt_base_element[4] =
8530 EL_CONVEYOR_BELT_1_LEFT,
8531 EL_CONVEYOR_BELT_2_LEFT,
8532 EL_CONVEYOR_BELT_3_LEFT,
8533 EL_CONVEYOR_BELT_4_LEFT
8536 return belt_base_element[belt_nr] + belt_dir_nr;
8539 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8541 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8543 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8546 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8548 static int belt_base_element[4] =
8550 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8551 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8552 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8553 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8556 return belt_base_element[belt_nr] + belt_dir_nr;
8559 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8561 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8563 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8566 boolean swapTiles_EM(boolean is_pre_emc_cave)
8568 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8571 boolean getTeamMode_EM(void)
8573 return game.team_mode || network_playing;
8576 boolean isActivePlayer_EM(int player_nr)
8578 return stored_player[player_nr].active;
8581 unsigned int InitRND(int seed)
8583 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8584 return InitEngineRandom_EM(seed);
8585 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8586 return InitEngineRandom_SP(seed);
8587 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8588 return InitEngineRandom_MM(seed);
8590 return InitEngineRandom_RND(seed);
8593 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8594 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8596 static int get_effective_element_EM(int tile, int frame_em)
8598 int element = object_mapping[tile].element_rnd;
8599 int action = object_mapping[tile].action;
8600 boolean is_backside = object_mapping[tile].is_backside;
8601 boolean action_removing = (action == ACTION_DIGGING ||
8602 action == ACTION_SNAPPING ||
8603 action == ACTION_COLLECTING);
8611 return (frame_em > 5 ? EL_EMPTY : element);
8617 else // frame_em == 7
8628 case Ydiamond_stone:
8632 case Xdrip_stretchB:
8648 case Ymagnify_blank:
8651 case Xsand_stonein_1:
8652 case Xsand_stonein_2:
8653 case Xsand_stonein_3:
8654 case Xsand_stonein_4:
8658 return (is_backside || action_removing ? EL_EMPTY : element);
8663 static boolean check_linear_animation_EM(int tile)
8667 case Xsand_stonesand_1:
8668 case Xsand_stonesand_quickout_1:
8669 case Xsand_sandstone_1:
8670 case Xsand_stonein_1:
8671 case Xsand_stoneout_1:
8699 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8700 boolean has_crumbled_graphics,
8701 int crumbled, int sync_frame)
8703 // if element can be crumbled, but certain action graphics are just empty
8704 // space (like instantly snapping sand to empty space in 1 frame), do not
8705 // treat these empty space graphics as crumbled graphics in EMC engine
8706 if (crumbled == IMG_EMPTY_SPACE)
8707 has_crumbled_graphics = FALSE;
8709 if (has_crumbled_graphics)
8711 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8712 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8713 g_crumbled->anim_delay,
8714 g_crumbled->anim_mode,
8715 g_crumbled->anim_start_frame,
8718 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8719 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8721 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8722 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8724 g_em->has_crumbled_graphics = TRUE;
8728 g_em->crumbled_bitmap = NULL;
8729 g_em->crumbled_src_x = 0;
8730 g_em->crumbled_src_y = 0;
8731 g_em->crumbled_border_size = 0;
8732 g_em->crumbled_tile_size = 0;
8734 g_em->has_crumbled_graphics = FALSE;
8739 void ResetGfxAnimation_EM(int x, int y, int tile)
8745 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8746 int tile, int frame_em, int x, int y)
8748 int action = object_mapping[tile].action;
8749 int direction = object_mapping[tile].direction;
8750 int effective_element = get_effective_element_EM(tile, frame_em);
8751 int graphic = (direction == MV_NONE ?
8752 el_act2img(effective_element, action) :
8753 el_act_dir2img(effective_element, action, direction));
8754 struct GraphicInfo *g = &graphic_info[graphic];
8756 boolean action_removing = (action == ACTION_DIGGING ||
8757 action == ACTION_SNAPPING ||
8758 action == ACTION_COLLECTING);
8759 boolean action_moving = (action == ACTION_FALLING ||
8760 action == ACTION_MOVING ||
8761 action == ACTION_PUSHING ||
8762 action == ACTION_EATING ||
8763 action == ACTION_FILLING ||
8764 action == ACTION_EMPTYING);
8765 boolean action_falling = (action == ACTION_FALLING ||
8766 action == ACTION_FILLING ||
8767 action == ACTION_EMPTYING);
8769 // special case: graphic uses "2nd movement tile" and has defined
8770 // 7 frames for movement animation (or less) => use default graphic
8771 // for last (8th) frame which ends the movement animation
8772 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8774 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8775 graphic = (direction == MV_NONE ?
8776 el_act2img(effective_element, action) :
8777 el_act_dir2img(effective_element, action, direction));
8779 g = &graphic_info[graphic];
8782 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8786 else if (action_moving)
8788 boolean is_backside = object_mapping[tile].is_backside;
8792 int direction = object_mapping[tile].direction;
8793 int move_dir = (action_falling ? MV_DOWN : direction);
8798 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8799 if (g->double_movement && frame_em == 0)
8803 if (move_dir == MV_LEFT)
8804 GfxFrame[x - 1][y] = GfxFrame[x][y];
8805 else if (move_dir == MV_RIGHT)
8806 GfxFrame[x + 1][y] = GfxFrame[x][y];
8807 else if (move_dir == MV_UP)
8808 GfxFrame[x][y - 1] = GfxFrame[x][y];
8809 else if (move_dir == MV_DOWN)
8810 GfxFrame[x][y + 1] = GfxFrame[x][y];
8817 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8818 if (tile == Xsand_stonesand_quickout_1 ||
8819 tile == Xsand_stonesand_quickout_2)
8823 if (graphic_info[graphic].anim_global_sync)
8824 sync_frame = FrameCounter;
8825 else if (graphic_info[graphic].anim_global_anim_sync)
8826 sync_frame = getGlobalAnimSyncFrame();
8827 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8828 sync_frame = GfxFrame[x][y];
8830 sync_frame = 0; // playfield border (pseudo steel)
8832 SetRandomAnimationValue(x, y);
8834 int frame = getAnimationFrame(g->anim_frames,
8837 g->anim_start_frame,
8840 g_em->unique_identifier =
8841 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8844 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8845 int tile, int frame_em, int x, int y)
8847 int action = object_mapping[tile].action;
8848 int direction = object_mapping[tile].direction;
8849 boolean is_backside = object_mapping[tile].is_backside;
8850 int effective_element = get_effective_element_EM(tile, frame_em);
8851 int effective_action = action;
8852 int graphic = (direction == MV_NONE ?
8853 el_act2img(effective_element, effective_action) :
8854 el_act_dir2img(effective_element, effective_action,
8856 int crumbled = (direction == MV_NONE ?
8857 el_act2crm(effective_element, effective_action) :
8858 el_act_dir2crm(effective_element, effective_action,
8860 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8861 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8862 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8863 struct GraphicInfo *g = &graphic_info[graphic];
8866 // special case: graphic uses "2nd movement tile" and has defined
8867 // 7 frames for movement animation (or less) => use default graphic
8868 // for last (8th) frame which ends the movement animation
8869 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8871 effective_action = ACTION_DEFAULT;
8872 graphic = (direction == MV_NONE ?
8873 el_act2img(effective_element, effective_action) :
8874 el_act_dir2img(effective_element, effective_action,
8876 crumbled = (direction == MV_NONE ?
8877 el_act2crm(effective_element, effective_action) :
8878 el_act_dir2crm(effective_element, effective_action,
8881 g = &graphic_info[graphic];
8884 if (graphic_info[graphic].anim_global_sync)
8885 sync_frame = FrameCounter;
8886 else if (graphic_info[graphic].anim_global_anim_sync)
8887 sync_frame = getGlobalAnimSyncFrame();
8888 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8889 sync_frame = GfxFrame[x][y];
8891 sync_frame = 0; // playfield border (pseudo steel)
8893 SetRandomAnimationValue(x, y);
8895 int frame = getAnimationFrame(g->anim_frames,
8898 g->anim_start_frame,
8901 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8902 g->double_movement && is_backside);
8904 // (updating the "crumbled" graphic definitions is probably not really needed,
8905 // as animations for crumbled graphics can't be longer than one EMC cycle)
8906 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8910 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8911 int player_nr, int anim, int frame_em)
8913 int element = player_mapping[player_nr][anim].element_rnd;
8914 int action = player_mapping[player_nr][anim].action;
8915 int direction = player_mapping[player_nr][anim].direction;
8916 int graphic = (direction == MV_NONE ?
8917 el_act2img(element, action) :
8918 el_act_dir2img(element, action, direction));
8919 struct GraphicInfo *g = &graphic_info[graphic];
8922 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8924 stored_player[player_nr].StepFrame = frame_em;
8926 sync_frame = stored_player[player_nr].Frame;
8928 int frame = getAnimationFrame(g->anim_frames,
8931 g->anim_start_frame,
8934 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8935 &g_em->src_x, &g_em->src_y, FALSE);
8938 void InitGraphicInfo_EM(void)
8942 // always start with reliable default values
8943 for (i = 0; i < GAME_TILE_MAX; i++)
8945 object_mapping[i].element_rnd = EL_UNKNOWN;
8946 object_mapping[i].is_backside = FALSE;
8947 object_mapping[i].action = ACTION_DEFAULT;
8948 object_mapping[i].direction = MV_NONE;
8951 // always start with reliable default values
8952 for (p = 0; p < MAX_PLAYERS; p++)
8954 for (i = 0; i < PLY_MAX; i++)
8956 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8957 player_mapping[p][i].action = ACTION_DEFAULT;
8958 player_mapping[p][i].direction = MV_NONE;
8962 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8964 int e = em_object_mapping_list[i].element_em;
8966 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8967 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8969 if (em_object_mapping_list[i].action != -1)
8970 object_mapping[e].action = em_object_mapping_list[i].action;
8972 if (em_object_mapping_list[i].direction != -1)
8973 object_mapping[e].direction =
8974 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8977 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8979 int a = em_player_mapping_list[i].action_em;
8980 int p = em_player_mapping_list[i].player_nr;
8982 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8984 if (em_player_mapping_list[i].action != -1)
8985 player_mapping[p][a].action = em_player_mapping_list[i].action;
8987 if (em_player_mapping_list[i].direction != -1)
8988 player_mapping[p][a].direction =
8989 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8992 for (i = 0; i < GAME_TILE_MAX; i++)
8994 int element = object_mapping[i].element_rnd;
8995 int action = object_mapping[i].action;
8996 int direction = object_mapping[i].direction;
8997 boolean is_backside = object_mapping[i].is_backside;
8998 boolean action_exploding = ((action == ACTION_EXPLODING ||
8999 action == ACTION_SMASHED_BY_ROCK ||
9000 action == ACTION_SMASHED_BY_SPRING) &&
9001 element != EL_DIAMOND);
9002 boolean action_active = (action == ACTION_ACTIVE);
9003 boolean action_other = (action == ACTION_OTHER);
9005 for (j = 0; j < 8; j++)
9007 int effective_element = get_effective_element_EM(i, j);
9008 int effective_action = (j < 7 ? action :
9009 i == Xdrip_stretch ? action :
9010 i == Xdrip_stretchB ? action :
9011 i == Ydrip_1_s ? action :
9012 i == Ydrip_1_sB ? action :
9013 i == Yball_1 ? action :
9014 i == Xball_2 ? action :
9015 i == Yball_2 ? action :
9016 i == Yball_blank ? action :
9017 i == Ykey_1_blank ? action :
9018 i == Ykey_2_blank ? action :
9019 i == Ykey_3_blank ? action :
9020 i == Ykey_4_blank ? action :
9021 i == Ykey_5_blank ? action :
9022 i == Ykey_6_blank ? action :
9023 i == Ykey_7_blank ? action :
9024 i == Ykey_8_blank ? action :
9025 i == Ylenses_blank ? action :
9026 i == Ymagnify_blank ? action :
9027 i == Ygrass_blank ? action :
9028 i == Ydirt_blank ? action :
9029 i == Xsand_stonein_1 ? action :
9030 i == Xsand_stonein_2 ? action :
9031 i == Xsand_stonein_3 ? action :
9032 i == Xsand_stonein_4 ? action :
9033 i == Xsand_stoneout_1 ? action :
9034 i == Xsand_stoneout_2 ? action :
9035 i == Xboom_android ? ACTION_EXPLODING :
9036 action_exploding ? ACTION_EXPLODING :
9037 action_active ? action :
9038 action_other ? action :
9040 int graphic = (el_act_dir2img(effective_element, effective_action,
9042 int crumbled = (el_act_dir2crm(effective_element, effective_action,
9044 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
9045 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
9046 boolean has_action_graphics = (graphic != base_graphic);
9047 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9048 struct GraphicInfo *g = &graphic_info[graphic];
9049 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9052 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9053 boolean special_animation = (action != ACTION_DEFAULT &&
9054 g->anim_frames == 3 &&
9055 g->anim_delay == 2 &&
9056 g->anim_mode & ANIM_LINEAR);
9057 int sync_frame = (i == Xdrip_stretch ? 7 :
9058 i == Xdrip_stretchB ? 7 :
9059 i == Ydrip_2_s ? j + 8 :
9060 i == Ydrip_2_sB ? j + 8 :
9069 i == Xfake_acid_1 ? 0 :
9070 i == Xfake_acid_2 ? 10 :
9071 i == Xfake_acid_3 ? 20 :
9072 i == Xfake_acid_4 ? 30 :
9073 i == Xfake_acid_5 ? 40 :
9074 i == Xfake_acid_6 ? 50 :
9075 i == Xfake_acid_7 ? 60 :
9076 i == Xfake_acid_8 ? 70 :
9077 i == Xfake_acid_1_player ? 0 :
9078 i == Xfake_acid_2_player ? 10 :
9079 i == Xfake_acid_3_player ? 20 :
9080 i == Xfake_acid_4_player ? 30 :
9081 i == Xfake_acid_5_player ? 40 :
9082 i == Xfake_acid_6_player ? 50 :
9083 i == Xfake_acid_7_player ? 60 :
9084 i == Xfake_acid_8_player ? 70 :
9086 i == Yball_2 ? j + 8 :
9087 i == Yball_blank ? j + 1 :
9088 i == Ykey_1_blank ? j + 1 :
9089 i == Ykey_2_blank ? j + 1 :
9090 i == Ykey_3_blank ? j + 1 :
9091 i == Ykey_4_blank ? j + 1 :
9092 i == Ykey_5_blank ? j + 1 :
9093 i == Ykey_6_blank ? j + 1 :
9094 i == Ykey_7_blank ? j + 1 :
9095 i == Ykey_8_blank ? j + 1 :
9096 i == Ylenses_blank ? j + 1 :
9097 i == Ymagnify_blank ? j + 1 :
9098 i == Ygrass_blank ? j + 1 :
9099 i == Ydirt_blank ? j + 1 :
9100 i == Xamoeba_1 ? 0 :
9101 i == Xamoeba_2 ? 1 :
9102 i == Xamoeba_3 ? 2 :
9103 i == Xamoeba_4 ? 3 :
9104 i == Xamoeba_5 ? 0 :
9105 i == Xamoeba_6 ? 1 :
9106 i == Xamoeba_7 ? 2 :
9107 i == Xamoeba_8 ? 3 :
9108 i == Xexit_2 ? j + 8 :
9109 i == Xexit_3 ? j + 16 :
9110 i == Xdynamite_1 ? 0 :
9111 i == Xdynamite_2 ? 8 :
9112 i == Xdynamite_3 ? 16 :
9113 i == Xdynamite_4 ? 24 :
9114 i == Xsand_stonein_1 ? j + 1 :
9115 i == Xsand_stonein_2 ? j + 9 :
9116 i == Xsand_stonein_3 ? j + 17 :
9117 i == Xsand_stonein_4 ? j + 25 :
9118 i == Xsand_stoneout_1 && j == 0 ? 0 :
9119 i == Xsand_stoneout_1 && j == 1 ? 0 :
9120 i == Xsand_stoneout_1 && j == 2 ? 1 :
9121 i == Xsand_stoneout_1 && j == 3 ? 2 :
9122 i == Xsand_stoneout_1 && j == 4 ? 2 :
9123 i == Xsand_stoneout_1 && j == 5 ? 3 :
9124 i == Xsand_stoneout_1 && j == 6 ? 4 :
9125 i == Xsand_stoneout_1 && j == 7 ? 4 :
9126 i == Xsand_stoneout_2 && j == 0 ? 5 :
9127 i == Xsand_stoneout_2 && j == 1 ? 6 :
9128 i == Xsand_stoneout_2 && j == 2 ? 7 :
9129 i == Xsand_stoneout_2 && j == 3 ? 8 :
9130 i == Xsand_stoneout_2 && j == 4 ? 9 :
9131 i == Xsand_stoneout_2 && j == 5 ? 11 :
9132 i == Xsand_stoneout_2 && j == 6 ? 13 :
9133 i == Xsand_stoneout_2 && j == 7 ? 15 :
9134 i == Xboom_bug && j == 1 ? 2 :
9135 i == Xboom_bug && j == 2 ? 2 :
9136 i == Xboom_bug && j == 3 ? 4 :
9137 i == Xboom_bug && j == 4 ? 4 :
9138 i == Xboom_bug && j == 5 ? 2 :
9139 i == Xboom_bug && j == 6 ? 2 :
9140 i == Xboom_bug && j == 7 ? 0 :
9141 i == Xboom_tank && j == 1 ? 2 :
9142 i == Xboom_tank && j == 2 ? 2 :
9143 i == Xboom_tank && j == 3 ? 4 :
9144 i == Xboom_tank && j == 4 ? 4 :
9145 i == Xboom_tank && j == 5 ? 2 :
9146 i == Xboom_tank && j == 6 ? 2 :
9147 i == Xboom_tank && j == 7 ? 0 :
9148 i == Xboom_android && j == 7 ? 6 :
9149 i == Xboom_1 && j == 1 ? 2 :
9150 i == Xboom_1 && j == 2 ? 2 :
9151 i == Xboom_1 && j == 3 ? 4 :
9152 i == Xboom_1 && j == 4 ? 4 :
9153 i == Xboom_1 && j == 5 ? 6 :
9154 i == Xboom_1 && j == 6 ? 6 :
9155 i == Xboom_1 && j == 7 ? 8 :
9156 i == Xboom_2 && j == 0 ? 8 :
9157 i == Xboom_2 && j == 1 ? 8 :
9158 i == Xboom_2 && j == 2 ? 10 :
9159 i == Xboom_2 && j == 3 ? 10 :
9160 i == Xboom_2 && j == 4 ? 10 :
9161 i == Xboom_2 && j == 5 ? 12 :
9162 i == Xboom_2 && j == 6 ? 12 :
9163 i == Xboom_2 && j == 7 ? 12 :
9164 special_animation && j == 4 ? 3 :
9165 effective_action != action ? 0 :
9167 int frame = getAnimationFrame(g->anim_frames,
9170 g->anim_start_frame,
9173 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9174 g->double_movement && is_backside);
9176 g_em->bitmap = src_bitmap;
9177 g_em->src_x = src_x;
9178 g_em->src_y = src_y;
9179 g_em->src_offset_x = 0;
9180 g_em->src_offset_y = 0;
9181 g_em->dst_offset_x = 0;
9182 g_em->dst_offset_y = 0;
9183 g_em->width = TILEX;
9184 g_em->height = TILEY;
9186 g_em->preserve_background = FALSE;
9188 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9191 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9192 effective_action == ACTION_MOVING ||
9193 effective_action == ACTION_PUSHING ||
9194 effective_action == ACTION_EATING)) ||
9195 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9196 effective_action == ACTION_EMPTYING)))
9199 (effective_action == ACTION_FALLING ||
9200 effective_action == ACTION_FILLING ||
9201 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9202 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9203 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9204 int num_steps = (i == Ydrip_1_s ? 16 :
9205 i == Ydrip_1_sB ? 16 :
9206 i == Ydrip_2_s ? 16 :
9207 i == Ydrip_2_sB ? 16 :
9208 i == Xsand_stonein_1 ? 32 :
9209 i == Xsand_stonein_2 ? 32 :
9210 i == Xsand_stonein_3 ? 32 :
9211 i == Xsand_stonein_4 ? 32 :
9212 i == Xsand_stoneout_1 ? 16 :
9213 i == Xsand_stoneout_2 ? 16 : 8);
9214 int cx = ABS(dx) * (TILEX / num_steps);
9215 int cy = ABS(dy) * (TILEY / num_steps);
9216 int step_frame = (i == Ydrip_2_s ? j + 8 :
9217 i == Ydrip_2_sB ? j + 8 :
9218 i == Xsand_stonein_2 ? j + 8 :
9219 i == Xsand_stonein_3 ? j + 16 :
9220 i == Xsand_stonein_4 ? j + 24 :
9221 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9222 int step = (is_backside ? step_frame : num_steps - step_frame);
9224 if (is_backside) // tile where movement starts
9226 if (dx < 0 || dy < 0)
9228 g_em->src_offset_x = cx * step;
9229 g_em->src_offset_y = cy * step;
9233 g_em->dst_offset_x = cx * step;
9234 g_em->dst_offset_y = cy * step;
9237 else // tile where movement ends
9239 if (dx < 0 || dy < 0)
9241 g_em->dst_offset_x = cx * step;
9242 g_em->dst_offset_y = cy * step;
9246 g_em->src_offset_x = cx * step;
9247 g_em->src_offset_y = cy * step;
9251 g_em->width = TILEX - cx * step;
9252 g_em->height = TILEY - cy * step;
9255 // create unique graphic identifier to decide if tile must be redrawn
9256 /* bit 31 - 16 (16 bit): EM style graphic
9257 bit 15 - 12 ( 4 bit): EM style frame
9258 bit 11 - 6 ( 6 bit): graphic width
9259 bit 5 - 0 ( 6 bit): graphic height */
9260 g_em->unique_identifier =
9261 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9265 for (i = 0; i < GAME_TILE_MAX; i++)
9267 for (j = 0; j < 8; j++)
9269 int element = object_mapping[i].element_rnd;
9270 int action = object_mapping[i].action;
9271 int direction = object_mapping[i].direction;
9272 boolean is_backside = object_mapping[i].is_backside;
9273 int graphic_action = el_act_dir2img(element, action, direction);
9274 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9276 if ((action == ACTION_SMASHED_BY_ROCK ||
9277 action == ACTION_SMASHED_BY_SPRING ||
9278 action == ACTION_EATING) &&
9279 graphic_action == graphic_default)
9281 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9282 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9283 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9284 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9287 // no separate animation for "smashed by rock" -- use rock instead
9288 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9289 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9291 g_em->bitmap = g_xx->bitmap;
9292 g_em->src_x = g_xx->src_x;
9293 g_em->src_y = g_xx->src_y;
9294 g_em->src_offset_x = g_xx->src_offset_x;
9295 g_em->src_offset_y = g_xx->src_offset_y;
9296 g_em->dst_offset_x = g_xx->dst_offset_x;
9297 g_em->dst_offset_y = g_xx->dst_offset_y;
9298 g_em->width = g_xx->width;
9299 g_em->height = g_xx->height;
9300 g_em->unique_identifier = g_xx->unique_identifier;
9303 g_em->preserve_background = TRUE;
9308 for (p = 0; p < MAX_PLAYERS; p++)
9310 for (i = 0; i < PLY_MAX; i++)
9312 int element = player_mapping[p][i].element_rnd;
9313 int action = player_mapping[p][i].action;
9314 int direction = player_mapping[p][i].direction;
9316 for (j = 0; j < 8; j++)
9318 int effective_element = element;
9319 int effective_action = action;
9320 int graphic = (direction == MV_NONE ?
9321 el_act2img(effective_element, effective_action) :
9322 el_act_dir2img(effective_element, effective_action,
9324 struct GraphicInfo *g = &graphic_info[graphic];
9325 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9329 int frame = getAnimationFrame(g->anim_frames,
9332 g->anim_start_frame,
9335 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9337 g_em->bitmap = src_bitmap;
9338 g_em->src_x = src_x;
9339 g_em->src_y = src_y;
9340 g_em->src_offset_x = 0;
9341 g_em->src_offset_y = 0;
9342 g_em->dst_offset_x = 0;
9343 g_em->dst_offset_y = 0;
9344 g_em->width = TILEX;
9345 g_em->height = TILEY;
9351 static void CheckSaveEngineSnapshot_EM(int frame,
9352 boolean any_player_moving,
9353 boolean any_player_snapping,
9354 boolean any_player_dropping)
9356 if (frame == 7 && !any_player_dropping)
9358 if (!local_player->was_waiting)
9360 if (!CheckSaveEngineSnapshotToList())
9363 local_player->was_waiting = TRUE;
9366 else if (any_player_moving || any_player_snapping || any_player_dropping)
9368 local_player->was_waiting = FALSE;
9372 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9373 boolean murphy_is_dropping)
9375 if (murphy_is_waiting)
9377 if (!local_player->was_waiting)
9379 if (!CheckSaveEngineSnapshotToList())
9382 local_player->was_waiting = TRUE;
9387 local_player->was_waiting = FALSE;
9391 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9392 boolean button_released)
9394 if (button_released)
9396 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9397 CheckSaveEngineSnapshotToList();
9399 else if (element_clicked)
9401 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9402 CheckSaveEngineSnapshotToList();
9404 game.snapshot.changed_action = TRUE;
9408 boolean CheckSingleStepMode_EM(int frame,
9409 boolean any_player_moving,
9410 boolean any_player_snapping,
9411 boolean any_player_dropping)
9413 if (tape.single_step && tape.recording && !tape.pausing)
9414 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9415 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9417 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9418 any_player_snapping, any_player_dropping);
9420 return tape.pausing;
9423 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9424 boolean murphy_is_dropping)
9426 boolean murphy_starts_dropping = FALSE;
9429 for (i = 0; i < MAX_PLAYERS; i++)
9430 if (stored_player[i].force_dropping)
9431 murphy_starts_dropping = TRUE;
9433 if (tape.single_step && tape.recording && !tape.pausing)
9434 if (murphy_is_waiting && !murphy_starts_dropping)
9435 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9437 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9440 void CheckSingleStepMode_MM(boolean element_clicked,
9441 boolean button_released)
9443 if (tape.single_step && tape.recording && !tape.pausing)
9444 if (button_released)
9445 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9447 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9450 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9451 int graphic, int sync_frame)
9453 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9455 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9458 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9460 return (IS_NEXT_FRAME(sync_frame, graphic));
9463 int getGraphicInfo_Delay(int graphic)
9465 return graphic_info[graphic].anim_delay;
9468 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9470 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9473 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9479 void PlayMenuSoundExt(int sound)
9481 if (sound == SND_UNDEFINED)
9484 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9485 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9488 if (IS_LOOP_SOUND(sound))
9489 PlaySoundLoop(sound);
9494 void PlayMenuSound(void)
9496 PlayMenuSoundExt(menu.sound[game_status]);
9499 void PlayMenuSoundStereo(int sound, int stereo_position)
9501 if (sound == SND_UNDEFINED)
9504 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9505 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9508 if (IS_LOOP_SOUND(sound))
9509 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9511 PlaySoundStereo(sound, stereo_position);
9514 void PlayMenuSoundIfLoopExt(int sound)
9516 if (sound == SND_UNDEFINED)
9519 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9520 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9523 if (IS_LOOP_SOUND(sound))
9524 PlaySoundLoop(sound);
9527 void PlayMenuSoundIfLoop(void)
9529 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9532 void PlayMenuMusicExt(int music)
9534 if (music == MUS_UNDEFINED)
9537 if (!setup.sound_music)
9540 if (IS_LOOP_MUSIC(music))
9541 PlayMusicLoop(music);
9546 void PlayMenuMusic(void)
9548 char *curr_music = getCurrentlyPlayingMusicFilename();
9549 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9551 if (!strEqual(curr_music, next_music))
9552 PlayMenuMusicExt(menu.music[game_status]);
9555 void PlayMenuSoundsAndMusic(void)
9561 static void FadeMenuSounds(void)
9566 static void FadeMenuMusic(void)
9568 char *curr_music = getCurrentlyPlayingMusicFilename();
9569 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9571 if (!strEqual(curr_music, next_music))
9575 void FadeMenuSoundsAndMusic(void)
9581 void PlaySoundActivating(void)
9584 PlaySound(SND_MENU_ITEM_ACTIVATING);
9588 void PlaySoundSelecting(void)
9591 PlaySound(SND_MENU_ITEM_SELECTING);
9595 void ToggleFullscreenIfNeeded(void)
9597 // if setup and video fullscreen state are already matching, nothing do do
9598 if (setup.fullscreen == video.fullscreen_enabled ||
9599 !video.fullscreen_available)
9602 SDLSetWindowFullscreen(setup.fullscreen);
9604 // set setup value according to successfully changed fullscreen mode
9605 setup.fullscreen = video.fullscreen_enabled;
9608 void ChangeWindowScalingIfNeeded(void)
9610 // if setup and video window scaling are already matching, nothing do do
9611 if (setup.window_scaling_percent == video.window_scaling_percent ||
9612 video.fullscreen_enabled)
9615 SDLSetWindowScaling(setup.window_scaling_percent);
9617 // set setup value according to successfully changed window scaling
9618 setup.window_scaling_percent = video.window_scaling_percent;
9621 void ChangeVsyncModeIfNeeded(void)
9623 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9624 int video_vsync_mode = video.vsync_mode;
9626 // if setup and video vsync mode are already matching, nothing do do
9627 if (setup_vsync_mode == video_vsync_mode)
9630 // if renderer is using OpenGL, vsync mode can directly be changed
9631 SDLSetScreenVsyncMode(setup.vsync_mode);
9633 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9634 if (video.vsync_mode == video_vsync_mode)
9636 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9638 // save backbuffer content which gets lost when re-creating screen
9639 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9641 // force re-creating screen and renderer to set new vsync mode
9642 video.fullscreen_enabled = !setup.fullscreen;
9644 // when creating new renderer, destroy textures linked to old renderer
9645 FreeAllImageTextures(); // needs old renderer to free the textures
9647 // re-create screen and renderer (including change of vsync mode)
9648 ChangeVideoModeIfNeeded(setup.fullscreen);
9650 // set setup value according to successfully changed fullscreen mode
9651 setup.fullscreen = video.fullscreen_enabled;
9653 // restore backbuffer content from temporary backbuffer backup bitmap
9654 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9655 FreeBitmap(tmp_backbuffer);
9657 // update visible window/screen
9658 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9660 // when changing vsync mode, re-create textures for new renderer
9661 InitImageTextures();
9664 // set setup value according to successfully changed vsync mode
9665 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9668 static void JoinRectangles(int *x, int *y, int *width, int *height,
9669 int x2, int y2, int width2, int height2)
9671 // do not join with "off-screen" rectangle
9672 if (x2 == -1 || y2 == -1)
9677 *width = MAX(*width, width2);
9678 *height = MAX(*height, height2);
9681 void SetAnimStatus(int anim_status_new)
9683 if (anim_status_new == GAME_MODE_MAIN)
9684 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9685 else if (anim_status_new == GAME_MODE_NAMES)
9686 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9687 else if (anim_status_new == GAME_MODE_SCORES)
9688 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9690 global.anim_status_next = anim_status_new;
9692 // directly set screen modes that are entered without fading
9693 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9694 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9695 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9696 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9697 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9698 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9699 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9700 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9701 global.anim_status = global.anim_status_next;
9704 void SetGameStatus(int game_status_new)
9706 if (game_status_new != game_status)
9707 game_status_last_screen = game_status;
9709 game_status = game_status_new;
9711 SetAnimStatus(game_status_new);
9714 void SetFontStatus(int game_status_new)
9716 static int last_game_status = -1;
9718 if (game_status_new != -1)
9720 // set game status for font use after storing last game status
9721 last_game_status = game_status;
9722 game_status = game_status_new;
9726 // reset game status after font use from last stored game status
9727 game_status = last_game_status;
9731 void ResetFontStatus(void)
9736 void SetLevelSetInfo(char *identifier, int level_nr)
9738 setString(&levelset.identifier, identifier);
9740 levelset.level_nr = level_nr;
9743 boolean CheckIfAllViewportsHaveChanged(void)
9745 // if game status has not changed, viewports have not changed either
9746 if (game_status == game_status_last)
9749 // check if all viewports have changed with current game status
9751 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9752 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9753 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9754 int new_real_sx = vp_playfield->x;
9755 int new_real_sy = vp_playfield->y;
9756 int new_full_sxsize = vp_playfield->width;
9757 int new_full_sysize = vp_playfield->height;
9758 int new_dx = vp_door_1->x;
9759 int new_dy = vp_door_1->y;
9760 int new_dxsize = vp_door_1->width;
9761 int new_dysize = vp_door_1->height;
9762 int new_vx = vp_door_2->x;
9763 int new_vy = vp_door_2->y;
9764 int new_vxsize = vp_door_2->width;
9765 int new_vysize = vp_door_2->height;
9767 boolean playfield_viewport_has_changed =
9768 (new_real_sx != REAL_SX ||
9769 new_real_sy != REAL_SY ||
9770 new_full_sxsize != FULL_SXSIZE ||
9771 new_full_sysize != FULL_SYSIZE);
9773 boolean door_1_viewport_has_changed =
9776 new_dxsize != DXSIZE ||
9777 new_dysize != DYSIZE);
9779 boolean door_2_viewport_has_changed =
9782 new_vxsize != VXSIZE ||
9783 new_vysize != VYSIZE ||
9784 game_status_last == GAME_MODE_EDITOR);
9786 return (playfield_viewport_has_changed &&
9787 door_1_viewport_has_changed &&
9788 door_2_viewport_has_changed);
9791 boolean CheckFadeAll(void)
9793 return (CheckIfGlobalBorderHasChanged() ||
9794 CheckIfAllViewportsHaveChanged());
9797 void ChangeViewportPropertiesIfNeeded(void)
9799 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9800 FALSE : setup.small_game_graphics);
9801 int gfx_game_mode = getGlobalGameStatus(game_status);
9802 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9804 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9805 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9806 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9807 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9808 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9809 int new_win_xsize = vp_window->width;
9810 int new_win_ysize = vp_window->height;
9811 int border_left = vp_playfield->border_left;
9812 int border_right = vp_playfield->border_right;
9813 int border_top = vp_playfield->border_top;
9814 int border_bottom = vp_playfield->border_bottom;
9815 int new_sx = vp_playfield->x + border_left;
9816 int new_sy = vp_playfield->y + border_top;
9817 int new_sxsize = vp_playfield->width - border_left - border_right;
9818 int new_sysize = vp_playfield->height - border_top - border_bottom;
9819 int new_real_sx = vp_playfield->x;
9820 int new_real_sy = vp_playfield->y;
9821 int new_full_sxsize = vp_playfield->width;
9822 int new_full_sysize = vp_playfield->height;
9823 int new_dx = vp_door_1->x;
9824 int new_dy = vp_door_1->y;
9825 int new_dxsize = vp_door_1->width;
9826 int new_dysize = vp_door_1->height;
9827 int new_vx = vp_door_2->x;
9828 int new_vy = vp_door_2->y;
9829 int new_vxsize = vp_door_2->width;
9830 int new_vysize = vp_door_2->height;
9831 int new_ex = vp_door_3->x;
9832 int new_ey = vp_door_3->y;
9833 int new_exsize = vp_door_3->width;
9834 int new_eysize = vp_door_3->height;
9835 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9836 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9837 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9838 int new_scr_fieldx = new_sxsize / tilesize;
9839 int new_scr_fieldy = new_sysize / tilesize;
9840 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9841 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9842 boolean init_gfx_buffers = FALSE;
9843 boolean init_video_buffer = FALSE;
9844 boolean init_gadgets_and_anims = FALSE;
9845 boolean init_em_graphics = FALSE;
9847 if (new_win_xsize != WIN_XSIZE ||
9848 new_win_ysize != WIN_YSIZE)
9850 WIN_XSIZE = new_win_xsize;
9851 WIN_YSIZE = new_win_ysize;
9853 init_video_buffer = TRUE;
9854 init_gfx_buffers = TRUE;
9855 init_gadgets_and_anims = TRUE;
9857 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9860 if (new_scr_fieldx != SCR_FIELDX ||
9861 new_scr_fieldy != SCR_FIELDY)
9863 // this always toggles between MAIN and GAME when using small tile size
9865 SCR_FIELDX = new_scr_fieldx;
9866 SCR_FIELDY = new_scr_fieldy;
9868 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9879 new_sxsize != SXSIZE ||
9880 new_sysize != SYSIZE ||
9881 new_dxsize != DXSIZE ||
9882 new_dysize != DYSIZE ||
9883 new_vxsize != VXSIZE ||
9884 new_vysize != VYSIZE ||
9885 new_exsize != EXSIZE ||
9886 new_eysize != EYSIZE ||
9887 new_real_sx != REAL_SX ||
9888 new_real_sy != REAL_SY ||
9889 new_full_sxsize != FULL_SXSIZE ||
9890 new_full_sysize != FULL_SYSIZE ||
9891 new_tilesize_var != TILESIZE_VAR
9894 // ------------------------------------------------------------------------
9895 // determine next fading area for changed viewport definitions
9896 // ------------------------------------------------------------------------
9898 // start with current playfield area (default fading area)
9901 FADE_SXSIZE = FULL_SXSIZE;
9902 FADE_SYSIZE = FULL_SYSIZE;
9904 // add new playfield area if position or size has changed
9905 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9906 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9908 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9909 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9912 // add current and new door 1 area if position or size has changed
9913 if (new_dx != DX || new_dy != DY ||
9914 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9916 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9917 DX, DY, DXSIZE, DYSIZE);
9918 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9919 new_dx, new_dy, new_dxsize, new_dysize);
9922 // add current and new door 2 area if position or size has changed
9923 if (new_vx != VX || new_vy != VY ||
9924 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9926 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9927 VX, VY, VXSIZE, VYSIZE);
9928 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9929 new_vx, new_vy, new_vxsize, new_vysize);
9932 // ------------------------------------------------------------------------
9933 // handle changed tile size
9934 // ------------------------------------------------------------------------
9936 if (new_tilesize_var != TILESIZE_VAR)
9938 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9940 // changing tile size invalidates scroll values of engine snapshots
9941 FreeEngineSnapshotSingle();
9943 // changing tile size requires update of graphic mapping for EM engine
9944 init_em_graphics = TRUE;
9955 SXSIZE = new_sxsize;
9956 SYSIZE = new_sysize;
9957 DXSIZE = new_dxsize;
9958 DYSIZE = new_dysize;
9959 VXSIZE = new_vxsize;
9960 VYSIZE = new_vysize;
9961 EXSIZE = new_exsize;
9962 EYSIZE = new_eysize;
9963 REAL_SX = new_real_sx;
9964 REAL_SY = new_real_sy;
9965 FULL_SXSIZE = new_full_sxsize;
9966 FULL_SYSIZE = new_full_sysize;
9967 TILESIZE_VAR = new_tilesize_var;
9969 init_gfx_buffers = TRUE;
9970 init_gadgets_and_anims = TRUE;
9972 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9973 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9976 if (init_gfx_buffers)
9978 // Debug("tools:viewport", "init_gfx_buffers");
9980 SCR_FIELDX = new_scr_fieldx_buffers;
9981 SCR_FIELDY = new_scr_fieldy_buffers;
9985 SCR_FIELDX = new_scr_fieldx;
9986 SCR_FIELDY = new_scr_fieldy;
9988 SetDrawDeactivationMask(REDRAW_NONE);
9989 SetDrawBackgroundMask(REDRAW_FIELD);
9992 if (init_video_buffer)
9994 // Debug("tools:viewport", "init_video_buffer");
9996 FreeAllImageTextures(); // needs old renderer to free the textures
9998 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9999 InitImageTextures();
10002 if (init_gadgets_and_anims)
10004 // Debug("tools:viewport", "init_gadgets_and_anims");
10007 InitGlobalAnimations();
10010 if (init_em_graphics)
10012 InitGraphicInfo_EM();
10016 void OpenURL(char *url)
10018 #if SDL_VERSION_ATLEAST(2,0,14)
10021 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
10022 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
10023 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
10027 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
10029 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
10033 // ============================================================================
10035 // ============================================================================
10037 #if defined(PLATFORM_WINDOWS)
10038 /* FILETIME of Jan 1 1970 00:00:00. */
10039 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
10042 * timezone information is stored outside the kernel so tzp isn't used anymore.
10044 * Note: this function is not for Win32 high precision timing purpose. See
10047 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
10049 FILETIME file_time;
10050 SYSTEMTIME system_time;
10051 ULARGE_INTEGER ularge;
10053 GetSystemTime(&system_time);
10054 SystemTimeToFileTime(&system_time, &file_time);
10055 ularge.LowPart = file_time.dwLowDateTime;
10056 ularge.HighPart = file_time.dwHighDateTime;
10058 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10059 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10065 static char *test_init_uuid_random_function_simple(void)
10067 static char seed_text[100];
10068 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10070 sprintf(seed_text, "%d", seed);
10075 static char *test_init_uuid_random_function_better(void)
10077 static char seed_text[100];
10078 struct timeval current_time;
10080 gettimeofday(¤t_time, NULL);
10082 prng_seed_bytes(¤t_time, sizeof(current_time));
10084 sprintf(seed_text, "%ld.%ld",
10085 (long)current_time.tv_sec,
10086 (long)current_time.tv_usec);
10091 #if defined(PLATFORM_WINDOWS)
10092 static char *test_init_uuid_random_function_better_windows(void)
10094 static char seed_text[100];
10095 struct timeval current_time;
10097 gettimeofday_windows(¤t_time, NULL);
10099 prng_seed_bytes(¤t_time, sizeof(current_time));
10101 sprintf(seed_text, "%ld.%ld",
10102 (long)current_time.tv_sec,
10103 (long)current_time.tv_usec);
10109 static unsigned int test_uuid_random_function_simple(int max)
10111 return GetSimpleRandom(max);
10114 static unsigned int test_uuid_random_function_better(int max)
10116 return (max > 0 ? prng_get_uint() % max : 0);
10119 #if defined(PLATFORM_WINDOWS)
10120 #define NUM_UUID_TESTS 3
10122 #define NUM_UUID_TESTS 2
10125 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10127 struct hashtable *hash_seeds =
10128 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10129 struct hashtable *hash_uuids =
10130 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10131 static char message[100];
10134 char *random_name = (nr == 0 ? "simple" : "better");
10135 char *random_type = (always_seed ? "always" : "only once");
10136 char *(*init_random_function)(void) =
10138 test_init_uuid_random_function_simple :
10139 test_init_uuid_random_function_better);
10140 unsigned int (*random_function)(int) =
10142 test_uuid_random_function_simple :
10143 test_uuid_random_function_better);
10146 #if defined(PLATFORM_WINDOWS)
10149 random_name = "windows";
10150 init_random_function = test_init_uuid_random_function_better_windows;
10156 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10157 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10159 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10160 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10161 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10163 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10167 // always initialize random number generator at least once
10168 init_random_function();
10170 unsigned int time_start = SDL_GetTicks();
10172 for (i = 0; i < num_uuids; i++)
10176 char *seed = getStringCopy(init_random_function());
10178 hashtable_remove(hash_seeds, seed);
10179 hashtable_insert(hash_seeds, seed, "1");
10182 char *uuid = getStringCopy(getUUIDExt(random_function));
10184 hashtable_remove(hash_uuids, uuid);
10185 hashtable_insert(hash_uuids, uuid, "1");
10188 int num_unique_seeds = hashtable_count(hash_seeds);
10189 int num_unique_uuids = hashtable_count(hash_uuids);
10191 unsigned int time_needed = SDL_GetTicks() - time_start;
10193 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10195 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10198 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10200 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10201 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10203 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10205 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10207 Request(message, REQ_CONFIRM);
10209 hashtable_destroy(hash_seeds, 0);
10210 hashtable_destroy(hash_uuids, 0);
10213 void TestGeneratingUUIDs(void)
10215 int num_uuids = 1000000;
10218 for (i = 0; i < NUM_UUID_TESTS; i++)
10219 for (j = 0; j < 2; j++)
10220 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10222 CloseAllAndExit(0);