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)
3017 if (request.bitmap == NULL ||
3018 xsize > request.xsize ||
3019 ysize > request.ysize)
3021 if (request.bitmap != NULL)
3022 FreeBitmap(request.bitmap);
3024 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
3026 SDL_Surface *surface = request.bitmap->surface;
3028 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
3029 Fail("SDLGetNativeSurface() failed");
3032 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3034 SDLFreeBitmapTextures(request.bitmap);
3035 SDLCreateBitmapTextures(request.bitmap);
3037 // set envelope request run-time values
3040 request.xsize = xsize;
3041 request.ysize = ysize;
3044 void DrawEnvelopeRequestToScreen(int drawing_target)
3046 if (global.use_envelope_request &&
3047 game.request_active_or_moving &&
3048 drawing_target == DRAW_TO_SCREEN)
3050 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3051 request.sx, request.sy);
3055 static void setRequestBasePosition(int *x, int *y)
3057 int sx_base, sy_base;
3059 if (request.x != -1)
3060 sx_base = request.x;
3061 else if (request.align == ALIGN_LEFT)
3063 else if (request.align == ALIGN_RIGHT)
3064 sx_base = SX + SXSIZE;
3066 sx_base = SX + SXSIZE / 2;
3068 if (request.y != -1)
3069 sy_base = request.y;
3070 else if (request.valign == VALIGN_TOP)
3072 else if (request.valign == VALIGN_BOTTOM)
3073 sy_base = SY + SYSIZE;
3075 sy_base = SY + SYSIZE / 2;
3081 static void setRequestPositionExt(int *x, int *y, int width, int height,
3082 boolean add_border_size)
3084 int border_size = request.border_size;
3085 int sx_base, sy_base;
3088 setRequestBasePosition(&sx_base, &sy_base);
3090 if (request.align == ALIGN_LEFT)
3092 else if (request.align == ALIGN_RIGHT)
3093 sx = sx_base - width;
3095 sx = sx_base - width / 2;
3097 if (request.valign == VALIGN_TOP)
3099 else if (request.valign == VALIGN_BOTTOM)
3100 sy = sy_base - height;
3102 sy = sy_base - height / 2;
3104 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3105 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3107 if (add_border_size)
3117 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3119 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3122 static void DrawEnvelopeRequest(char *text)
3124 char *text_final = text;
3125 char *text_door_style = NULL;
3126 int graphic = IMG_BACKGROUND_REQUEST;
3127 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3128 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3129 int font_nr = FONT_REQUEST;
3130 int font_width = getFontWidth(font_nr);
3131 int font_height = getFontHeight(font_nr);
3132 int border_size = request.border_size;
3133 int line_spacing = request.line_spacing;
3134 int line_height = font_height + line_spacing;
3135 int max_text_width = request.width - 2 * border_size;
3136 int max_text_height = request.height - 2 * border_size;
3137 int line_length = max_text_width / font_width;
3138 int max_lines = max_text_height / line_height;
3139 int text_width = line_length * font_width;
3140 int width = request.width;
3141 int height = request.height;
3142 int tile_size = MAX(request.step_offset, 1);
3143 int x_steps = width / tile_size;
3144 int y_steps = height / tile_size;
3145 int sx_offset = border_size;
3146 int sy_offset = border_size;
3150 if (request.centered)
3151 sx_offset = (request.width - text_width) / 2;
3153 if (request.wrap_single_words && !request.autowrap)
3155 char *src_text_ptr, *dst_text_ptr;
3157 text_door_style = checked_malloc(2 * strlen(text) + 1);
3159 src_text_ptr = text;
3160 dst_text_ptr = text_door_style;
3162 while (*src_text_ptr)
3164 if (*src_text_ptr == ' ' ||
3165 *src_text_ptr == '?' ||
3166 *src_text_ptr == '!')
3167 *dst_text_ptr++ = '\n';
3169 if (*src_text_ptr != ' ')
3170 *dst_text_ptr++ = *src_text_ptr;
3175 *dst_text_ptr = '\0';
3177 text_final = text_door_style;
3180 setRequestPosition(&sx, &sy, FALSE);
3182 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3184 for (y = 0; y < y_steps; y++)
3185 for (x = 0; x < x_steps; x++)
3186 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3187 x, y, x_steps, y_steps,
3188 tile_size, tile_size);
3190 // force DOOR font inside door area
3191 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3193 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3194 line_length, -1, max_lines, line_spacing, mask_mode,
3195 request.autowrap, request.centered, FALSE);
3199 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3200 RedrawGadget(tool_gadget[i]);
3202 // store readily prepared envelope request for later use when animating
3203 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3205 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3207 if (text_door_style)
3208 free(text_door_style);
3211 static void AnimateEnvelopeRequest(int anim_mode, int action)
3213 int graphic = IMG_BACKGROUND_REQUEST;
3214 boolean draw_masked = graphic_info[graphic].draw_masked;
3215 int delay_value_normal = request.step_delay;
3216 int delay_value_fast = delay_value_normal / 2;
3217 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3218 boolean no_delay = (tape.warp_forward);
3219 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3220 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3221 DelayCounter anim_delay = { anim_delay_value };
3223 int tile_size = MAX(request.step_offset, 1);
3224 int max_xsize = request.width / tile_size;
3225 int max_ysize = request.height / tile_size;
3226 int max_xsize_inner = max_xsize - 2;
3227 int max_ysize_inner = max_ysize - 2;
3229 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3230 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3231 int xend = max_xsize_inner;
3232 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3233 int xstep = (xstart < xend ? 1 : 0);
3234 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3236 int end = MAX(xend - xstart, yend - ystart);
3239 if (setup.quick_doors)
3246 for (i = start; i <= end; i++)
3248 int last_frame = end; // last frame of this "for" loop
3249 int x = xstart + i * xstep;
3250 int y = ystart + i * ystep;
3251 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3252 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3253 int xsize_size_left = (xsize - 1) * tile_size;
3254 int ysize_size_top = (ysize - 1) * tile_size;
3255 int max_xsize_pos = (max_xsize - 1) * tile_size;
3256 int max_ysize_pos = (max_ysize - 1) * tile_size;
3257 int width = xsize * tile_size;
3258 int height = ysize * tile_size;
3263 setRequestPosition(&src_x, &src_y, FALSE);
3264 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3266 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3268 for (yy = 0; yy < 2; yy++)
3270 for (xx = 0; xx < 2; xx++)
3272 int src_xx = src_x + xx * max_xsize_pos;
3273 int src_yy = src_y + yy * max_ysize_pos;
3274 int dst_xx = dst_x + xx * xsize_size_left;
3275 int dst_yy = dst_y + yy * ysize_size_top;
3276 int xx_size = (xx ? tile_size : xsize_size_left);
3277 int yy_size = (yy ? tile_size : ysize_size_top);
3280 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3281 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3283 BlitBitmap(bitmap_db_store_2, backbuffer,
3284 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3288 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3290 redraw_mask |= REDRAW_FIELD;
3294 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3297 ClearAutoRepeatKeyEvents();
3300 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3302 int graphic = IMG_BACKGROUND_REQUEST;
3303 int sound_opening = SND_REQUEST_OPENING;
3304 int sound_closing = SND_REQUEST_CLOSING;
3305 int anim_mode_1 = request.anim_mode; // (higher priority)
3306 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3307 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3308 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3309 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3311 if (game_status == GAME_MODE_PLAYING)
3312 BlitScreenToBitmap(backbuffer);
3314 SetDrawtoField(DRAW_TO_BACKBUFFER);
3316 // SetDrawBackgroundMask(REDRAW_NONE);
3318 if (action == ACTION_OPENING)
3320 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3322 if (req_state & REQ_ASK)
3324 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3325 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3326 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3327 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3329 else if (req_state & REQ_CONFIRM)
3331 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3332 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3334 else if (req_state & REQ_PLAYER)
3336 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3337 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3338 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3339 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3342 DrawEnvelopeRequest(text);
3345 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3347 if (action == ACTION_OPENING)
3349 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3351 if (anim_mode == ANIM_DEFAULT)
3352 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3354 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3358 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3360 if (anim_mode != ANIM_NONE)
3361 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3363 if (anim_mode == ANIM_DEFAULT)
3364 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3367 game.envelope_active = FALSE;
3369 if (action == ACTION_CLOSING)
3370 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3372 // SetDrawBackgroundMask(last_draw_background_mask);
3374 redraw_mask |= REDRAW_FIELD;
3378 if (action == ACTION_CLOSING &&
3379 game_status == GAME_MODE_PLAYING &&
3380 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3381 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3384 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3386 if (IS_MM_WALL(element))
3388 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3394 int graphic = el2preimg(element);
3396 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3397 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3402 void DrawLevel(int draw_background_mask)
3406 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3407 SetDrawBackgroundMask(draw_background_mask);
3411 for (x = BX1; x <= BX2; x++)
3412 for (y = BY1; y <= BY2; y++)
3413 DrawScreenField(x, y);
3415 redraw_mask |= REDRAW_FIELD;
3418 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3423 for (x = 0; x < size_x; x++)
3424 for (y = 0; y < size_y; y++)
3425 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3427 redraw_mask |= REDRAW_FIELD;
3430 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3434 for (x = 0; x < size_x; x++)
3435 for (y = 0; y < size_y; y++)
3436 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3438 redraw_mask |= REDRAW_FIELD;
3441 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3443 boolean show_level_border = (BorderElement != EL_EMPTY);
3444 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3445 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3446 int tile_size = preview.tile_size;
3447 int preview_width = preview.xsize * tile_size;
3448 int preview_height = preview.ysize * tile_size;
3449 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3450 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3451 int real_preview_width = real_preview_xsize * tile_size;
3452 int real_preview_height = real_preview_ysize * tile_size;
3453 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3454 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3457 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3460 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3462 dst_x += (preview_width - real_preview_width) / 2;
3463 dst_y += (preview_height - real_preview_height) / 2;
3465 for (x = 0; x < real_preview_xsize; x++)
3467 for (y = 0; y < real_preview_ysize; y++)
3469 int lx = from_x + x + (show_level_border ? -1 : 0);
3470 int ly = from_y + y + (show_level_border ? -1 : 0);
3471 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3472 getBorderElement(lx, ly));
3474 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3475 element, tile_size);
3479 redraw_mask |= REDRAW_FIELD;
3482 #define MICROLABEL_EMPTY 0
3483 #define MICROLABEL_LEVEL_NAME 1
3484 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3485 #define MICROLABEL_LEVEL_AUTHOR 3
3486 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3487 #define MICROLABEL_IMPORTED_FROM 5
3488 #define MICROLABEL_IMPORTED_BY_HEAD 6
3489 #define MICROLABEL_IMPORTED_BY 7
3491 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3493 int max_text_width = SXSIZE;
3494 int font_width = getFontWidth(font_nr);
3496 if (pos->align == ALIGN_CENTER)
3497 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3498 else if (pos->align == ALIGN_RIGHT)
3499 max_text_width = pos->x;
3501 max_text_width = SXSIZE - pos->x;
3503 return max_text_width / font_width;
3506 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3508 char label_text[MAX_OUTPUT_LINESIZE + 1];
3509 int max_len_label_text;
3510 int font_nr = pos->font;
3513 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3516 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3517 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3518 mode == MICROLABEL_IMPORTED_BY_HEAD)
3519 font_nr = pos->font_alt;
3521 max_len_label_text = getMaxTextLength(pos, font_nr);
3523 if (pos->size != -1)
3524 max_len_label_text = pos->size;
3526 for (i = 0; i < max_len_label_text; i++)
3527 label_text[i] = ' ';
3528 label_text[max_len_label_text] = '\0';
3530 if (strlen(label_text) > 0)
3531 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3534 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3535 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3536 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3537 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3538 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3539 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3540 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3541 max_len_label_text);
3542 label_text[max_len_label_text] = '\0';
3544 if (strlen(label_text) > 0)
3545 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3547 redraw_mask |= REDRAW_FIELD;
3550 static void DrawPreviewLevelLabel(int mode)
3552 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3555 static void DrawPreviewLevelInfo(int mode)
3557 if (mode == MICROLABEL_LEVEL_NAME)
3558 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3559 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3560 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3563 static void DrawPreviewLevelExt(boolean restart)
3565 static DelayCounter scroll_delay = { 0 };
3566 static DelayCounter label_delay = { 0 };
3567 static int from_x, from_y, scroll_direction;
3568 static int label_state, label_counter;
3569 boolean show_level_border = (BorderElement != EL_EMPTY);
3570 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3571 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3573 scroll_delay.value = preview.step_delay;
3574 label_delay.value = MICROLEVEL_LABEL_DELAY;
3581 if (preview.anim_mode == ANIM_CENTERED)
3583 if (level_xsize > preview.xsize)
3584 from_x = (level_xsize - preview.xsize) / 2;
3585 if (level_ysize > preview.ysize)
3586 from_y = (level_ysize - preview.ysize) / 2;
3589 from_x += preview.xoffset;
3590 from_y += preview.yoffset;
3592 scroll_direction = MV_RIGHT;
3596 DrawPreviewLevelPlayfield(from_x, from_y);
3597 DrawPreviewLevelLabel(label_state);
3599 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3600 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3602 // initialize delay counters
3603 ResetDelayCounter(&scroll_delay);
3604 ResetDelayCounter(&label_delay);
3606 if (leveldir_current->name)
3608 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3609 char label_text[MAX_OUTPUT_LINESIZE + 1];
3610 int font_nr = pos->font;
3611 int max_len_label_text = getMaxTextLength(pos, font_nr);
3613 if (pos->size != -1)
3614 max_len_label_text = pos->size;
3616 strncpy(label_text, leveldir_current->name, max_len_label_text);
3617 label_text[max_len_label_text] = '\0';
3619 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3620 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3626 // scroll preview level, if needed
3627 if (preview.anim_mode != ANIM_NONE &&
3628 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3629 DelayReached(&scroll_delay))
3631 switch (scroll_direction)
3636 from_x -= preview.step_offset;
3637 from_x = (from_x < 0 ? 0 : from_x);
3640 scroll_direction = MV_UP;
3644 if (from_x < level_xsize - preview.xsize)
3646 from_x += preview.step_offset;
3647 from_x = (from_x > level_xsize - preview.xsize ?
3648 level_xsize - preview.xsize : from_x);
3651 scroll_direction = MV_DOWN;
3657 from_y -= preview.step_offset;
3658 from_y = (from_y < 0 ? 0 : from_y);
3661 scroll_direction = MV_RIGHT;
3665 if (from_y < level_ysize - preview.ysize)
3667 from_y += preview.step_offset;
3668 from_y = (from_y > level_ysize - preview.ysize ?
3669 level_ysize - preview.ysize : from_y);
3672 scroll_direction = MV_LEFT;
3679 DrawPreviewLevelPlayfield(from_x, from_y);
3682 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3683 // redraw micro level label, if needed
3684 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3685 !strEqual(level.author, ANONYMOUS_NAME) &&
3686 !strEqual(level.author, leveldir_current->name) &&
3687 DelayReached(&label_delay))
3689 int max_label_counter = 23;
3691 if (leveldir_current->imported_from != NULL &&
3692 strlen(leveldir_current->imported_from) > 0)
3693 max_label_counter += 14;
3694 if (leveldir_current->imported_by != NULL &&
3695 strlen(leveldir_current->imported_by) > 0)
3696 max_label_counter += 14;
3698 label_counter = (label_counter + 1) % max_label_counter;
3699 label_state = (label_counter >= 0 && label_counter <= 7 ?
3700 MICROLABEL_LEVEL_NAME :
3701 label_counter >= 9 && label_counter <= 12 ?
3702 MICROLABEL_LEVEL_AUTHOR_HEAD :
3703 label_counter >= 14 && label_counter <= 21 ?
3704 MICROLABEL_LEVEL_AUTHOR :
3705 label_counter >= 23 && label_counter <= 26 ?
3706 MICROLABEL_IMPORTED_FROM_HEAD :
3707 label_counter >= 28 && label_counter <= 35 ?
3708 MICROLABEL_IMPORTED_FROM :
3709 label_counter >= 37 && label_counter <= 40 ?
3710 MICROLABEL_IMPORTED_BY_HEAD :
3711 label_counter >= 42 && label_counter <= 49 ?
3712 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3714 if (leveldir_current->imported_from == NULL &&
3715 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3716 label_state == MICROLABEL_IMPORTED_FROM))
3717 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3718 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3720 DrawPreviewLevelLabel(label_state);
3724 void DrawPreviewPlayers(void)
3726 if (game_status != GAME_MODE_MAIN)
3729 // do not draw preview players if level preview redefined, but players aren't
3730 if (preview.redefined && !menu.main.preview_players.redefined)
3733 boolean player_found[MAX_PLAYERS];
3734 int num_players = 0;
3737 for (i = 0; i < MAX_PLAYERS; i++)
3738 player_found[i] = FALSE;
3740 // check which players can be found in the level (simple approach)
3741 for (x = 0; x < lev_fieldx; x++)
3743 for (y = 0; y < lev_fieldy; y++)
3745 int element = level.field[x][y];
3747 if (IS_PLAYER_ELEMENT(element))
3749 int player_nr = GET_PLAYER_NR(element);
3751 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3753 if (!player_found[player_nr])
3756 player_found[player_nr] = TRUE;
3761 struct TextPosInfo *pos = &menu.main.preview_players;
3762 int tile_size = pos->tile_size;
3763 int border_size = pos->border_size;
3764 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3765 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3766 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3767 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3768 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3769 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3770 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3771 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3772 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3773 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3774 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3775 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3777 // clear area in which the players will be drawn
3778 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3779 max_players_width, max_players_height);
3781 if (!network.enabled && !setup.team_mode)
3784 // only draw players if level is suited for team mode
3785 if (num_players < 2)
3788 // draw all players that were found in the level
3789 for (i = 0; i < MAX_PLAYERS; i++)
3791 if (player_found[i])
3793 int graphic = el2img(EL_PLAYER_1 + i);
3795 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3797 xpos += player_xoffset;
3798 ypos += player_yoffset;
3803 void DrawPreviewLevelInitial(void)
3805 DrawPreviewLevelExt(TRUE);
3806 DrawPreviewPlayers();
3809 void DrawPreviewLevelAnimation(void)
3811 DrawPreviewLevelExt(FALSE);
3814 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3815 int border_size, int font_nr)
3817 int graphic = el2img(EL_PLAYER_1 + player_nr);
3818 int font_height = getFontHeight(font_nr);
3819 int player_height = MAX(tile_size, font_height);
3820 int xoffset_text = tile_size + border_size;
3821 int yoffset_text = (player_height - font_height) / 2;
3822 int yoffset_graphic = (player_height - tile_size) / 2;
3823 char *player_name = getNetworkPlayerName(player_nr + 1);
3825 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3827 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3830 static void DrawNetworkPlayersExt(boolean force)
3832 if (game_status != GAME_MODE_MAIN)
3835 if (!network.connected && !force)
3838 // do not draw network players if level preview redefined, but players aren't
3839 if (preview.redefined && !menu.main.network_players.redefined)
3842 int num_players = 0;
3845 for (i = 0; i < MAX_PLAYERS; i++)
3846 if (stored_player[i].connected_network)
3849 struct TextPosInfo *pos = &menu.main.network_players;
3850 int tile_size = pos->tile_size;
3851 int border_size = pos->border_size;
3852 int xoffset_text = tile_size + border_size;
3853 int font_nr = pos->font;
3854 int font_width = getFontWidth(font_nr);
3855 int font_height = getFontHeight(font_nr);
3856 int player_height = MAX(tile_size, font_height);
3857 int player_yoffset = player_height + border_size;
3858 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3859 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3860 int all_players_height = num_players * player_yoffset - border_size;
3861 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3862 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3863 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3865 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3866 max_players_width, max_players_height);
3868 // first draw local network player ...
3869 for (i = 0; i < MAX_PLAYERS; i++)
3871 if (stored_player[i].connected_network &&
3872 stored_player[i].connected_locally)
3874 char *player_name = getNetworkPlayerName(i + 1);
3875 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3876 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3878 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3880 ypos += player_yoffset;
3884 // ... then draw all other network players
3885 for (i = 0; i < MAX_PLAYERS; i++)
3887 if (stored_player[i].connected_network &&
3888 !stored_player[i].connected_locally)
3890 char *player_name = getNetworkPlayerName(i + 1);
3891 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3892 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3894 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3896 ypos += player_yoffset;
3901 void DrawNetworkPlayers(void)
3903 DrawNetworkPlayersExt(FALSE);
3906 void ClearNetworkPlayers(void)
3908 DrawNetworkPlayersExt(TRUE);
3911 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3912 int graphic, int lx, int ly,
3915 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3917 if (mask_mode == USE_MASKING)
3918 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3920 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3923 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3924 int graphic, int sync_frame, int mask_mode)
3926 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3928 if (mask_mode == USE_MASKING)
3929 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3931 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3934 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3935 int graphic, int sync_frame, int tilesize,
3938 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3940 if (mask_mode == USE_MASKING)
3941 DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
3943 DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
3946 static void DrawGraphicAnimation(int x, int y, int graphic)
3948 int lx = LEVELX(x), ly = LEVELY(y);
3949 int mask_mode = NO_MASKING;
3951 if (!IN_SCR_FIELD(x, y))
3954 if (game.use_masked_elements)
3956 if (Tile[lx][ly] != EL_EMPTY)
3958 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3960 mask_mode = USE_MASKING;
3964 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3965 graphic, lx, ly, mask_mode);
3967 MarkTileDirty(x, y);
3970 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3972 int lx = LEVELX(x), ly = LEVELY(y);
3973 int mask_mode = NO_MASKING;
3975 if (!IN_SCR_FIELD(x, y))
3978 if (game.use_masked_elements)
3980 if (Tile[lx][ly] != EL_EMPTY)
3982 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3984 mask_mode = USE_MASKING;
3988 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3989 graphic, lx, ly, mask_mode);
3991 MarkTileDirty(x, y);
3994 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3996 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3999 void DrawLevelElementAnimation(int x, int y, int element)
4001 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4003 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4006 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
4008 int sx = SCREENX(x), sy = SCREENY(y);
4010 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4013 if (Tile[x][y] == EL_EMPTY)
4014 graphic = el2img(GfxElementEmpty[x][y]);
4016 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4019 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4022 DrawGraphicAnimation(sx, sy, graphic);
4025 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4026 DrawLevelFieldCrumbled(x, y);
4028 if (GFX_CRUMBLED(Tile[x][y]))
4029 DrawLevelFieldCrumbled(x, y);
4033 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4035 int sx = SCREENX(x), sy = SCREENY(y);
4038 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4041 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4043 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4046 DrawGraphicAnimation(sx, sy, graphic);
4048 if (GFX_CRUMBLED(element))
4049 DrawLevelFieldCrumbled(x, y);
4052 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4054 if (player->use_murphy)
4056 // this works only because currently only one player can be "murphy" ...
4057 static int last_horizontal_dir = MV_LEFT;
4058 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4060 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4061 last_horizontal_dir = move_dir;
4063 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4065 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4067 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4073 return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4076 static boolean equalGraphics(int graphic1, int graphic2)
4078 struct GraphicInfo *g1 = &graphic_info[graphic1];
4079 struct GraphicInfo *g2 = &graphic_info[graphic2];
4081 return (g1->bitmap == g2->bitmap &&
4082 g1->src_x == g2->src_x &&
4083 g1->src_y == g2->src_y &&
4084 g1->anim_frames == g2->anim_frames &&
4085 g1->anim_delay == g2->anim_delay &&
4086 g1->anim_mode == g2->anim_mode);
4089 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4093 DRAW_PLAYER_STAGE_INIT = 0,
4094 DRAW_PLAYER_STAGE_LAST_FIELD,
4095 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4096 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4097 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4098 DRAW_PLAYER_STAGE_PLAYER,
4100 DRAW_PLAYER_STAGE_PLAYER,
4101 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4103 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4104 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4106 NUM_DRAW_PLAYER_STAGES
4109 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4111 static int static_last_player_graphic[MAX_PLAYERS];
4112 static int static_last_player_frame[MAX_PLAYERS];
4113 static boolean static_player_is_opaque[MAX_PLAYERS];
4114 static boolean draw_player[MAX_PLAYERS];
4115 int pnr = player->index_nr;
4117 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4119 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4120 static_last_player_frame[pnr] = player->Frame;
4121 static_player_is_opaque[pnr] = FALSE;
4123 draw_player[pnr] = TRUE;
4126 if (!draw_player[pnr])
4130 if (!IN_LEV_FIELD(player->jx, player->jy))
4132 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4133 Debug("draw:DrawPlayerExt", "This should never happen!");
4135 draw_player[pnr] = FALSE;
4141 int last_player_graphic = static_last_player_graphic[pnr];
4142 int last_player_frame = static_last_player_frame[pnr];
4143 boolean player_is_opaque = static_player_is_opaque[pnr];
4145 int jx = player->jx;
4146 int jy = player->jy;
4147 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4148 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4149 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4150 int last_jx = (player->is_moving ? jx - dx : jx);
4151 int last_jy = (player->is_moving ? jy - dy : jy);
4152 int next_jx = jx + dx;
4153 int next_jy = jy + dy;
4154 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4155 int sx = SCREENX(jx);
4156 int sy = SCREENY(jy);
4157 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4158 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4159 int element = Tile[jx][jy];
4160 int last_element = Tile[last_jx][last_jy];
4161 int action = (player->is_pushing ? ACTION_PUSHING :
4162 player->is_digging ? ACTION_DIGGING :
4163 player->is_collecting ? ACTION_COLLECTING :
4164 player->is_moving ? ACTION_MOVING :
4165 player->is_snapping ? ACTION_SNAPPING :
4166 player->is_dropping ? ACTION_DROPPING :
4167 player->is_waiting ? player->action_waiting :
4170 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4172 // ------------------------------------------------------------------------
4173 // initialize drawing the player
4174 // ------------------------------------------------------------------------
4176 draw_player[pnr] = FALSE;
4178 // GfxElement[][] is set to the element the player is digging or collecting;
4179 // remove also for off-screen player if the player is not moving anymore
4180 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4181 GfxElement[jx][jy] = EL_UNDEFINED;
4183 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4186 if (element == EL_EXPLOSION)
4189 InitPlayerGfxAnimation(player, action, move_dir);
4191 draw_player[pnr] = TRUE;
4193 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4195 // ------------------------------------------------------------------------
4196 // draw things in the field the player is leaving, if needed
4197 // ------------------------------------------------------------------------
4199 if (!IN_SCR_FIELD(sx, sy))
4200 draw_player[pnr] = FALSE;
4202 if (!player->is_moving)
4205 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4207 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4209 if (last_element == EL_DYNAMITE_ACTIVE ||
4210 last_element == EL_EM_DYNAMITE_ACTIVE ||
4211 last_element == EL_SP_DISK_RED_ACTIVE)
4212 DrawDynamite(last_jx, last_jy);
4214 DrawLevelFieldThruMask(last_jx, last_jy);
4216 else if (last_element == EL_DYNAMITE_ACTIVE ||
4217 last_element == EL_EM_DYNAMITE_ACTIVE ||
4218 last_element == EL_SP_DISK_RED_ACTIVE)
4219 DrawDynamite(last_jx, last_jy);
4221 DrawLevelField(last_jx, last_jy);
4223 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4225 // ------------------------------------------------------------------------
4226 // draw things behind the player, if needed
4227 // ------------------------------------------------------------------------
4231 DrawLevelElement(jx, jy, Back[jx][jy]);
4236 if (IS_ACTIVE_BOMB(element))
4238 DrawLevelElement(jx, jy, EL_EMPTY);
4243 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4245 int old_element = GfxElement[jx][jy];
4246 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4247 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4249 if (GFX_CRUMBLED(old_element))
4250 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4252 DrawScreenGraphic(sx, sy, old_graphic, frame);
4254 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4255 static_player_is_opaque[pnr] = TRUE;
4259 GfxElement[jx][jy] = EL_UNDEFINED;
4261 // make sure that pushed elements are drawn with correct frame rate
4262 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4264 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4265 GfxFrame[jx][jy] = player->StepFrame;
4267 DrawLevelField(jx, jy);
4270 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4272 // ------------------------------------------------------------------------
4273 // draw things the player is pushing, if needed
4274 // ------------------------------------------------------------------------
4276 if (!player->is_pushing || !player->is_moving)
4279 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4282 int gfx_frame = GfxFrame[jx][jy];
4284 if (!IS_MOVING(jx, jy)) // push movement already finished
4286 element = Tile[next_jx][next_jy];
4287 gfx_frame = GfxFrame[next_jx][next_jy];
4290 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4291 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4292 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4294 // draw background element under pushed element (like the Sokoban field)
4295 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4297 // this allows transparent pushing animation over non-black background
4300 DrawLevelElement(jx, jy, Back[jx][jy]);
4302 DrawLevelElement(jx, jy, EL_EMPTY);
4305 if (Back[next_jx][next_jy])
4306 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4308 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4310 int px = SCREENX(jx), py = SCREENY(jy);
4311 int pxx = (TILEX - ABS(sxx)) * dx;
4312 int pyy = (TILEY - ABS(syy)) * dy;
4315 // do not draw (EM style) pushing animation when pushing is finished
4316 // (two-tile animations usually do not contain start and end frame)
4317 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4318 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4320 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4322 // masked drawing is needed for EMC style (double) movement graphics
4323 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4324 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4327 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4329 // ------------------------------------------------------------------------
4330 // draw player himself
4331 // ------------------------------------------------------------------------
4333 int graphic = getPlayerGraphic(player, move_dir);
4335 // in the case of changed player action or direction, prevent the current
4336 // animation frame from being restarted for identical animations
4337 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4338 player->Frame = last_player_frame;
4340 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4342 if (player_is_opaque)
4343 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4345 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4347 if (SHIELD_ON(player))
4349 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4350 IMG_SHIELD_NORMAL_ACTIVE);
4351 frame = getGraphicAnimationFrame(graphic, -1);
4353 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4356 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4358 // ------------------------------------------------------------------------
4359 // draw things in front of player (active dynamite or dynabombs)
4360 // ------------------------------------------------------------------------
4362 if (IS_ACTIVE_BOMB(element))
4364 int graphic = el2img(element);
4365 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4367 if (game.emulation == EMU_SUPAPLEX)
4368 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4370 DrawGraphicThruMask(sx, sy, graphic, frame);
4373 if (player_is_moving && last_element == EL_EXPLOSION)
4375 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4376 GfxElement[last_jx][last_jy] : EL_EMPTY);
4377 int graphic = el_act2img(element, ACTION_EXPLODING);
4378 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4379 int phase = ExplodePhase[last_jx][last_jy] - 1;
4380 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4383 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4386 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4388 // ------------------------------------------------------------------------
4389 // draw elements the player is just walking/passing through/under
4390 // ------------------------------------------------------------------------
4392 if (player_is_moving)
4394 // handle the field the player is leaving ...
4395 if (IS_ACCESSIBLE_INSIDE(last_element))
4396 DrawLevelField(last_jx, last_jy);
4397 else if (IS_ACCESSIBLE_UNDER(last_element))
4398 DrawLevelFieldThruMask(last_jx, last_jy);
4401 // do not redraw accessible elements if the player is just pushing them
4402 if (!player_is_moving || !player->is_pushing)
4404 // ... and the field the player is entering
4405 if (IS_ACCESSIBLE_INSIDE(element))
4406 DrawLevelField(jx, jy);
4407 else if (IS_ACCESSIBLE_UNDER(element))
4408 DrawLevelFieldThruMask(jx, jy);
4411 MarkTileDirty(sx, sy);
4415 void DrawPlayer(struct PlayerInfo *player)
4419 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4420 DrawPlayerExt(player, i);
4423 void DrawAllPlayers(void)
4427 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4428 for (j = 0; j < MAX_PLAYERS; j++)
4429 if (stored_player[j].active)
4430 DrawPlayerExt(&stored_player[j], i);
4433 void DrawPlayerField(int x, int y)
4435 if (!IS_PLAYER(x, y))
4438 DrawPlayer(PLAYERINFO(x, y));
4441 // ----------------------------------------------------------------------------
4443 void WaitForEventToContinue(void)
4445 boolean first_wait = TRUE;
4446 boolean still_wait = TRUE;
4448 if (program.headless)
4451 // simulate releasing mouse button over last gadget, if still pressed
4453 HandleGadgets(-1, -1, 0);
4455 button_status = MB_RELEASED;
4458 ClearPlayerAction();
4464 if (NextValidEvent(&event))
4468 case EVENT_BUTTONPRESS:
4469 case EVENT_FINGERPRESS:
4473 case EVENT_BUTTONRELEASE:
4474 case EVENT_FINGERRELEASE:
4475 still_wait = first_wait;
4478 case EVENT_KEYPRESS:
4479 case SDL_CONTROLLERBUTTONDOWN:
4480 case SDL_JOYBUTTONDOWN:
4485 HandleOtherEvents(&event);
4489 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4494 if (!PendingEvent())
4499 #define MAX_REQUEST_LINES 13
4500 #define MAX_REQUEST_LINE_FONT1_LEN 7
4501 #define MAX_REQUEST_LINE_FONT2_LEN 10
4503 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4505 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4507 int draw_buffer_last = GetDrawtoField();
4508 int width = request.width;
4509 int height = request.height;
4513 // when showing request dialog after game ended, deactivate game panel
4514 if (game_just_ended)
4515 game.panel.active = FALSE;
4517 game.request_active = TRUE;
4519 setRequestPosition(&sx, &sy, FALSE);
4521 button_status = MB_RELEASED;
4523 request_gadget_id = -1;
4528 boolean event_handled = FALSE;
4530 if (game_just_ended)
4532 SetDrawtoField(draw_buffer_game);
4534 HandleGameActions();
4536 SetDrawtoField(DRAW_TO_BACKBUFFER);
4538 if (global.use_envelope_request)
4540 // copy current state of request area to middle of playfield area
4541 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4549 while (NextValidEvent(&event))
4551 event_handled = TRUE;
4555 case EVENT_BUTTONPRESS:
4556 case EVENT_BUTTONRELEASE:
4557 case EVENT_MOTIONNOTIFY:
4561 if (event.type == EVENT_MOTIONNOTIFY)
4566 motion_status = TRUE;
4567 mx = ((MotionEvent *) &event)->x;
4568 my = ((MotionEvent *) &event)->y;
4572 motion_status = FALSE;
4573 mx = ((ButtonEvent *) &event)->x;
4574 my = ((ButtonEvent *) &event)->y;
4575 if (event.type == EVENT_BUTTONPRESS)
4576 button_status = ((ButtonEvent *) &event)->button;
4578 button_status = MB_RELEASED;
4581 // this sets 'request_gadget_id'
4582 HandleGadgets(mx, my, button_status);
4584 switch (request_gadget_id)
4586 case TOOL_CTRL_ID_YES:
4587 case TOOL_CTRL_ID_TOUCH_YES:
4590 case TOOL_CTRL_ID_NO:
4591 case TOOL_CTRL_ID_TOUCH_NO:
4594 case TOOL_CTRL_ID_CONFIRM:
4595 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4596 result = TRUE | FALSE;
4599 case TOOL_CTRL_ID_PLAYER_1:
4602 case TOOL_CTRL_ID_PLAYER_2:
4605 case TOOL_CTRL_ID_PLAYER_3:
4608 case TOOL_CTRL_ID_PLAYER_4:
4613 // only check clickable animations if no request gadget clicked
4614 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4621 case SDL_WINDOWEVENT:
4622 HandleWindowEvent((WindowEvent *) &event);
4625 case SDL_APP_WILLENTERBACKGROUND:
4626 case SDL_APP_DIDENTERBACKGROUND:
4627 case SDL_APP_WILLENTERFOREGROUND:
4628 case SDL_APP_DIDENTERFOREGROUND:
4629 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4632 case EVENT_KEYPRESS:
4634 Key key = GetEventKey((KeyEvent *)&event);
4639 if (req_state & REQ_CONFIRM)
4648 #if defined(KSYM_Rewind)
4649 case KSYM_Rewind: // for Amazon Fire TV remote
4658 #if defined(KSYM_FastForward)
4659 case KSYM_FastForward: // for Amazon Fire TV remote
4665 HandleKeysDebug(key, KEY_PRESSED);
4669 if (req_state & REQ_PLAYER)
4671 int old_player_nr = setup.network_player_nr;
4674 result = old_player_nr + 1;
4679 result = old_player_nr + 1;
4710 case EVENT_FINGERRELEASE:
4711 case EVENT_KEYRELEASE:
4712 ClearPlayerAction();
4715 case SDL_CONTROLLERBUTTONDOWN:
4716 switch (event.cbutton.button)
4718 case SDL_CONTROLLER_BUTTON_A:
4719 case SDL_CONTROLLER_BUTTON_X:
4720 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4721 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4725 case SDL_CONTROLLER_BUTTON_B:
4726 case SDL_CONTROLLER_BUTTON_Y:
4727 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4728 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4729 case SDL_CONTROLLER_BUTTON_BACK:
4734 if (req_state & REQ_PLAYER)
4736 int old_player_nr = setup.network_player_nr;
4739 result = old_player_nr + 1;
4741 switch (event.cbutton.button)
4743 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4744 case SDL_CONTROLLER_BUTTON_Y:
4748 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4749 case SDL_CONTROLLER_BUTTON_B:
4753 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4754 case SDL_CONTROLLER_BUTTON_A:
4758 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4759 case SDL_CONTROLLER_BUTTON_X:
4770 case SDL_CONTROLLERBUTTONUP:
4771 HandleJoystickEvent(&event);
4772 ClearPlayerAction();
4776 HandleOtherEvents(&event);
4781 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4783 int joy = AnyJoystick();
4785 if (joy & JOY_BUTTON_1)
4787 else if (joy & JOY_BUTTON_2)
4790 else if (AnyJoystick())
4792 int joy = AnyJoystick();
4794 if (req_state & REQ_PLAYER)
4798 else if (joy & JOY_RIGHT)
4800 else if (joy & JOY_DOWN)
4802 else if (joy & JOY_LEFT)
4809 if (game_just_ended)
4811 if (global.use_envelope_request)
4813 // copy back current state of pressed buttons inside request area
4814 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4818 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4824 SetDrawtoField(draw_buffer_last);
4826 game.request_active = FALSE;
4831 static boolean RequestDoor(char *text, unsigned int req_state)
4833 int draw_buffer_last = GetDrawtoField();
4834 unsigned int old_door_state;
4835 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4836 int font_nr = FONT_TEXT_2;
4841 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4843 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4844 font_nr = FONT_TEXT_1;
4847 if (game_status == GAME_MODE_PLAYING)
4848 BlitScreenToBitmap(backbuffer);
4850 // disable deactivated drawing when quick-loading level tape recording
4851 if (tape.playing && tape.deactivate_display)
4852 TapeDeactivateDisplayOff(TRUE);
4854 SetMouseCursor(CURSOR_DEFAULT);
4856 // pause network game while waiting for request to answer
4857 if (network.enabled &&
4858 game_status == GAME_MODE_PLAYING &&
4859 !game.all_players_gone &&
4860 req_state & REQUEST_WAIT_FOR_INPUT)
4861 SendToServer_PausePlaying();
4863 old_door_state = GetDoorState();
4865 // simulate releasing mouse button over last gadget, if still pressed
4867 HandleGadgets(-1, -1, 0);
4871 // draw released gadget before proceeding
4874 if (old_door_state & DOOR_OPEN_1)
4876 CloseDoor(DOOR_CLOSE_1);
4878 // save old door content
4879 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4880 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4883 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4884 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4886 // clear door drawing field
4887 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4889 // force DOOR font inside door area
4890 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4892 // write text for request
4893 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4895 char text_line[max_request_line_len + 1];
4901 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4903 tc = *(text_ptr + tx);
4904 // if (!tc || tc == ' ')
4905 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4909 if ((tc == '?' || tc == '!') && tl == 0)
4919 strncpy(text_line, text_ptr, tl);
4922 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4923 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4924 text_line, font_nr);
4926 text_ptr += tl + (tc == ' ' ? 1 : 0);
4927 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4932 if (req_state & REQ_ASK)
4934 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4935 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4936 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4937 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4939 else if (req_state & REQ_CONFIRM)
4941 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4942 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4944 else if (req_state & REQ_PLAYER)
4946 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4947 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4948 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4949 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4952 // copy request gadgets to door backbuffer
4953 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4955 OpenDoor(DOOR_OPEN_1);
4957 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4959 if (game_status == GAME_MODE_PLAYING)
4961 SetPanelBackground();
4962 SetDrawBackgroundMask(REDRAW_DOOR_1);
4966 SetDrawBackgroundMask(REDRAW_FIELD);
4972 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4974 // ---------- handle request buttons ----------
4975 result = RequestHandleEvents(req_state, draw_buffer_last);
4979 if (!(req_state & REQ_STAY_OPEN))
4981 CloseDoor(DOOR_CLOSE_1);
4983 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4984 (req_state & REQ_REOPEN))
4985 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4990 if (game_status == GAME_MODE_PLAYING)
4992 SetPanelBackground();
4993 SetDrawBackgroundMask(REDRAW_DOOR_1);
4997 SetDrawBackgroundMask(REDRAW_FIELD);
5000 // continue network game after request
5001 if (network.enabled &&
5002 game_status == GAME_MODE_PLAYING &&
5003 !game.all_players_gone &&
5004 req_state & REQUEST_WAIT_FOR_INPUT)
5005 SendToServer_ContinuePlaying();
5007 // restore deactivated drawing when quick-loading level tape recording
5008 if (tape.playing && tape.deactivate_display)
5009 TapeDeactivateDisplayOn();
5014 static boolean RequestEnvelope(char *text, unsigned int req_state)
5016 int draw_buffer_last = GetDrawtoField();
5019 if (game_status == GAME_MODE_PLAYING)
5020 BlitScreenToBitmap(backbuffer);
5022 // disable deactivated drawing when quick-loading level tape recording
5023 if (tape.playing && tape.deactivate_display)
5024 TapeDeactivateDisplayOff(TRUE);
5026 SetMouseCursor(CURSOR_DEFAULT);
5028 // pause network game while waiting for request to answer
5029 if (network.enabled &&
5030 game_status == GAME_MODE_PLAYING &&
5031 !game.all_players_gone &&
5032 req_state & REQUEST_WAIT_FOR_INPUT)
5033 SendToServer_PausePlaying();
5035 // simulate releasing mouse button over last gadget, if still pressed
5037 HandleGadgets(-1, -1, 0);
5041 // (replace with setting corresponding request background)
5042 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5043 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5045 // clear door drawing field
5046 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5048 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5050 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5052 if (game_status == GAME_MODE_PLAYING)
5054 SetPanelBackground();
5055 SetDrawBackgroundMask(REDRAW_DOOR_1);
5059 SetDrawBackgroundMask(REDRAW_FIELD);
5065 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5067 // ---------- handle request buttons ----------
5068 result = RequestHandleEvents(req_state, draw_buffer_last);
5072 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5076 if (game_status == GAME_MODE_PLAYING)
5078 SetPanelBackground();
5079 SetDrawBackgroundMask(REDRAW_DOOR_1);
5083 SetDrawBackgroundMask(REDRAW_FIELD);
5086 // continue network game after request
5087 if (network.enabled &&
5088 game_status == GAME_MODE_PLAYING &&
5089 !game.all_players_gone &&
5090 req_state & REQUEST_WAIT_FOR_INPUT)
5091 SendToServer_ContinuePlaying();
5093 // restore deactivated drawing when quick-loading level tape recording
5094 if (tape.playing && tape.deactivate_display)
5095 TapeDeactivateDisplayOn();
5100 boolean Request(char *text, unsigned int req_state)
5102 boolean overlay_enabled = GetOverlayEnabled();
5105 game.request_active_or_moving = TRUE;
5107 SetOverlayEnabled(FALSE);
5109 if (global.use_envelope_request)
5110 result = RequestEnvelope(text, req_state);
5112 result = RequestDoor(text, req_state);
5114 SetOverlayEnabled(overlay_enabled);
5116 game.request_active_or_moving = FALSE;
5121 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5123 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5124 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5127 if (dpo1->sort_priority != dpo2->sort_priority)
5128 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5130 compare_result = dpo1->nr - dpo2->nr;
5132 return compare_result;
5135 void InitGraphicCompatibilityInfo_Doors(void)
5141 struct DoorInfo *door;
5145 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5146 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5148 { -1, -1, -1, NULL }
5150 struct Rect door_rect_list[] =
5152 { DX, DY, DXSIZE, DYSIZE },
5153 { VX, VY, VXSIZE, VYSIZE }
5157 for (i = 0; doors[i].door_token != -1; i++)
5159 int door_token = doors[i].door_token;
5160 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5161 int part_1 = doors[i].part_1;
5162 int part_8 = doors[i].part_8;
5163 int part_2 = part_1 + 1;
5164 int part_3 = part_1 + 2;
5165 struct DoorInfo *door = doors[i].door;
5166 struct Rect *door_rect = &door_rect_list[door_index];
5167 boolean door_gfx_redefined = FALSE;
5169 // check if any door part graphic definitions have been redefined
5171 for (j = 0; door_part_controls[j].door_token != -1; j++)
5173 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5174 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5176 if (dpc->door_token == door_token && fi->redefined)
5177 door_gfx_redefined = TRUE;
5180 // check for old-style door graphic/animation modifications
5182 if (!door_gfx_redefined)
5184 if (door->anim_mode & ANIM_STATIC_PANEL)
5186 door->panel.step_xoffset = 0;
5187 door->panel.step_yoffset = 0;
5190 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5192 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5193 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5194 int num_door_steps, num_panel_steps;
5196 // remove door part graphics other than the two default wings
5198 for (j = 0; door_part_controls[j].door_token != -1; j++)
5200 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5201 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5203 if (dpc->graphic >= part_3 &&
5204 dpc->graphic <= part_8)
5208 // set graphics and screen positions of the default wings
5210 g_part_1->width = door_rect->width;
5211 g_part_1->height = door_rect->height;
5212 g_part_2->width = door_rect->width;
5213 g_part_2->height = door_rect->height;
5214 g_part_2->src_x = door_rect->width;
5215 g_part_2->src_y = g_part_1->src_y;
5217 door->part_2.x = door->part_1.x;
5218 door->part_2.y = door->part_1.y;
5220 if (door->width != -1)
5222 g_part_1->width = door->width;
5223 g_part_2->width = door->width;
5225 // special treatment for graphics and screen position of right wing
5226 g_part_2->src_x += door_rect->width - door->width;
5227 door->part_2.x += door_rect->width - door->width;
5230 if (door->height != -1)
5232 g_part_1->height = door->height;
5233 g_part_2->height = door->height;
5235 // special treatment for graphics and screen position of bottom wing
5236 g_part_2->src_y += door_rect->height - door->height;
5237 door->part_2.y += door_rect->height - door->height;
5240 // set animation delays for the default wings and panels
5242 door->part_1.step_delay = door->step_delay;
5243 door->part_2.step_delay = door->step_delay;
5244 door->panel.step_delay = door->step_delay;
5246 // set animation draw order for the default wings
5248 door->part_1.sort_priority = 2; // draw left wing over ...
5249 door->part_2.sort_priority = 1; // ... right wing
5251 // set animation draw offset for the default wings
5253 if (door->anim_mode & ANIM_HORIZONTAL)
5255 door->part_1.step_xoffset = door->step_offset;
5256 door->part_1.step_yoffset = 0;
5257 door->part_2.step_xoffset = door->step_offset * -1;
5258 door->part_2.step_yoffset = 0;
5260 num_door_steps = g_part_1->width / door->step_offset;
5262 else // ANIM_VERTICAL
5264 door->part_1.step_xoffset = 0;
5265 door->part_1.step_yoffset = door->step_offset;
5266 door->part_2.step_xoffset = 0;
5267 door->part_2.step_yoffset = door->step_offset * -1;
5269 num_door_steps = g_part_1->height / door->step_offset;
5272 // set animation draw offset for the default panels
5274 if (door->step_offset > 1)
5276 num_panel_steps = 2 * door_rect->height / door->step_offset;
5277 door->panel.start_step = num_panel_steps - num_door_steps;
5278 door->panel.start_step_closing = door->panel.start_step;
5282 num_panel_steps = door_rect->height / door->step_offset;
5283 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5284 door->panel.start_step_closing = door->panel.start_step;
5285 door->panel.step_delay *= 2;
5292 void InitDoors(void)
5296 for (i = 0; door_part_controls[i].door_token != -1; i++)
5298 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5299 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5301 // initialize "start_step_opening" and "start_step_closing", if needed
5302 if (dpc->pos->start_step_opening == 0 &&
5303 dpc->pos->start_step_closing == 0)
5305 // dpc->pos->start_step_opening = dpc->pos->start_step;
5306 dpc->pos->start_step_closing = dpc->pos->start_step;
5309 // fill structure for door part draw order (sorted below)
5311 dpo->sort_priority = dpc->pos->sort_priority;
5314 // sort door part controls according to sort_priority and graphic number
5315 qsort(door_part_order, MAX_DOOR_PARTS,
5316 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5319 unsigned int OpenDoor(unsigned int door_state)
5321 if (door_state & DOOR_COPY_BACK)
5323 if (door_state & DOOR_OPEN_1)
5324 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5325 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5327 if (door_state & DOOR_OPEN_2)
5328 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5329 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5331 door_state &= ~DOOR_COPY_BACK;
5334 return MoveDoor(door_state);
5337 unsigned int CloseDoor(unsigned int door_state)
5339 unsigned int old_door_state = GetDoorState();
5341 if (!(door_state & DOOR_NO_COPY_BACK))
5343 if (old_door_state & DOOR_OPEN_1)
5344 BlitBitmap(backbuffer, bitmap_db_door_1,
5345 DX, DY, DXSIZE, DYSIZE, 0, 0);
5347 if (old_door_state & DOOR_OPEN_2)
5348 BlitBitmap(backbuffer, bitmap_db_door_2,
5349 VX, VY, VXSIZE, VYSIZE, 0, 0);
5351 door_state &= ~DOOR_NO_COPY_BACK;
5354 return MoveDoor(door_state);
5357 unsigned int GetDoorState(void)
5359 return MoveDoor(DOOR_GET_STATE);
5362 unsigned int SetDoorState(unsigned int door_state)
5364 return MoveDoor(door_state | DOOR_SET_STATE);
5367 static int euclid(int a, int b)
5369 return (b ? euclid(b, a % b) : a);
5372 unsigned int MoveDoor(unsigned int door_state)
5374 struct Rect door_rect_list[] =
5376 { DX, DY, DXSIZE, DYSIZE },
5377 { VX, VY, VXSIZE, VYSIZE }
5379 static int door1 = DOOR_CLOSE_1;
5380 static int door2 = DOOR_CLOSE_2;
5381 DelayCounter door_delay = { 0 };
5384 if (door_state == DOOR_GET_STATE)
5385 return (door1 | door2);
5387 if (door_state & DOOR_SET_STATE)
5389 if (door_state & DOOR_ACTION_1)
5390 door1 = door_state & DOOR_ACTION_1;
5391 if (door_state & DOOR_ACTION_2)
5392 door2 = door_state & DOOR_ACTION_2;
5394 return (door1 | door2);
5397 if (!(door_state & DOOR_FORCE_REDRAW))
5399 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5400 door_state &= ~DOOR_OPEN_1;
5401 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5402 door_state &= ~DOOR_CLOSE_1;
5403 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5404 door_state &= ~DOOR_OPEN_2;
5405 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5406 door_state &= ~DOOR_CLOSE_2;
5409 if (global.autoplay_leveldir)
5411 door_state |= DOOR_NO_DELAY;
5412 door_state &= ~DOOR_CLOSE_ALL;
5415 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5416 door_state |= DOOR_NO_DELAY;
5418 if (door_state & DOOR_ACTION)
5420 boolean door_panel_drawn[NUM_DOORS];
5421 boolean panel_has_doors[NUM_DOORS];
5422 boolean door_part_skip[MAX_DOOR_PARTS];
5423 boolean door_part_done[MAX_DOOR_PARTS];
5424 boolean door_part_done_all;
5425 int num_steps[MAX_DOOR_PARTS];
5426 int max_move_delay = 0; // delay for complete animations of all doors
5427 int max_step_delay = 0; // delay (ms) between two animation frames
5428 int num_move_steps = 0; // number of animation steps for all doors
5429 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5430 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5434 for (i = 0; i < NUM_DOORS; i++)
5435 panel_has_doors[i] = FALSE;
5437 for (i = 0; i < MAX_DOOR_PARTS; i++)
5439 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5440 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5441 int door_token = dpc->door_token;
5443 door_part_done[i] = FALSE;
5444 door_part_skip[i] = (!(door_state & door_token) ||
5448 for (i = 0; i < MAX_DOOR_PARTS; i++)
5450 int nr = door_part_order[i].nr;
5451 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5452 struct DoorPartPosInfo *pos = dpc->pos;
5453 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5454 int door_token = dpc->door_token;
5455 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5456 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5457 int step_xoffset = ABS(pos->step_xoffset);
5458 int step_yoffset = ABS(pos->step_yoffset);
5459 int step_delay = pos->step_delay;
5460 int current_door_state = door_state & door_token;
5461 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5462 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5463 boolean part_opening = (is_panel ? door_closing : door_opening);
5464 int start_step = (part_opening ? pos->start_step_opening :
5465 pos->start_step_closing);
5466 float move_xsize = (step_xoffset ? g->width : 0);
5467 float move_ysize = (step_yoffset ? g->height : 0);
5468 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5469 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5470 int move_steps = (move_xsteps && move_ysteps ?
5471 MIN(move_xsteps, move_ysteps) :
5472 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5473 int move_delay = move_steps * step_delay;
5475 if (door_part_skip[nr])
5478 max_move_delay = MAX(max_move_delay, move_delay);
5479 max_step_delay = (max_step_delay == 0 ? step_delay :
5480 euclid(max_step_delay, step_delay));
5481 num_steps[nr] = move_steps;
5485 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5487 panel_has_doors[door_index] = TRUE;
5491 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5493 num_move_steps = max_move_delay / max_step_delay;
5494 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5496 door_delay.value = max_step_delay;
5498 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5500 start = num_move_steps - 1;
5504 // opening door sound has priority over simultaneously closing door
5505 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5507 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5509 if (door_state & DOOR_OPEN_1)
5510 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5511 if (door_state & DOOR_OPEN_2)
5512 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5514 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5516 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5518 if (door_state & DOOR_CLOSE_1)
5519 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5520 if (door_state & DOOR_CLOSE_2)
5521 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5525 for (k = start; k < num_move_steps; k++)
5527 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5529 door_part_done_all = TRUE;
5531 for (i = 0; i < NUM_DOORS; i++)
5532 door_panel_drawn[i] = FALSE;
5534 for (i = 0; i < MAX_DOOR_PARTS; i++)
5536 int nr = door_part_order[i].nr;
5537 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5538 struct DoorPartPosInfo *pos = dpc->pos;
5539 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5540 int door_token = dpc->door_token;
5541 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5542 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5543 boolean is_panel_and_door_has_closed = FALSE;
5544 struct Rect *door_rect = &door_rect_list[door_index];
5545 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5547 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5548 int current_door_state = door_state & door_token;
5549 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5550 boolean door_closing = !door_opening;
5551 boolean part_opening = (is_panel ? door_closing : door_opening);
5552 boolean part_closing = !part_opening;
5553 int start_step = (part_opening ? pos->start_step_opening :
5554 pos->start_step_closing);
5555 int step_delay = pos->step_delay;
5556 int step_factor = step_delay / max_step_delay;
5557 int k1 = (step_factor ? k / step_factor + 1 : k);
5558 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5559 int kk = MAX(0, k2);
5562 int src_x, src_y, src_xx, src_yy;
5563 int dst_x, dst_y, dst_xx, dst_yy;
5566 if (door_part_skip[nr])
5569 if (!(door_state & door_token))
5577 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5578 int kk_door = MAX(0, k2_door);
5579 int sync_frame = kk_door * door_delay.value;
5580 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5582 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5583 &g_src_x, &g_src_y);
5588 if (!door_panel_drawn[door_index])
5590 ClearRectangle(drawto, door_rect->x, door_rect->y,
5591 door_rect->width, door_rect->height);
5593 door_panel_drawn[door_index] = TRUE;
5596 // draw opening or closing door parts
5598 if (pos->step_xoffset < 0) // door part on right side
5601 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5604 if (dst_xx + width > door_rect->width)
5605 width = door_rect->width - dst_xx;
5607 else // door part on left side
5610 dst_xx = pos->x - kk * pos->step_xoffset;
5614 src_xx = ABS(dst_xx);
5618 width = g->width - src_xx;
5620 if (width > door_rect->width)
5621 width = door_rect->width;
5623 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5626 if (pos->step_yoffset < 0) // door part on bottom side
5629 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5632 if (dst_yy + height > door_rect->height)
5633 height = door_rect->height - dst_yy;
5635 else // door part on top side
5638 dst_yy = pos->y - kk * pos->step_yoffset;
5642 src_yy = ABS(dst_yy);
5646 height = g->height - src_yy;
5649 src_x = g_src_x + src_xx;
5650 src_y = g_src_y + src_yy;
5652 dst_x = door_rect->x + dst_xx;
5653 dst_y = door_rect->y + dst_yy;
5655 is_panel_and_door_has_closed =
5658 panel_has_doors[door_index] &&
5659 k >= num_move_steps_doors_only - 1);
5661 if (width >= 0 && width <= g->width &&
5662 height >= 0 && height <= g->height &&
5663 !is_panel_and_door_has_closed)
5665 if (is_panel || !pos->draw_masked)
5666 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5669 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5673 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5675 if ((part_opening && (width < 0 || height < 0)) ||
5676 (part_closing && (width >= g->width && height >= g->height)))
5677 door_part_done[nr] = TRUE;
5679 // continue door part animations, but not panel after door has closed
5680 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5681 door_part_done_all = FALSE;
5684 if (!(door_state & DOOR_NO_DELAY))
5688 SkipUntilDelayReached(&door_delay, &k, last_frame);
5690 // prevent OS (Windows) from complaining about program not responding
5694 if (door_part_done_all)
5698 if (!(door_state & DOOR_NO_DELAY))
5700 // wait for specified door action post delay
5701 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5702 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5703 else if (door_state & DOOR_ACTION_1)
5704 door_delay.value = door_1.post_delay;
5705 else if (door_state & DOOR_ACTION_2)
5706 door_delay.value = door_2.post_delay;
5708 while (!DelayReached(&door_delay))
5713 if (door_state & DOOR_ACTION_1)
5714 door1 = door_state & DOOR_ACTION_1;
5715 if (door_state & DOOR_ACTION_2)
5716 door2 = door_state & DOOR_ACTION_2;
5718 // draw masked border over door area
5719 DrawMaskedBorder(REDRAW_DOOR_1);
5720 DrawMaskedBorder(REDRAW_DOOR_2);
5722 ClearAutoRepeatKeyEvents();
5724 return (door1 | door2);
5727 static boolean useSpecialEditorDoor(void)
5729 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5730 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5732 // do not draw special editor door if editor border defined or redefined
5733 if (graphic_info[graphic].bitmap != NULL || redefined)
5736 // do not draw special editor door if global border defined to be empty
5737 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5740 // do not draw special editor door if viewport definitions do not match
5744 EY + EYSIZE != VY + VYSIZE)
5750 void DrawSpecialEditorDoor(void)
5752 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5753 int top_border_width = gfx1->width;
5754 int top_border_height = gfx1->height;
5755 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5756 int ex = EX - outer_border;
5757 int ey = EY - outer_border;
5758 int vy = VY - outer_border;
5759 int exsize = EXSIZE + 2 * outer_border;
5761 if (!useSpecialEditorDoor())
5764 // draw bigger level editor toolbox window
5765 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5766 top_border_width, top_border_height, ex, ey - top_border_height);
5767 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5768 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5770 redraw_mask |= REDRAW_ALL;
5773 void UndrawSpecialEditorDoor(void)
5775 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5776 int top_border_width = gfx1->width;
5777 int top_border_height = gfx1->height;
5778 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5779 int ex = EX - outer_border;
5780 int ey = EY - outer_border;
5781 int ey_top = ey - top_border_height;
5782 int exsize = EXSIZE + 2 * outer_border;
5783 int eysize = EYSIZE + 2 * outer_border;
5785 if (!useSpecialEditorDoor())
5788 // draw normal tape recorder window
5789 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5791 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5792 ex, ey_top, top_border_width, top_border_height,
5794 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5795 ex, ey, exsize, eysize, ex, ey);
5799 // if screen background is set to "[NONE]", clear editor toolbox window
5800 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5801 ClearRectangle(drawto, ex, ey, exsize, eysize);
5804 redraw_mask |= REDRAW_ALL;
5808 // ---------- new tool button stuff -------------------------------------------
5813 struct TextPosInfo *pos;
5815 boolean is_touch_button;
5817 } toolbutton_info[NUM_TOOL_BUTTONS] =
5820 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5821 TOOL_CTRL_ID_YES, FALSE, "yes"
5824 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5825 TOOL_CTRL_ID_NO, FALSE, "no"
5828 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5829 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5832 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5833 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5836 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5837 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5840 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5841 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5844 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5845 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5848 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5849 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5852 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5853 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5856 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5857 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5861 void CreateToolButtons(void)
5865 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5867 int graphic = toolbutton_info[i].graphic;
5868 struct GraphicInfo *gfx = &graphic_info[graphic];
5869 struct TextPosInfo *pos = toolbutton_info[i].pos;
5870 struct GadgetInfo *gi;
5871 Bitmap *deco_bitmap = None;
5872 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5873 unsigned int event_mask = GD_EVENT_RELEASED;
5874 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5875 int base_x = (is_touch_button ? 0 : DX);
5876 int base_y = (is_touch_button ? 0 : DY);
5877 int gd_x = gfx->src_x;
5878 int gd_y = gfx->src_y;
5879 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5880 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5885 // do not use touch buttons if overlay touch buttons are disabled
5886 if (is_touch_button && !setup.touch.overlay_buttons)
5889 if (global.use_envelope_request && !is_touch_button)
5891 setRequestPosition(&base_x, &base_y, TRUE);
5893 // check if request buttons are outside of envelope and fix, if needed
5894 if (x < 0 || x + gfx->width > request.width ||
5895 y < 0 || y + gfx->height > request.height)
5897 if (id == TOOL_CTRL_ID_YES)
5900 y = request.height - 2 * request.border_size - gfx->height;
5902 else if (id == TOOL_CTRL_ID_NO)
5904 x = request.width - 2 * request.border_size - gfx->width;
5905 y = request.height - 2 * request.border_size - gfx->height;
5907 else if (id == TOOL_CTRL_ID_CONFIRM)
5909 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5910 y = request.height - 2 * request.border_size - gfx->height;
5912 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5914 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5916 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5917 y = request.height - 2 * request.border_size - gfx->height * 2;
5919 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5920 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5925 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5928 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5930 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5931 pos->size, &deco_bitmap, &deco_x, &deco_y);
5932 deco_xpos = (gfx->width - pos->size) / 2;
5933 deco_ypos = (gfx->height - pos->size) / 2;
5936 gi = CreateGadget(GDI_CUSTOM_ID, id,
5937 GDI_IMAGE_ID, graphic,
5938 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5941 GDI_WIDTH, gfx->width,
5942 GDI_HEIGHT, gfx->height,
5943 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5944 GDI_STATE, GD_BUTTON_UNPRESSED,
5945 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5946 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5947 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5948 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5949 GDI_DECORATION_SIZE, pos->size, pos->size,
5950 GDI_DECORATION_SHIFTING, 1, 1,
5951 GDI_DIRECT_DRAW, FALSE,
5952 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5953 GDI_EVENT_MASK, event_mask,
5954 GDI_CALLBACK_ACTION, HandleToolButtons,
5958 Fail("cannot create gadget");
5960 tool_gadget[id] = gi;
5964 void FreeToolButtons(void)
5968 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5969 FreeGadget(tool_gadget[i]);
5972 static void UnmapToolButtons(void)
5976 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5977 UnmapGadget(tool_gadget[i]);
5980 static void HandleToolButtons(struct GadgetInfo *gi)
5982 request_gadget_id = gi->custom_id;
5985 static struct Mapping_EM_to_RND_object
5988 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5989 boolean is_backside; // backside of moving element
5995 em_object_mapping_list[GAME_TILE_MAX + 1] =
5998 Zborder, FALSE, FALSE,
6002 Zplayer, FALSE, FALSE,
6011 Ztank, FALSE, FALSE,
6015 Zeater, FALSE, FALSE,
6019 Zdynamite, FALSE, FALSE,
6023 Zboom, FALSE, FALSE,
6028 Xchain, FALSE, FALSE,
6029 EL_DEFAULT, ACTION_EXPLODING, -1
6032 Xboom_bug, FALSE, FALSE,
6033 EL_BUG, ACTION_EXPLODING, -1
6036 Xboom_tank, FALSE, FALSE,
6037 EL_SPACESHIP, ACTION_EXPLODING, -1
6040 Xboom_android, FALSE, FALSE,
6041 EL_EMC_ANDROID, ACTION_OTHER, -1
6044 Xboom_1, FALSE, FALSE,
6045 EL_DEFAULT, ACTION_EXPLODING, -1
6048 Xboom_2, FALSE, FALSE,
6049 EL_DEFAULT, ACTION_EXPLODING, -1
6053 Xblank, TRUE, FALSE,
6058 Xsplash_e, FALSE, FALSE,
6059 EL_ACID_SPLASH_RIGHT, -1, -1
6062 Xsplash_w, FALSE, FALSE,
6063 EL_ACID_SPLASH_LEFT, -1, -1
6067 Xplant, TRUE, FALSE,
6068 EL_EMC_PLANT, -1, -1
6071 Yplant, FALSE, FALSE,
6072 EL_EMC_PLANT, -1, -1
6076 Xacid_1, TRUE, FALSE,
6080 Xacid_2, FALSE, FALSE,
6084 Xacid_3, FALSE, FALSE,
6088 Xacid_4, FALSE, FALSE,
6092 Xacid_5, FALSE, FALSE,
6096 Xacid_6, FALSE, FALSE,
6100 Xacid_7, FALSE, FALSE,
6104 Xacid_8, FALSE, FALSE,
6109 Xfake_acid_1, TRUE, FALSE,
6110 EL_EMC_FAKE_ACID, -1, -1
6113 Xfake_acid_2, FALSE, FALSE,
6114 EL_EMC_FAKE_ACID, -1, -1
6117 Xfake_acid_3, FALSE, FALSE,
6118 EL_EMC_FAKE_ACID, -1, -1
6121 Xfake_acid_4, FALSE, FALSE,
6122 EL_EMC_FAKE_ACID, -1, -1
6125 Xfake_acid_5, FALSE, FALSE,
6126 EL_EMC_FAKE_ACID, -1, -1
6129 Xfake_acid_6, FALSE, FALSE,
6130 EL_EMC_FAKE_ACID, -1, -1
6133 Xfake_acid_7, FALSE, FALSE,
6134 EL_EMC_FAKE_ACID, -1, -1
6137 Xfake_acid_8, FALSE, FALSE,
6138 EL_EMC_FAKE_ACID, -1, -1
6142 Xfake_acid_1_player, FALSE, FALSE,
6143 EL_EMC_FAKE_ACID, -1, -1
6146 Xfake_acid_2_player, FALSE, FALSE,
6147 EL_EMC_FAKE_ACID, -1, -1
6150 Xfake_acid_3_player, FALSE, FALSE,
6151 EL_EMC_FAKE_ACID, -1, -1
6154 Xfake_acid_4_player, FALSE, FALSE,
6155 EL_EMC_FAKE_ACID, -1, -1
6158 Xfake_acid_5_player, FALSE, FALSE,
6159 EL_EMC_FAKE_ACID, -1, -1
6162 Xfake_acid_6_player, FALSE, FALSE,
6163 EL_EMC_FAKE_ACID, -1, -1
6166 Xfake_acid_7_player, FALSE, FALSE,
6167 EL_EMC_FAKE_ACID, -1, -1
6170 Xfake_acid_8_player, FALSE, FALSE,
6171 EL_EMC_FAKE_ACID, -1, -1
6175 Xgrass, TRUE, FALSE,
6176 EL_EMC_GRASS, -1, -1
6179 Ygrass_nB, FALSE, FALSE,
6180 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6183 Ygrass_eB, FALSE, FALSE,
6184 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6187 Ygrass_sB, FALSE, FALSE,
6188 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6191 Ygrass_wB, FALSE, FALSE,
6192 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6200 Ydirt_nB, FALSE, FALSE,
6201 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6204 Ydirt_eB, FALSE, FALSE,
6205 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6208 Ydirt_sB, FALSE, FALSE,
6209 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6212 Ydirt_wB, FALSE, FALSE,
6213 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6217 Xandroid, TRUE, FALSE,
6218 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6221 Xandroid_1_n, FALSE, FALSE,
6222 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6225 Xandroid_2_n, FALSE, FALSE,
6226 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6229 Xandroid_1_e, FALSE, FALSE,
6230 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6233 Xandroid_2_e, FALSE, FALSE,
6234 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6237 Xandroid_1_w, FALSE, FALSE,
6238 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6241 Xandroid_2_w, FALSE, FALSE,
6242 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6245 Xandroid_1_s, FALSE, FALSE,
6246 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6249 Xandroid_2_s, FALSE, FALSE,
6250 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6253 Yandroid_n, FALSE, FALSE,
6254 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6257 Yandroid_nB, FALSE, TRUE,
6258 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6261 Yandroid_ne, FALSE, FALSE,
6262 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6265 Yandroid_neB, FALSE, TRUE,
6266 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6269 Yandroid_e, FALSE, FALSE,
6270 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6273 Yandroid_eB, FALSE, TRUE,
6274 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6277 Yandroid_se, FALSE, FALSE,
6278 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6281 Yandroid_seB, FALSE, TRUE,
6282 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6285 Yandroid_s, FALSE, FALSE,
6286 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6289 Yandroid_sB, FALSE, TRUE,
6290 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6293 Yandroid_sw, FALSE, FALSE,
6294 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6297 Yandroid_swB, FALSE, TRUE,
6298 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6301 Yandroid_w, FALSE, FALSE,
6302 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6305 Yandroid_wB, FALSE, TRUE,
6306 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6309 Yandroid_nw, FALSE, FALSE,
6310 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6313 Yandroid_nwB, FALSE, TRUE,
6314 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6318 Xeater_n, TRUE, FALSE,
6319 EL_YAMYAM_UP, -1, -1
6322 Xeater_e, TRUE, FALSE,
6323 EL_YAMYAM_RIGHT, -1, -1
6326 Xeater_w, TRUE, FALSE,
6327 EL_YAMYAM_LEFT, -1, -1
6330 Xeater_s, TRUE, FALSE,
6331 EL_YAMYAM_DOWN, -1, -1
6334 Yeater_n, FALSE, FALSE,
6335 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6338 Yeater_nB, FALSE, TRUE,
6339 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6342 Yeater_e, FALSE, FALSE,
6343 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6346 Yeater_eB, FALSE, TRUE,
6347 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6350 Yeater_s, FALSE, FALSE,
6351 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6354 Yeater_sB, FALSE, TRUE,
6355 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6358 Yeater_w, FALSE, FALSE,
6359 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6362 Yeater_wB, FALSE, TRUE,
6363 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6366 Yeater_stone, FALSE, FALSE,
6367 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6370 Yeater_spring, FALSE, FALSE,
6371 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6375 Xalien, TRUE, FALSE,
6379 Xalien_pause, FALSE, FALSE,
6383 Yalien_n, FALSE, FALSE,
6384 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6387 Yalien_nB, FALSE, TRUE,
6388 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6391 Yalien_e, FALSE, FALSE,
6392 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6395 Yalien_eB, FALSE, TRUE,
6396 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6399 Yalien_s, FALSE, FALSE,
6400 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6403 Yalien_sB, FALSE, TRUE,
6404 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6407 Yalien_w, FALSE, FALSE,
6408 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6411 Yalien_wB, FALSE, TRUE,
6412 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6415 Yalien_stone, FALSE, FALSE,
6416 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6419 Yalien_spring, FALSE, FALSE,
6420 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6424 Xbug_1_n, TRUE, FALSE,
6428 Xbug_1_e, TRUE, FALSE,
6429 EL_BUG_RIGHT, -1, -1
6432 Xbug_1_s, TRUE, FALSE,
6436 Xbug_1_w, TRUE, FALSE,
6440 Xbug_2_n, FALSE, FALSE,
6444 Xbug_2_e, FALSE, FALSE,
6445 EL_BUG_RIGHT, -1, -1
6448 Xbug_2_s, FALSE, FALSE,
6452 Xbug_2_w, FALSE, FALSE,
6456 Ybug_n, FALSE, FALSE,
6457 EL_BUG, ACTION_MOVING, MV_BIT_UP
6460 Ybug_nB, FALSE, TRUE,
6461 EL_BUG, ACTION_MOVING, MV_BIT_UP
6464 Ybug_e, FALSE, FALSE,
6465 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6468 Ybug_eB, FALSE, TRUE,
6469 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6472 Ybug_s, FALSE, FALSE,
6473 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6476 Ybug_sB, FALSE, TRUE,
6477 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6480 Ybug_w, FALSE, FALSE,
6481 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6484 Ybug_wB, FALSE, TRUE,
6485 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6488 Ybug_w_n, FALSE, FALSE,
6489 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6492 Ybug_n_e, FALSE, FALSE,
6493 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6496 Ybug_e_s, FALSE, FALSE,
6497 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6500 Ybug_s_w, FALSE, FALSE,
6501 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6504 Ybug_e_n, FALSE, FALSE,
6505 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6508 Ybug_s_e, FALSE, FALSE,
6509 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6512 Ybug_w_s, FALSE, FALSE,
6513 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6516 Ybug_n_w, FALSE, FALSE,
6517 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6520 Ybug_stone, FALSE, FALSE,
6521 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6524 Ybug_spring, FALSE, FALSE,
6525 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6529 Xtank_1_n, TRUE, FALSE,
6530 EL_SPACESHIP_UP, -1, -1
6533 Xtank_1_e, TRUE, FALSE,
6534 EL_SPACESHIP_RIGHT, -1, -1
6537 Xtank_1_s, TRUE, FALSE,
6538 EL_SPACESHIP_DOWN, -1, -1
6541 Xtank_1_w, TRUE, FALSE,
6542 EL_SPACESHIP_LEFT, -1, -1
6545 Xtank_2_n, FALSE, FALSE,
6546 EL_SPACESHIP_UP, -1, -1
6549 Xtank_2_e, FALSE, FALSE,
6550 EL_SPACESHIP_RIGHT, -1, -1
6553 Xtank_2_s, FALSE, FALSE,
6554 EL_SPACESHIP_DOWN, -1, -1
6557 Xtank_2_w, FALSE, FALSE,
6558 EL_SPACESHIP_LEFT, -1, -1
6561 Ytank_n, FALSE, FALSE,
6562 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6565 Ytank_nB, FALSE, TRUE,
6566 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6569 Ytank_e, FALSE, FALSE,
6570 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6573 Ytank_eB, FALSE, TRUE,
6574 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6577 Ytank_s, FALSE, FALSE,
6578 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6581 Ytank_sB, FALSE, TRUE,
6582 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6585 Ytank_w, FALSE, FALSE,
6586 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6589 Ytank_wB, FALSE, TRUE,
6590 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6593 Ytank_w_n, FALSE, FALSE,
6594 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6597 Ytank_n_e, FALSE, FALSE,
6598 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6601 Ytank_e_s, FALSE, FALSE,
6602 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6605 Ytank_s_w, FALSE, FALSE,
6606 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6609 Ytank_e_n, FALSE, FALSE,
6610 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6613 Ytank_s_e, FALSE, FALSE,
6614 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6617 Ytank_w_s, FALSE, FALSE,
6618 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6621 Ytank_n_w, FALSE, FALSE,
6622 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6625 Ytank_stone, FALSE, FALSE,
6626 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6629 Ytank_spring, FALSE, FALSE,
6630 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6634 Xemerald, TRUE, FALSE,
6638 Xemerald_pause, FALSE, FALSE,
6642 Xemerald_fall, FALSE, FALSE,
6646 Xemerald_shine, FALSE, FALSE,
6647 EL_EMERALD, ACTION_TWINKLING, -1
6650 Yemerald_s, FALSE, FALSE,
6651 EL_EMERALD, ACTION_FALLING, -1
6654 Yemerald_sB, FALSE, TRUE,
6655 EL_EMERALD, ACTION_FALLING, -1
6658 Yemerald_e, FALSE, FALSE,
6659 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6662 Yemerald_eB, FALSE, TRUE,
6663 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6666 Yemerald_w, FALSE, FALSE,
6667 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6670 Yemerald_wB, FALSE, TRUE,
6671 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6674 Yemerald_blank, FALSE, FALSE,
6675 EL_EMERALD, ACTION_COLLECTING, -1
6679 Xdiamond, TRUE, FALSE,
6683 Xdiamond_pause, FALSE, FALSE,
6687 Xdiamond_fall, FALSE, FALSE,
6691 Xdiamond_shine, FALSE, FALSE,
6692 EL_DIAMOND, ACTION_TWINKLING, -1
6695 Ydiamond_s, FALSE, FALSE,
6696 EL_DIAMOND, ACTION_FALLING, -1
6699 Ydiamond_sB, FALSE, TRUE,
6700 EL_DIAMOND, ACTION_FALLING, -1
6703 Ydiamond_e, FALSE, FALSE,
6704 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6707 Ydiamond_eB, FALSE, TRUE,
6708 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6711 Ydiamond_w, FALSE, FALSE,
6712 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6715 Ydiamond_wB, FALSE, TRUE,
6716 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6719 Ydiamond_blank, FALSE, FALSE,
6720 EL_DIAMOND, ACTION_COLLECTING, -1
6723 Ydiamond_stone, FALSE, FALSE,
6724 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6728 Xstone, TRUE, FALSE,
6732 Xstone_pause, FALSE, FALSE,
6736 Xstone_fall, FALSE, FALSE,
6740 Ystone_s, FALSE, FALSE,
6741 EL_ROCK, ACTION_FALLING, -1
6744 Ystone_sB, FALSE, TRUE,
6745 EL_ROCK, ACTION_FALLING, -1
6748 Ystone_e, FALSE, FALSE,
6749 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6752 Ystone_eB, FALSE, TRUE,
6753 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6756 Ystone_w, FALSE, FALSE,
6757 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6760 Ystone_wB, FALSE, TRUE,
6761 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6769 Xbomb_pause, FALSE, FALSE,
6773 Xbomb_fall, FALSE, FALSE,
6777 Ybomb_s, FALSE, FALSE,
6778 EL_BOMB, ACTION_FALLING, -1
6781 Ybomb_sB, FALSE, TRUE,
6782 EL_BOMB, ACTION_FALLING, -1
6785 Ybomb_e, FALSE, FALSE,
6786 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6789 Ybomb_eB, FALSE, TRUE,
6790 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6793 Ybomb_w, FALSE, FALSE,
6794 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6797 Ybomb_wB, FALSE, TRUE,
6798 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6801 Ybomb_blank, FALSE, FALSE,
6802 EL_BOMB, ACTION_ACTIVATING, -1
6810 Xnut_pause, FALSE, FALSE,
6814 Xnut_fall, FALSE, FALSE,
6818 Ynut_s, FALSE, FALSE,
6819 EL_NUT, ACTION_FALLING, -1
6822 Ynut_sB, FALSE, TRUE,
6823 EL_NUT, ACTION_FALLING, -1
6826 Ynut_e, FALSE, FALSE,
6827 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6830 Ynut_eB, FALSE, TRUE,
6831 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6834 Ynut_w, FALSE, FALSE,
6835 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6838 Ynut_wB, FALSE, TRUE,
6839 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6842 Ynut_stone, FALSE, FALSE,
6843 EL_NUT, ACTION_BREAKING, -1
6847 Xspring, TRUE, FALSE,
6851 Xspring_pause, FALSE, FALSE,
6855 Xspring_e, TRUE, FALSE,
6856 EL_SPRING_RIGHT, -1, -1
6859 Xspring_w, TRUE, FALSE,
6860 EL_SPRING_LEFT, -1, -1
6863 Xspring_fall, FALSE, FALSE,
6867 Yspring_s, FALSE, FALSE,
6868 EL_SPRING, ACTION_FALLING, -1
6871 Yspring_sB, FALSE, TRUE,
6872 EL_SPRING, ACTION_FALLING, -1
6875 Yspring_e, FALSE, FALSE,
6876 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6879 Yspring_eB, FALSE, TRUE,
6880 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6883 Yspring_w, FALSE, FALSE,
6884 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6887 Yspring_wB, FALSE, TRUE,
6888 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6891 Yspring_alien_e, FALSE, FALSE,
6892 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6895 Yspring_alien_eB, FALSE, TRUE,
6896 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6899 Yspring_alien_w, FALSE, FALSE,
6900 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6903 Yspring_alien_wB, FALSE, TRUE,
6904 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6908 Xpush_emerald_e, FALSE, FALSE,
6909 EL_EMERALD, -1, MV_BIT_RIGHT
6912 Xpush_emerald_w, FALSE, FALSE,
6913 EL_EMERALD, -1, MV_BIT_LEFT
6916 Xpush_diamond_e, FALSE, FALSE,
6917 EL_DIAMOND, -1, MV_BIT_RIGHT
6920 Xpush_diamond_w, FALSE, FALSE,
6921 EL_DIAMOND, -1, MV_BIT_LEFT
6924 Xpush_stone_e, FALSE, FALSE,
6925 EL_ROCK, -1, MV_BIT_RIGHT
6928 Xpush_stone_w, FALSE, FALSE,
6929 EL_ROCK, -1, MV_BIT_LEFT
6932 Xpush_bomb_e, FALSE, FALSE,
6933 EL_BOMB, -1, MV_BIT_RIGHT
6936 Xpush_bomb_w, FALSE, FALSE,
6937 EL_BOMB, -1, MV_BIT_LEFT
6940 Xpush_nut_e, FALSE, FALSE,
6941 EL_NUT, -1, MV_BIT_RIGHT
6944 Xpush_nut_w, FALSE, FALSE,
6945 EL_NUT, -1, MV_BIT_LEFT
6948 Xpush_spring_e, FALSE, FALSE,
6949 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6952 Xpush_spring_w, FALSE, FALSE,
6953 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6957 Xdynamite, TRUE, FALSE,
6958 EL_EM_DYNAMITE, -1, -1
6961 Ydynamite_blank, FALSE, FALSE,
6962 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6965 Xdynamite_1, TRUE, FALSE,
6966 EL_EM_DYNAMITE_ACTIVE, -1, -1
6969 Xdynamite_2, FALSE, FALSE,
6970 EL_EM_DYNAMITE_ACTIVE, -1, -1
6973 Xdynamite_3, FALSE, FALSE,
6974 EL_EM_DYNAMITE_ACTIVE, -1, -1
6977 Xdynamite_4, FALSE, FALSE,
6978 EL_EM_DYNAMITE_ACTIVE, -1, -1
6982 Xkey_1, TRUE, FALSE,
6986 Xkey_2, TRUE, FALSE,
6990 Xkey_3, TRUE, FALSE,
6994 Xkey_4, TRUE, FALSE,
6998 Xkey_5, TRUE, FALSE,
6999 EL_EMC_KEY_5, -1, -1
7002 Xkey_6, TRUE, FALSE,
7003 EL_EMC_KEY_6, -1, -1
7006 Xkey_7, TRUE, FALSE,
7007 EL_EMC_KEY_7, -1, -1
7010 Xkey_8, TRUE, FALSE,
7011 EL_EMC_KEY_8, -1, -1
7015 Xdoor_1, TRUE, FALSE,
7016 EL_EM_GATE_1, -1, -1
7019 Xdoor_2, TRUE, FALSE,
7020 EL_EM_GATE_2, -1, -1
7023 Xdoor_3, TRUE, FALSE,
7024 EL_EM_GATE_3, -1, -1
7027 Xdoor_4, TRUE, FALSE,
7028 EL_EM_GATE_4, -1, -1
7031 Xdoor_5, TRUE, FALSE,
7032 EL_EMC_GATE_5, -1, -1
7035 Xdoor_6, TRUE, FALSE,
7036 EL_EMC_GATE_6, -1, -1
7039 Xdoor_7, TRUE, FALSE,
7040 EL_EMC_GATE_7, -1, -1
7043 Xdoor_8, TRUE, FALSE,
7044 EL_EMC_GATE_8, -1, -1
7048 Xfake_door_1, TRUE, FALSE,
7049 EL_EM_GATE_1_GRAY, -1, -1
7052 Xfake_door_2, TRUE, FALSE,
7053 EL_EM_GATE_2_GRAY, -1, -1
7056 Xfake_door_3, TRUE, FALSE,
7057 EL_EM_GATE_3_GRAY, -1, -1
7060 Xfake_door_4, TRUE, FALSE,
7061 EL_EM_GATE_4_GRAY, -1, -1
7064 Xfake_door_5, TRUE, FALSE,
7065 EL_EMC_GATE_5_GRAY, -1, -1
7068 Xfake_door_6, TRUE, FALSE,
7069 EL_EMC_GATE_6_GRAY, -1, -1
7072 Xfake_door_7, TRUE, FALSE,
7073 EL_EMC_GATE_7_GRAY, -1, -1
7076 Xfake_door_8, TRUE, FALSE,
7077 EL_EMC_GATE_8_GRAY, -1, -1
7081 Xballoon, TRUE, FALSE,
7085 Yballoon_n, FALSE, FALSE,
7086 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7089 Yballoon_nB, FALSE, TRUE,
7090 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7093 Yballoon_e, FALSE, FALSE,
7094 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7097 Yballoon_eB, FALSE, TRUE,
7098 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7101 Yballoon_s, FALSE, FALSE,
7102 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7105 Yballoon_sB, FALSE, TRUE,
7106 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7109 Yballoon_w, FALSE, FALSE,
7110 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7113 Yballoon_wB, FALSE, TRUE,
7114 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7118 Xball_1, TRUE, FALSE,
7119 EL_EMC_MAGIC_BALL, -1, -1
7122 Yball_1, FALSE, FALSE,
7123 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7126 Xball_2, FALSE, FALSE,
7127 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7130 Yball_2, FALSE, FALSE,
7131 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7134 Yball_blank, FALSE, FALSE,
7135 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7139 Xamoeba_1, TRUE, FALSE,
7140 EL_AMOEBA_DRY, ACTION_OTHER, -1
7143 Xamoeba_2, FALSE, FALSE,
7144 EL_AMOEBA_DRY, ACTION_OTHER, -1
7147 Xamoeba_3, FALSE, FALSE,
7148 EL_AMOEBA_DRY, ACTION_OTHER, -1
7151 Xamoeba_4, FALSE, FALSE,
7152 EL_AMOEBA_DRY, ACTION_OTHER, -1
7155 Xamoeba_5, TRUE, FALSE,
7156 EL_AMOEBA_WET, ACTION_OTHER, -1
7159 Xamoeba_6, FALSE, FALSE,
7160 EL_AMOEBA_WET, ACTION_OTHER, -1
7163 Xamoeba_7, FALSE, FALSE,
7164 EL_AMOEBA_WET, ACTION_OTHER, -1
7167 Xamoeba_8, FALSE, FALSE,
7168 EL_AMOEBA_WET, ACTION_OTHER, -1
7173 EL_AMOEBA_DROP, ACTION_GROWING, -1
7176 Xdrip_fall, FALSE, FALSE,
7177 EL_AMOEBA_DROP, -1, -1
7180 Xdrip_stretch, FALSE, FALSE,
7181 EL_AMOEBA_DROP, ACTION_FALLING, -1
7184 Xdrip_stretchB, FALSE, TRUE,
7185 EL_AMOEBA_DROP, ACTION_FALLING, -1
7188 Ydrip_1_s, FALSE, FALSE,
7189 EL_AMOEBA_DROP, ACTION_FALLING, -1
7192 Ydrip_1_sB, FALSE, TRUE,
7193 EL_AMOEBA_DROP, ACTION_FALLING, -1
7196 Ydrip_2_s, FALSE, FALSE,
7197 EL_AMOEBA_DROP, ACTION_FALLING, -1
7200 Ydrip_2_sB, FALSE, TRUE,
7201 EL_AMOEBA_DROP, ACTION_FALLING, -1
7205 Xwonderwall, TRUE, FALSE,
7206 EL_MAGIC_WALL, -1, -1
7209 Ywonderwall, FALSE, FALSE,
7210 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7214 Xwheel, TRUE, FALSE,
7215 EL_ROBOT_WHEEL, -1, -1
7218 Ywheel, FALSE, FALSE,
7219 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7223 Xswitch, TRUE, FALSE,
7224 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7227 Yswitch, FALSE, FALSE,
7228 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7232 Xbumper, TRUE, FALSE,
7233 EL_EMC_SPRING_BUMPER, -1, -1
7236 Ybumper, FALSE, FALSE,
7237 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7241 Xacid_nw, TRUE, FALSE,
7242 EL_ACID_POOL_TOPLEFT, -1, -1
7245 Xacid_ne, TRUE, FALSE,
7246 EL_ACID_POOL_TOPRIGHT, -1, -1
7249 Xacid_sw, TRUE, FALSE,
7250 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7253 Xacid_s, TRUE, FALSE,
7254 EL_ACID_POOL_BOTTOM, -1, -1
7257 Xacid_se, TRUE, FALSE,
7258 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7262 Xfake_blank, TRUE, FALSE,
7263 EL_INVISIBLE_WALL, -1, -1
7266 Yfake_blank, FALSE, FALSE,
7267 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7271 Xfake_grass, TRUE, FALSE,
7272 EL_EMC_FAKE_GRASS, -1, -1
7275 Yfake_grass, FALSE, FALSE,
7276 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7280 Xfake_amoeba, TRUE, FALSE,
7281 EL_EMC_DRIPPER, -1, -1
7284 Yfake_amoeba, FALSE, FALSE,
7285 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7289 Xlenses, TRUE, FALSE,
7290 EL_EMC_LENSES, -1, -1
7294 Xmagnify, TRUE, FALSE,
7295 EL_EMC_MAGNIFIER, -1, -1
7300 EL_QUICKSAND_EMPTY, -1, -1
7303 Xsand_stone, TRUE, FALSE,
7304 EL_QUICKSAND_FULL, -1, -1
7307 Xsand_stonein_1, FALSE, TRUE,
7308 EL_ROCK, ACTION_FILLING, -1
7311 Xsand_stonein_2, FALSE, TRUE,
7312 EL_ROCK, ACTION_FILLING, -1
7315 Xsand_stonein_3, FALSE, TRUE,
7316 EL_ROCK, ACTION_FILLING, -1
7319 Xsand_stonein_4, FALSE, TRUE,
7320 EL_ROCK, ACTION_FILLING, -1
7323 Xsand_sandstone_1, FALSE, FALSE,
7324 EL_QUICKSAND_FILLING, -1, -1
7327 Xsand_sandstone_2, FALSE, FALSE,
7328 EL_QUICKSAND_FILLING, -1, -1
7331 Xsand_sandstone_3, FALSE, FALSE,
7332 EL_QUICKSAND_FILLING, -1, -1
7335 Xsand_sandstone_4, FALSE, FALSE,
7336 EL_QUICKSAND_FILLING, -1, -1
7339 Xsand_stonesand_1, FALSE, FALSE,
7340 EL_QUICKSAND_EMPTYING, -1, -1
7343 Xsand_stonesand_2, FALSE, FALSE,
7344 EL_QUICKSAND_EMPTYING, -1, -1
7347 Xsand_stonesand_3, FALSE, FALSE,
7348 EL_QUICKSAND_EMPTYING, -1, -1
7351 Xsand_stonesand_4, FALSE, FALSE,
7352 EL_QUICKSAND_EMPTYING, -1, -1
7355 Xsand_stoneout_1, FALSE, FALSE,
7356 EL_ROCK, ACTION_EMPTYING, -1
7359 Xsand_stoneout_2, FALSE, FALSE,
7360 EL_ROCK, ACTION_EMPTYING, -1
7363 Xsand_stonesand_quickout_1, FALSE, FALSE,
7364 EL_QUICKSAND_EMPTYING, -1, -1
7367 Xsand_stonesand_quickout_2, FALSE, FALSE,
7368 EL_QUICKSAND_EMPTYING, -1, -1
7372 Xslide_ns, TRUE, FALSE,
7373 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7376 Yslide_ns_blank, FALSE, FALSE,
7377 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7380 Xslide_ew, TRUE, FALSE,
7381 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7384 Yslide_ew_blank, FALSE, FALSE,
7385 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7389 Xwind_n, TRUE, FALSE,
7390 EL_BALLOON_SWITCH_UP, -1, -1
7393 Xwind_e, TRUE, FALSE,
7394 EL_BALLOON_SWITCH_RIGHT, -1, -1
7397 Xwind_s, TRUE, FALSE,
7398 EL_BALLOON_SWITCH_DOWN, -1, -1
7401 Xwind_w, TRUE, FALSE,
7402 EL_BALLOON_SWITCH_LEFT, -1, -1
7405 Xwind_any, TRUE, FALSE,
7406 EL_BALLOON_SWITCH_ANY, -1, -1
7409 Xwind_stop, TRUE, FALSE,
7410 EL_BALLOON_SWITCH_NONE, -1, -1
7415 EL_EM_EXIT_CLOSED, -1, -1
7418 Xexit_1, TRUE, FALSE,
7419 EL_EM_EXIT_OPEN, -1, -1
7422 Xexit_2, FALSE, FALSE,
7423 EL_EM_EXIT_OPEN, -1, -1
7426 Xexit_3, FALSE, FALSE,
7427 EL_EM_EXIT_OPEN, -1, -1
7431 Xpause, FALSE, FALSE,
7436 Xwall_1, TRUE, FALSE,
7440 Xwall_2, TRUE, FALSE,
7441 EL_EMC_WALL_14, -1, -1
7444 Xwall_3, TRUE, FALSE,
7445 EL_EMC_WALL_15, -1, -1
7448 Xwall_4, TRUE, FALSE,
7449 EL_EMC_WALL_16, -1, -1
7453 Xroundwall_1, TRUE, FALSE,
7454 EL_WALL_SLIPPERY, -1, -1
7457 Xroundwall_2, TRUE, FALSE,
7458 EL_EMC_WALL_SLIPPERY_2, -1, -1
7461 Xroundwall_3, TRUE, FALSE,
7462 EL_EMC_WALL_SLIPPERY_3, -1, -1
7465 Xroundwall_4, TRUE, FALSE,
7466 EL_EMC_WALL_SLIPPERY_4, -1, -1
7470 Xsteel_1, TRUE, FALSE,
7471 EL_STEELWALL, -1, -1
7474 Xsteel_2, TRUE, FALSE,
7475 EL_EMC_STEELWALL_2, -1, -1
7478 Xsteel_3, TRUE, FALSE,
7479 EL_EMC_STEELWALL_3, -1, -1
7482 Xsteel_4, TRUE, FALSE,
7483 EL_EMC_STEELWALL_4, -1, -1
7487 Xdecor_1, TRUE, FALSE,
7488 EL_EMC_WALL_8, -1, -1
7491 Xdecor_2, TRUE, FALSE,
7492 EL_EMC_WALL_6, -1, -1
7495 Xdecor_3, TRUE, FALSE,
7496 EL_EMC_WALL_4, -1, -1
7499 Xdecor_4, TRUE, FALSE,
7500 EL_EMC_WALL_7, -1, -1
7503 Xdecor_5, TRUE, FALSE,
7504 EL_EMC_WALL_5, -1, -1
7507 Xdecor_6, TRUE, FALSE,
7508 EL_EMC_WALL_9, -1, -1
7511 Xdecor_7, TRUE, FALSE,
7512 EL_EMC_WALL_10, -1, -1
7515 Xdecor_8, TRUE, FALSE,
7516 EL_EMC_WALL_1, -1, -1
7519 Xdecor_9, TRUE, FALSE,
7520 EL_EMC_WALL_2, -1, -1
7523 Xdecor_10, TRUE, FALSE,
7524 EL_EMC_WALL_3, -1, -1
7527 Xdecor_11, TRUE, FALSE,
7528 EL_EMC_WALL_11, -1, -1
7531 Xdecor_12, TRUE, FALSE,
7532 EL_EMC_WALL_12, -1, -1
7536 Xalpha_0, TRUE, FALSE,
7537 EL_CHAR('0'), -1, -1
7540 Xalpha_1, TRUE, FALSE,
7541 EL_CHAR('1'), -1, -1
7544 Xalpha_2, TRUE, FALSE,
7545 EL_CHAR('2'), -1, -1
7548 Xalpha_3, TRUE, FALSE,
7549 EL_CHAR('3'), -1, -1
7552 Xalpha_4, TRUE, FALSE,
7553 EL_CHAR('4'), -1, -1
7556 Xalpha_5, TRUE, FALSE,
7557 EL_CHAR('5'), -1, -1
7560 Xalpha_6, TRUE, FALSE,
7561 EL_CHAR('6'), -1, -1
7564 Xalpha_7, TRUE, FALSE,
7565 EL_CHAR('7'), -1, -1
7568 Xalpha_8, TRUE, FALSE,
7569 EL_CHAR('8'), -1, -1
7572 Xalpha_9, TRUE, FALSE,
7573 EL_CHAR('9'), -1, -1
7576 Xalpha_excla, TRUE, FALSE,
7577 EL_CHAR('!'), -1, -1
7580 Xalpha_apost, TRUE, FALSE,
7581 EL_CHAR('\''), -1, -1
7584 Xalpha_comma, TRUE, FALSE,
7585 EL_CHAR(','), -1, -1
7588 Xalpha_minus, TRUE, FALSE,
7589 EL_CHAR('-'), -1, -1
7592 Xalpha_perio, TRUE, FALSE,
7593 EL_CHAR('.'), -1, -1
7596 Xalpha_colon, TRUE, FALSE,
7597 EL_CHAR(':'), -1, -1
7600 Xalpha_quest, TRUE, FALSE,
7601 EL_CHAR('?'), -1, -1
7604 Xalpha_a, TRUE, FALSE,
7605 EL_CHAR('A'), -1, -1
7608 Xalpha_b, TRUE, FALSE,
7609 EL_CHAR('B'), -1, -1
7612 Xalpha_c, TRUE, FALSE,
7613 EL_CHAR('C'), -1, -1
7616 Xalpha_d, TRUE, FALSE,
7617 EL_CHAR('D'), -1, -1
7620 Xalpha_e, TRUE, FALSE,
7621 EL_CHAR('E'), -1, -1
7624 Xalpha_f, TRUE, FALSE,
7625 EL_CHAR('F'), -1, -1
7628 Xalpha_g, TRUE, FALSE,
7629 EL_CHAR('G'), -1, -1
7632 Xalpha_h, TRUE, FALSE,
7633 EL_CHAR('H'), -1, -1
7636 Xalpha_i, TRUE, FALSE,
7637 EL_CHAR('I'), -1, -1
7640 Xalpha_j, TRUE, FALSE,
7641 EL_CHAR('J'), -1, -1
7644 Xalpha_k, TRUE, FALSE,
7645 EL_CHAR('K'), -1, -1
7648 Xalpha_l, TRUE, FALSE,
7649 EL_CHAR('L'), -1, -1
7652 Xalpha_m, TRUE, FALSE,
7653 EL_CHAR('M'), -1, -1
7656 Xalpha_n, TRUE, FALSE,
7657 EL_CHAR('N'), -1, -1
7660 Xalpha_o, TRUE, FALSE,
7661 EL_CHAR('O'), -1, -1
7664 Xalpha_p, TRUE, FALSE,
7665 EL_CHAR('P'), -1, -1
7668 Xalpha_q, TRUE, FALSE,
7669 EL_CHAR('Q'), -1, -1
7672 Xalpha_r, TRUE, FALSE,
7673 EL_CHAR('R'), -1, -1
7676 Xalpha_s, TRUE, FALSE,
7677 EL_CHAR('S'), -1, -1
7680 Xalpha_t, TRUE, FALSE,
7681 EL_CHAR('T'), -1, -1
7684 Xalpha_u, TRUE, FALSE,
7685 EL_CHAR('U'), -1, -1
7688 Xalpha_v, TRUE, FALSE,
7689 EL_CHAR('V'), -1, -1
7692 Xalpha_w, TRUE, FALSE,
7693 EL_CHAR('W'), -1, -1
7696 Xalpha_x, TRUE, FALSE,
7697 EL_CHAR('X'), -1, -1
7700 Xalpha_y, TRUE, FALSE,
7701 EL_CHAR('Y'), -1, -1
7704 Xalpha_z, TRUE, FALSE,
7705 EL_CHAR('Z'), -1, -1
7708 Xalpha_arrow_e, TRUE, FALSE,
7709 EL_CHAR('>'), -1, -1
7712 Xalpha_arrow_w, TRUE, FALSE,
7713 EL_CHAR('<'), -1, -1
7716 Xalpha_copyr, TRUE, FALSE,
7717 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7721 Ykey_1_blank, FALSE, FALSE,
7722 EL_EM_KEY_1, ACTION_COLLECTING, -1
7725 Ykey_2_blank, FALSE, FALSE,
7726 EL_EM_KEY_2, ACTION_COLLECTING, -1
7729 Ykey_3_blank, FALSE, FALSE,
7730 EL_EM_KEY_3, ACTION_COLLECTING, -1
7733 Ykey_4_blank, FALSE, FALSE,
7734 EL_EM_KEY_4, ACTION_COLLECTING, -1
7737 Ykey_5_blank, FALSE, FALSE,
7738 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7741 Ykey_6_blank, FALSE, FALSE,
7742 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7745 Ykey_7_blank, FALSE, FALSE,
7746 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7749 Ykey_8_blank, FALSE, FALSE,
7750 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7753 Ylenses_blank, FALSE, FALSE,
7754 EL_EMC_LENSES, ACTION_COLLECTING, -1
7757 Ymagnify_blank, FALSE, FALSE,
7758 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7761 Ygrass_blank, FALSE, FALSE,
7762 EL_EMC_GRASS, ACTION_SNAPPING, -1
7765 Ydirt_blank, FALSE, FALSE,
7766 EL_SAND, ACTION_SNAPPING, -1
7775 static struct Mapping_EM_to_RND_player
7784 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7788 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7792 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7796 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7800 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7804 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7808 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7812 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7816 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7820 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7824 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7828 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7832 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7836 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7840 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7844 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7848 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7852 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7856 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7860 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7864 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7868 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7872 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7876 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7880 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7884 EL_PLAYER_1, ACTION_DEFAULT, -1,
7888 EL_PLAYER_2, ACTION_DEFAULT, -1,
7892 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7896 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7900 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7904 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7908 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7912 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7916 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7920 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7924 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7928 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7932 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7936 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7940 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7944 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7948 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7952 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7956 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7960 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7964 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7968 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7972 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7976 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7980 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7984 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7988 EL_PLAYER_3, ACTION_DEFAULT, -1,
7992 EL_PLAYER_4, ACTION_DEFAULT, -1,
8001 int map_element_RND_to_EM_cave(int element_rnd)
8003 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
8004 static boolean mapping_initialized = FALSE;
8006 if (!mapping_initialized)
8010 // return "Xalpha_quest" for all undefined elements in mapping array
8011 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8012 mapping_RND_to_EM[i] = Xalpha_quest;
8014 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8015 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
8016 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
8017 em_object_mapping_list[i].element_em;
8019 mapping_initialized = TRUE;
8022 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
8024 Warn("invalid RND level element %d", element_rnd);
8029 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
8032 int map_element_EM_to_RND_cave(int element_em_cave)
8034 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8035 static boolean mapping_initialized = FALSE;
8037 if (!mapping_initialized)
8041 // return "EL_UNKNOWN" for all undefined elements in mapping array
8042 for (i = 0; i < GAME_TILE_MAX; i++)
8043 mapping_EM_to_RND[i] = EL_UNKNOWN;
8045 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8046 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8047 em_object_mapping_list[i].element_rnd;
8049 mapping_initialized = TRUE;
8052 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8054 Warn("invalid EM cave element %d", element_em_cave);
8059 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8062 int map_element_EM_to_RND_game(int element_em_game)
8064 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8065 static boolean mapping_initialized = FALSE;
8067 if (!mapping_initialized)
8071 // return "EL_UNKNOWN" for all undefined elements in mapping array
8072 for (i = 0; i < GAME_TILE_MAX; i++)
8073 mapping_EM_to_RND[i] = EL_UNKNOWN;
8075 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8076 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8077 em_object_mapping_list[i].element_rnd;
8079 mapping_initialized = TRUE;
8082 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8084 Warn("invalid EM game element %d", element_em_game);
8089 return mapping_EM_to_RND[element_em_game];
8092 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8094 struct LevelInfo_EM *level_em = level->native_em_level;
8095 struct CAVE *cav = level_em->cav;
8098 for (i = 0; i < GAME_TILE_MAX; i++)
8099 cav->android_array[i] = Cblank;
8101 for (i = 0; i < level->num_android_clone_elements; i++)
8103 int element_rnd = level->android_clone_element[i];
8104 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8106 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8107 if (em_object_mapping_list[j].element_rnd == element_rnd)
8108 cav->android_array[em_object_mapping_list[j].element_em] =
8113 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8115 struct LevelInfo_EM *level_em = level->native_em_level;
8116 struct CAVE *cav = level_em->cav;
8119 level->num_android_clone_elements = 0;
8121 for (i = 0; i < GAME_TILE_MAX; i++)
8123 int element_em_cave = cav->android_array[i];
8125 boolean element_found = FALSE;
8127 if (element_em_cave == Cblank)
8130 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8132 for (j = 0; j < level->num_android_clone_elements; j++)
8133 if (level->android_clone_element[j] == element_rnd)
8134 element_found = TRUE;
8138 level->android_clone_element[level->num_android_clone_elements++] =
8141 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8146 if (level->num_android_clone_elements == 0)
8148 level->num_android_clone_elements = 1;
8149 level->android_clone_element[0] = EL_EMPTY;
8153 int map_direction_RND_to_EM(int direction)
8155 return (direction == MV_UP ? 0 :
8156 direction == MV_RIGHT ? 1 :
8157 direction == MV_DOWN ? 2 :
8158 direction == MV_LEFT ? 3 :
8162 int map_direction_EM_to_RND(int direction)
8164 return (direction == 0 ? MV_UP :
8165 direction == 1 ? MV_RIGHT :
8166 direction == 2 ? MV_DOWN :
8167 direction == 3 ? MV_LEFT :
8171 int map_element_RND_to_SP(int element_rnd)
8173 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8175 if (element_rnd >= EL_SP_START &&
8176 element_rnd <= EL_SP_END)
8177 element_sp = element_rnd - EL_SP_START;
8178 else if (element_rnd == EL_EMPTY_SPACE)
8180 else if (element_rnd == EL_INVISIBLE_WALL)
8186 int map_element_SP_to_RND(int element_sp)
8188 int element_rnd = EL_UNKNOWN;
8190 if (element_sp >= 0x00 &&
8192 element_rnd = EL_SP_START + element_sp;
8193 else if (element_sp == 0x28)
8194 element_rnd = EL_INVISIBLE_WALL;
8199 int map_action_SP_to_RND(int action_sp)
8203 case actActive: return ACTION_ACTIVE;
8204 case actImpact: return ACTION_IMPACT;
8205 case actExploding: return ACTION_EXPLODING;
8206 case actDigging: return ACTION_DIGGING;
8207 case actSnapping: return ACTION_SNAPPING;
8208 case actCollecting: return ACTION_COLLECTING;
8209 case actPassing: return ACTION_PASSING;
8210 case actPushing: return ACTION_PUSHING;
8211 case actDropping: return ACTION_DROPPING;
8213 default: return ACTION_DEFAULT;
8217 int map_element_RND_to_MM(int element_rnd)
8219 return (element_rnd >= EL_MM_START_1 &&
8220 element_rnd <= EL_MM_END_1 ?
8221 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8223 element_rnd >= EL_MM_START_2 &&
8224 element_rnd <= EL_MM_END_2 ?
8225 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8227 element_rnd >= EL_MM_START_3 &&
8228 element_rnd <= EL_MM_END_3 ?
8229 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
8231 element_rnd >= EL_CHAR_START &&
8232 element_rnd <= EL_CHAR_END ?
8233 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8235 element_rnd >= EL_MM_RUNTIME_START &&
8236 element_rnd <= EL_MM_RUNTIME_END ?
8237 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8239 EL_MM_EMPTY_NATIVE);
8242 int map_element_MM_to_RND(int element_mm)
8244 return (element_mm == EL_MM_EMPTY_NATIVE ||
8245 element_mm == EL_DF_EMPTY_NATIVE ?
8248 element_mm >= EL_MM_START_1_NATIVE &&
8249 element_mm <= EL_MM_END_1_NATIVE ?
8250 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8252 element_mm >= EL_MM_START_2_NATIVE &&
8253 element_mm <= EL_MM_END_2_NATIVE ?
8254 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8256 element_mm >= EL_MM_START_3_NATIVE &&
8257 element_mm <= EL_MM_END_3_NATIVE ?
8258 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
8260 element_mm >= EL_MM_CHAR_START_NATIVE &&
8261 element_mm <= EL_MM_CHAR_END_NATIVE ?
8262 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8264 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8265 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8266 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8271 int map_action_MM_to_RND(int action_mm)
8273 // all MM actions are defined to exactly match their RND counterparts
8277 int map_sound_MM_to_RND(int sound_mm)
8281 case SND_MM_GAME_LEVELTIME_CHARGING:
8282 return SND_GAME_LEVELTIME_CHARGING;
8284 case SND_MM_GAME_HEALTH_CHARGING:
8285 return SND_GAME_HEALTH_CHARGING;
8288 return SND_UNDEFINED;
8292 int map_mm_wall_element(int element)
8294 return (element >= EL_MM_STEEL_WALL_START &&
8295 element <= EL_MM_STEEL_WALL_END ?
8298 element >= EL_MM_WOODEN_WALL_START &&
8299 element <= EL_MM_WOODEN_WALL_END ?
8302 element >= EL_MM_ICE_WALL_START &&
8303 element <= EL_MM_ICE_WALL_END ?
8306 element >= EL_MM_AMOEBA_WALL_START &&
8307 element <= EL_MM_AMOEBA_WALL_END ?
8310 element >= EL_DF_STEEL_WALL_START &&
8311 element <= EL_DF_STEEL_WALL_END ?
8314 element >= EL_DF_WOODEN_WALL_START &&
8315 element <= EL_DF_WOODEN_WALL_END ?
8321 int map_mm_wall_element_editor(int element)
8325 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8326 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8327 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8328 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8329 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8330 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8332 default: return element;
8336 int get_next_element(int element)
8340 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8341 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8342 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8343 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8344 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8345 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8346 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8347 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8348 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8349 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8350 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8352 default: return element;
8356 int el2img_mm(int element_mm)
8358 return el2img(map_element_MM_to_RND(element_mm));
8361 int el_act2img_mm(int element_mm, int action)
8363 return el_act2img(map_element_MM_to_RND(element_mm), action);
8366 int el_act_dir2img(int element, int action, int direction)
8368 element = GFX_ELEMENT(element);
8369 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8371 // direction_graphic[][] == graphic[] for undefined direction graphics
8372 return element_info[element].direction_graphic[action][direction];
8375 static int el_act_dir2crm(int element, int action, int direction)
8377 element = GFX_ELEMENT(element);
8378 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8380 // direction_graphic[][] == graphic[] for undefined direction graphics
8381 return element_info[element].direction_crumbled[action][direction];
8384 int el_act2img(int element, int action)
8386 element = GFX_ELEMENT(element);
8388 return element_info[element].graphic[action];
8391 int el_act2crm(int element, int action)
8393 element = GFX_ELEMENT(element);
8395 return element_info[element].crumbled[action];
8398 int el_dir2img(int element, int direction)
8400 element = GFX_ELEMENT(element);
8402 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8405 int el2baseimg(int element)
8407 return element_info[element].graphic[ACTION_DEFAULT];
8410 int el2img(int element)
8412 element = GFX_ELEMENT(element);
8414 return element_info[element].graphic[ACTION_DEFAULT];
8417 int el2edimg(int element)
8419 element = GFX_ELEMENT(element);
8421 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8424 int el2preimg(int element)
8426 element = GFX_ELEMENT(element);
8428 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8431 int el2panelimg(int element)
8433 element = GFX_ELEMENT(element);
8435 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8438 int font2baseimg(int font_nr)
8440 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8443 int getBeltNrFromBeltElement(int element)
8445 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8446 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8447 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8450 int getBeltNrFromBeltActiveElement(int element)
8452 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8453 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8454 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8457 int getBeltNrFromBeltSwitchElement(int element)
8459 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8460 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8461 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8464 int getBeltDirNrFromBeltElement(int element)
8466 static int belt_base_element[4] =
8468 EL_CONVEYOR_BELT_1_LEFT,
8469 EL_CONVEYOR_BELT_2_LEFT,
8470 EL_CONVEYOR_BELT_3_LEFT,
8471 EL_CONVEYOR_BELT_4_LEFT
8474 int belt_nr = getBeltNrFromBeltElement(element);
8475 int belt_dir_nr = element - belt_base_element[belt_nr];
8477 return (belt_dir_nr % 3);
8480 int getBeltDirNrFromBeltSwitchElement(int element)
8482 static int belt_base_element[4] =
8484 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8485 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8486 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8487 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8490 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8491 int belt_dir_nr = element - belt_base_element[belt_nr];
8493 return (belt_dir_nr % 3);
8496 int getBeltDirFromBeltElement(int element)
8498 static int belt_move_dir[3] =
8505 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8507 return belt_move_dir[belt_dir_nr];
8510 int getBeltDirFromBeltSwitchElement(int element)
8512 static int belt_move_dir[3] =
8519 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8521 return belt_move_dir[belt_dir_nr];
8524 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8526 static int belt_base_element[4] =
8528 EL_CONVEYOR_BELT_1_LEFT,
8529 EL_CONVEYOR_BELT_2_LEFT,
8530 EL_CONVEYOR_BELT_3_LEFT,
8531 EL_CONVEYOR_BELT_4_LEFT
8534 return belt_base_element[belt_nr] + belt_dir_nr;
8537 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8539 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8541 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8544 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8546 static int belt_base_element[4] =
8548 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8549 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8550 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8551 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8554 return belt_base_element[belt_nr] + belt_dir_nr;
8557 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8559 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8561 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8564 boolean swapTiles_EM(boolean is_pre_emc_cave)
8566 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8569 boolean getTeamMode_EM(void)
8571 return game.team_mode || network_playing;
8574 boolean isActivePlayer_EM(int player_nr)
8576 return stored_player[player_nr].active;
8579 unsigned int InitRND(int seed)
8581 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8582 return InitEngineRandom_EM(seed);
8583 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8584 return InitEngineRandom_SP(seed);
8585 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8586 return InitEngineRandom_MM(seed);
8588 return InitEngineRandom_RND(seed);
8591 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8592 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8594 static int get_effective_element_EM(int tile, int frame_em)
8596 int element = object_mapping[tile].element_rnd;
8597 int action = object_mapping[tile].action;
8598 boolean is_backside = object_mapping[tile].is_backside;
8599 boolean action_removing = (action == ACTION_DIGGING ||
8600 action == ACTION_SNAPPING ||
8601 action == ACTION_COLLECTING);
8609 return (frame_em > 5 ? EL_EMPTY : element);
8615 else // frame_em == 7
8626 case Ydiamond_stone:
8630 case Xdrip_stretchB:
8646 case Ymagnify_blank:
8649 case Xsand_stonein_1:
8650 case Xsand_stonein_2:
8651 case Xsand_stonein_3:
8652 case Xsand_stonein_4:
8656 return (is_backside || action_removing ? EL_EMPTY : element);
8661 static boolean check_linear_animation_EM(int tile)
8665 case Xsand_stonesand_1:
8666 case Xsand_stonesand_quickout_1:
8667 case Xsand_sandstone_1:
8668 case Xsand_stonein_1:
8669 case Xsand_stoneout_1:
8697 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8698 boolean has_crumbled_graphics,
8699 int crumbled, int sync_frame)
8701 // if element can be crumbled, but certain action graphics are just empty
8702 // space (like instantly snapping sand to empty space in 1 frame), do not
8703 // treat these empty space graphics as crumbled graphics in EMC engine
8704 if (crumbled == IMG_EMPTY_SPACE)
8705 has_crumbled_graphics = FALSE;
8707 if (has_crumbled_graphics)
8709 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8710 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8711 g_crumbled->anim_delay,
8712 g_crumbled->anim_mode,
8713 g_crumbled->anim_start_frame,
8716 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8717 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8719 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8720 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8722 g_em->has_crumbled_graphics = TRUE;
8726 g_em->crumbled_bitmap = NULL;
8727 g_em->crumbled_src_x = 0;
8728 g_em->crumbled_src_y = 0;
8729 g_em->crumbled_border_size = 0;
8730 g_em->crumbled_tile_size = 0;
8732 g_em->has_crumbled_graphics = FALSE;
8737 void ResetGfxAnimation_EM(int x, int y, int tile)
8743 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8744 int tile, int frame_em, int x, int y)
8746 int action = object_mapping[tile].action;
8747 int direction = object_mapping[tile].direction;
8748 int effective_element = get_effective_element_EM(tile, frame_em);
8749 int graphic = (direction == MV_NONE ?
8750 el_act2img(effective_element, action) :
8751 el_act_dir2img(effective_element, action, direction));
8752 struct GraphicInfo *g = &graphic_info[graphic];
8754 boolean action_removing = (action == ACTION_DIGGING ||
8755 action == ACTION_SNAPPING ||
8756 action == ACTION_COLLECTING);
8757 boolean action_moving = (action == ACTION_FALLING ||
8758 action == ACTION_MOVING ||
8759 action == ACTION_PUSHING ||
8760 action == ACTION_EATING ||
8761 action == ACTION_FILLING ||
8762 action == ACTION_EMPTYING);
8763 boolean action_falling = (action == ACTION_FALLING ||
8764 action == ACTION_FILLING ||
8765 action == ACTION_EMPTYING);
8767 // special case: graphic uses "2nd movement tile" and has defined
8768 // 7 frames for movement animation (or less) => use default graphic
8769 // for last (8th) frame which ends the movement animation
8770 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8772 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8773 graphic = (direction == MV_NONE ?
8774 el_act2img(effective_element, action) :
8775 el_act_dir2img(effective_element, action, direction));
8777 g = &graphic_info[graphic];
8780 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8784 else if (action_moving)
8786 boolean is_backside = object_mapping[tile].is_backside;
8790 int direction = object_mapping[tile].direction;
8791 int move_dir = (action_falling ? MV_DOWN : direction);
8796 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8797 if (g->double_movement && frame_em == 0)
8801 if (move_dir == MV_LEFT)
8802 GfxFrame[x - 1][y] = GfxFrame[x][y];
8803 else if (move_dir == MV_RIGHT)
8804 GfxFrame[x + 1][y] = GfxFrame[x][y];
8805 else if (move_dir == MV_UP)
8806 GfxFrame[x][y - 1] = GfxFrame[x][y];
8807 else if (move_dir == MV_DOWN)
8808 GfxFrame[x][y + 1] = GfxFrame[x][y];
8815 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8816 if (tile == Xsand_stonesand_quickout_1 ||
8817 tile == Xsand_stonesand_quickout_2)
8821 if (graphic_info[graphic].anim_global_sync)
8822 sync_frame = FrameCounter;
8823 else if (graphic_info[graphic].anim_global_anim_sync)
8824 sync_frame = getGlobalAnimSyncFrame();
8825 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8826 sync_frame = GfxFrame[x][y];
8828 sync_frame = 0; // playfield border (pseudo steel)
8830 SetRandomAnimationValue(x, y);
8832 int frame = getAnimationFrame(g->anim_frames,
8835 g->anim_start_frame,
8838 g_em->unique_identifier =
8839 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8842 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8843 int tile, int frame_em, int x, int y)
8845 int action = object_mapping[tile].action;
8846 int direction = object_mapping[tile].direction;
8847 boolean is_backside = object_mapping[tile].is_backside;
8848 int effective_element = get_effective_element_EM(tile, frame_em);
8849 int effective_action = action;
8850 int graphic = (direction == MV_NONE ?
8851 el_act2img(effective_element, effective_action) :
8852 el_act_dir2img(effective_element, effective_action,
8854 int crumbled = (direction == MV_NONE ?
8855 el_act2crm(effective_element, effective_action) :
8856 el_act_dir2crm(effective_element, effective_action,
8858 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8859 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8860 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8861 struct GraphicInfo *g = &graphic_info[graphic];
8864 // special case: graphic uses "2nd movement tile" and has defined
8865 // 7 frames for movement animation (or less) => use default graphic
8866 // for last (8th) frame which ends the movement animation
8867 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8869 effective_action = ACTION_DEFAULT;
8870 graphic = (direction == MV_NONE ?
8871 el_act2img(effective_element, effective_action) :
8872 el_act_dir2img(effective_element, effective_action,
8874 crumbled = (direction == MV_NONE ?
8875 el_act2crm(effective_element, effective_action) :
8876 el_act_dir2crm(effective_element, effective_action,
8879 g = &graphic_info[graphic];
8882 if (graphic_info[graphic].anim_global_sync)
8883 sync_frame = FrameCounter;
8884 else if (graphic_info[graphic].anim_global_anim_sync)
8885 sync_frame = getGlobalAnimSyncFrame();
8886 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8887 sync_frame = GfxFrame[x][y];
8889 sync_frame = 0; // playfield border (pseudo steel)
8891 SetRandomAnimationValue(x, y);
8893 int frame = getAnimationFrame(g->anim_frames,
8896 g->anim_start_frame,
8899 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8900 g->double_movement && is_backside);
8902 // (updating the "crumbled" graphic definitions is probably not really needed,
8903 // as animations for crumbled graphics can't be longer than one EMC cycle)
8904 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8908 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8909 int player_nr, int anim, int frame_em)
8911 int element = player_mapping[player_nr][anim].element_rnd;
8912 int action = player_mapping[player_nr][anim].action;
8913 int direction = player_mapping[player_nr][anim].direction;
8914 int graphic = (direction == MV_NONE ?
8915 el_act2img(element, action) :
8916 el_act_dir2img(element, action, direction));
8917 struct GraphicInfo *g = &graphic_info[graphic];
8920 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8922 stored_player[player_nr].StepFrame = frame_em;
8924 sync_frame = stored_player[player_nr].Frame;
8926 int frame = getAnimationFrame(g->anim_frames,
8929 g->anim_start_frame,
8932 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8933 &g_em->src_x, &g_em->src_y, FALSE);
8936 void InitGraphicInfo_EM(void)
8940 // always start with reliable default values
8941 for (i = 0; i < GAME_TILE_MAX; i++)
8943 object_mapping[i].element_rnd = EL_UNKNOWN;
8944 object_mapping[i].is_backside = FALSE;
8945 object_mapping[i].action = ACTION_DEFAULT;
8946 object_mapping[i].direction = MV_NONE;
8949 // always start with reliable default values
8950 for (p = 0; p < MAX_PLAYERS; p++)
8952 for (i = 0; i < PLY_MAX; i++)
8954 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8955 player_mapping[p][i].action = ACTION_DEFAULT;
8956 player_mapping[p][i].direction = MV_NONE;
8960 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8962 int e = em_object_mapping_list[i].element_em;
8964 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8965 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8967 if (em_object_mapping_list[i].action != -1)
8968 object_mapping[e].action = em_object_mapping_list[i].action;
8970 if (em_object_mapping_list[i].direction != -1)
8971 object_mapping[e].direction =
8972 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8975 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8977 int a = em_player_mapping_list[i].action_em;
8978 int p = em_player_mapping_list[i].player_nr;
8980 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8982 if (em_player_mapping_list[i].action != -1)
8983 player_mapping[p][a].action = em_player_mapping_list[i].action;
8985 if (em_player_mapping_list[i].direction != -1)
8986 player_mapping[p][a].direction =
8987 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8990 for (i = 0; i < GAME_TILE_MAX; i++)
8992 int element = object_mapping[i].element_rnd;
8993 int action = object_mapping[i].action;
8994 int direction = object_mapping[i].direction;
8995 boolean is_backside = object_mapping[i].is_backside;
8996 boolean action_exploding = ((action == ACTION_EXPLODING ||
8997 action == ACTION_SMASHED_BY_ROCK ||
8998 action == ACTION_SMASHED_BY_SPRING) &&
8999 element != EL_DIAMOND);
9000 boolean action_active = (action == ACTION_ACTIVE);
9001 boolean action_other = (action == ACTION_OTHER);
9003 for (j = 0; j < 8; j++)
9005 int effective_element = get_effective_element_EM(i, j);
9006 int effective_action = (j < 7 ? action :
9007 i == Xdrip_stretch ? action :
9008 i == Xdrip_stretchB ? action :
9009 i == Ydrip_1_s ? action :
9010 i == Ydrip_1_sB ? action :
9011 i == Yball_1 ? action :
9012 i == Xball_2 ? action :
9013 i == Yball_2 ? action :
9014 i == Yball_blank ? action :
9015 i == Ykey_1_blank ? action :
9016 i == Ykey_2_blank ? action :
9017 i == Ykey_3_blank ? action :
9018 i == Ykey_4_blank ? action :
9019 i == Ykey_5_blank ? action :
9020 i == Ykey_6_blank ? action :
9021 i == Ykey_7_blank ? action :
9022 i == Ykey_8_blank ? action :
9023 i == Ylenses_blank ? action :
9024 i == Ymagnify_blank ? action :
9025 i == Ygrass_blank ? action :
9026 i == Ydirt_blank ? action :
9027 i == Xsand_stonein_1 ? action :
9028 i == Xsand_stonein_2 ? action :
9029 i == Xsand_stonein_3 ? action :
9030 i == Xsand_stonein_4 ? action :
9031 i == Xsand_stoneout_1 ? action :
9032 i == Xsand_stoneout_2 ? action :
9033 i == Xboom_android ? ACTION_EXPLODING :
9034 action_exploding ? ACTION_EXPLODING :
9035 action_active ? action :
9036 action_other ? action :
9038 int graphic = (el_act_dir2img(effective_element, effective_action,
9040 int crumbled = (el_act_dir2crm(effective_element, effective_action,
9042 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
9043 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
9044 boolean has_action_graphics = (graphic != base_graphic);
9045 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9046 struct GraphicInfo *g = &graphic_info[graphic];
9047 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9050 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9051 boolean special_animation = (action != ACTION_DEFAULT &&
9052 g->anim_frames == 3 &&
9053 g->anim_delay == 2 &&
9054 g->anim_mode & ANIM_LINEAR);
9055 int sync_frame = (i == Xdrip_stretch ? 7 :
9056 i == Xdrip_stretchB ? 7 :
9057 i == Ydrip_2_s ? j + 8 :
9058 i == Ydrip_2_sB ? j + 8 :
9067 i == Xfake_acid_1 ? 0 :
9068 i == Xfake_acid_2 ? 10 :
9069 i == Xfake_acid_3 ? 20 :
9070 i == Xfake_acid_4 ? 30 :
9071 i == Xfake_acid_5 ? 40 :
9072 i == Xfake_acid_6 ? 50 :
9073 i == Xfake_acid_7 ? 60 :
9074 i == Xfake_acid_8 ? 70 :
9075 i == Xfake_acid_1_player ? 0 :
9076 i == Xfake_acid_2_player ? 10 :
9077 i == Xfake_acid_3_player ? 20 :
9078 i == Xfake_acid_4_player ? 30 :
9079 i == Xfake_acid_5_player ? 40 :
9080 i == Xfake_acid_6_player ? 50 :
9081 i == Xfake_acid_7_player ? 60 :
9082 i == Xfake_acid_8_player ? 70 :
9084 i == Yball_2 ? j + 8 :
9085 i == Yball_blank ? j + 1 :
9086 i == Ykey_1_blank ? j + 1 :
9087 i == Ykey_2_blank ? j + 1 :
9088 i == Ykey_3_blank ? j + 1 :
9089 i == Ykey_4_blank ? j + 1 :
9090 i == Ykey_5_blank ? j + 1 :
9091 i == Ykey_6_blank ? j + 1 :
9092 i == Ykey_7_blank ? j + 1 :
9093 i == Ykey_8_blank ? j + 1 :
9094 i == Ylenses_blank ? j + 1 :
9095 i == Ymagnify_blank ? j + 1 :
9096 i == Ygrass_blank ? j + 1 :
9097 i == Ydirt_blank ? j + 1 :
9098 i == Xamoeba_1 ? 0 :
9099 i == Xamoeba_2 ? 1 :
9100 i == Xamoeba_3 ? 2 :
9101 i == Xamoeba_4 ? 3 :
9102 i == Xamoeba_5 ? 0 :
9103 i == Xamoeba_6 ? 1 :
9104 i == Xamoeba_7 ? 2 :
9105 i == Xamoeba_8 ? 3 :
9106 i == Xexit_2 ? j + 8 :
9107 i == Xexit_3 ? j + 16 :
9108 i == Xdynamite_1 ? 0 :
9109 i == Xdynamite_2 ? 8 :
9110 i == Xdynamite_3 ? 16 :
9111 i == Xdynamite_4 ? 24 :
9112 i == Xsand_stonein_1 ? j + 1 :
9113 i == Xsand_stonein_2 ? j + 9 :
9114 i == Xsand_stonein_3 ? j + 17 :
9115 i == Xsand_stonein_4 ? j + 25 :
9116 i == Xsand_stoneout_1 && j == 0 ? 0 :
9117 i == Xsand_stoneout_1 && j == 1 ? 0 :
9118 i == Xsand_stoneout_1 && j == 2 ? 1 :
9119 i == Xsand_stoneout_1 && j == 3 ? 2 :
9120 i == Xsand_stoneout_1 && j == 4 ? 2 :
9121 i == Xsand_stoneout_1 && j == 5 ? 3 :
9122 i == Xsand_stoneout_1 && j == 6 ? 4 :
9123 i == Xsand_stoneout_1 && j == 7 ? 4 :
9124 i == Xsand_stoneout_2 && j == 0 ? 5 :
9125 i == Xsand_stoneout_2 && j == 1 ? 6 :
9126 i == Xsand_stoneout_2 && j == 2 ? 7 :
9127 i == Xsand_stoneout_2 && j == 3 ? 8 :
9128 i == Xsand_stoneout_2 && j == 4 ? 9 :
9129 i == Xsand_stoneout_2 && j == 5 ? 11 :
9130 i == Xsand_stoneout_2 && j == 6 ? 13 :
9131 i == Xsand_stoneout_2 && j == 7 ? 15 :
9132 i == Xboom_bug && j == 1 ? 2 :
9133 i == Xboom_bug && j == 2 ? 2 :
9134 i == Xboom_bug && j == 3 ? 4 :
9135 i == Xboom_bug && j == 4 ? 4 :
9136 i == Xboom_bug && j == 5 ? 2 :
9137 i == Xboom_bug && j == 6 ? 2 :
9138 i == Xboom_bug && j == 7 ? 0 :
9139 i == Xboom_tank && j == 1 ? 2 :
9140 i == Xboom_tank && j == 2 ? 2 :
9141 i == Xboom_tank && j == 3 ? 4 :
9142 i == Xboom_tank && j == 4 ? 4 :
9143 i == Xboom_tank && j == 5 ? 2 :
9144 i == Xboom_tank && j == 6 ? 2 :
9145 i == Xboom_tank && j == 7 ? 0 :
9146 i == Xboom_android && j == 7 ? 6 :
9147 i == Xboom_1 && j == 1 ? 2 :
9148 i == Xboom_1 && j == 2 ? 2 :
9149 i == Xboom_1 && j == 3 ? 4 :
9150 i == Xboom_1 && j == 4 ? 4 :
9151 i == Xboom_1 && j == 5 ? 6 :
9152 i == Xboom_1 && j == 6 ? 6 :
9153 i == Xboom_1 && j == 7 ? 8 :
9154 i == Xboom_2 && j == 0 ? 8 :
9155 i == Xboom_2 && j == 1 ? 8 :
9156 i == Xboom_2 && j == 2 ? 10 :
9157 i == Xboom_2 && j == 3 ? 10 :
9158 i == Xboom_2 && j == 4 ? 10 :
9159 i == Xboom_2 && j == 5 ? 12 :
9160 i == Xboom_2 && j == 6 ? 12 :
9161 i == Xboom_2 && j == 7 ? 12 :
9162 special_animation && j == 4 ? 3 :
9163 effective_action != action ? 0 :
9165 int frame = getAnimationFrame(g->anim_frames,
9168 g->anim_start_frame,
9171 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9172 g->double_movement && is_backside);
9174 g_em->bitmap = src_bitmap;
9175 g_em->src_x = src_x;
9176 g_em->src_y = src_y;
9177 g_em->src_offset_x = 0;
9178 g_em->src_offset_y = 0;
9179 g_em->dst_offset_x = 0;
9180 g_em->dst_offset_y = 0;
9181 g_em->width = TILEX;
9182 g_em->height = TILEY;
9184 g_em->preserve_background = FALSE;
9186 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9189 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9190 effective_action == ACTION_MOVING ||
9191 effective_action == ACTION_PUSHING ||
9192 effective_action == ACTION_EATING)) ||
9193 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9194 effective_action == ACTION_EMPTYING)))
9197 (effective_action == ACTION_FALLING ||
9198 effective_action == ACTION_FILLING ||
9199 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9200 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9201 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9202 int num_steps = (i == Ydrip_1_s ? 16 :
9203 i == Ydrip_1_sB ? 16 :
9204 i == Ydrip_2_s ? 16 :
9205 i == Ydrip_2_sB ? 16 :
9206 i == Xsand_stonein_1 ? 32 :
9207 i == Xsand_stonein_2 ? 32 :
9208 i == Xsand_stonein_3 ? 32 :
9209 i == Xsand_stonein_4 ? 32 :
9210 i == Xsand_stoneout_1 ? 16 :
9211 i == Xsand_stoneout_2 ? 16 : 8);
9212 int cx = ABS(dx) * (TILEX / num_steps);
9213 int cy = ABS(dy) * (TILEY / num_steps);
9214 int step_frame = (i == Ydrip_2_s ? j + 8 :
9215 i == Ydrip_2_sB ? j + 8 :
9216 i == Xsand_stonein_2 ? j + 8 :
9217 i == Xsand_stonein_3 ? j + 16 :
9218 i == Xsand_stonein_4 ? j + 24 :
9219 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9220 int step = (is_backside ? step_frame : num_steps - step_frame);
9222 if (is_backside) // tile where movement starts
9224 if (dx < 0 || dy < 0)
9226 g_em->src_offset_x = cx * step;
9227 g_em->src_offset_y = cy * step;
9231 g_em->dst_offset_x = cx * step;
9232 g_em->dst_offset_y = cy * step;
9235 else // tile where movement ends
9237 if (dx < 0 || dy < 0)
9239 g_em->dst_offset_x = cx * step;
9240 g_em->dst_offset_y = cy * step;
9244 g_em->src_offset_x = cx * step;
9245 g_em->src_offset_y = cy * step;
9249 g_em->width = TILEX - cx * step;
9250 g_em->height = TILEY - cy * step;
9253 // create unique graphic identifier to decide if tile must be redrawn
9254 /* bit 31 - 16 (16 bit): EM style graphic
9255 bit 15 - 12 ( 4 bit): EM style frame
9256 bit 11 - 6 ( 6 bit): graphic width
9257 bit 5 - 0 ( 6 bit): graphic height */
9258 g_em->unique_identifier =
9259 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9263 for (i = 0; i < GAME_TILE_MAX; i++)
9265 for (j = 0; j < 8; j++)
9267 int element = object_mapping[i].element_rnd;
9268 int action = object_mapping[i].action;
9269 int direction = object_mapping[i].direction;
9270 boolean is_backside = object_mapping[i].is_backside;
9271 int graphic_action = el_act_dir2img(element, action, direction);
9272 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9274 if ((action == ACTION_SMASHED_BY_ROCK ||
9275 action == ACTION_SMASHED_BY_SPRING ||
9276 action == ACTION_EATING) &&
9277 graphic_action == graphic_default)
9279 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9280 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9281 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9282 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9285 // no separate animation for "smashed by rock" -- use rock instead
9286 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9287 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9289 g_em->bitmap = g_xx->bitmap;
9290 g_em->src_x = g_xx->src_x;
9291 g_em->src_y = g_xx->src_y;
9292 g_em->src_offset_x = g_xx->src_offset_x;
9293 g_em->src_offset_y = g_xx->src_offset_y;
9294 g_em->dst_offset_x = g_xx->dst_offset_x;
9295 g_em->dst_offset_y = g_xx->dst_offset_y;
9296 g_em->width = g_xx->width;
9297 g_em->height = g_xx->height;
9298 g_em->unique_identifier = g_xx->unique_identifier;
9301 g_em->preserve_background = TRUE;
9306 for (p = 0; p < MAX_PLAYERS; p++)
9308 for (i = 0; i < PLY_MAX; i++)
9310 int element = player_mapping[p][i].element_rnd;
9311 int action = player_mapping[p][i].action;
9312 int direction = player_mapping[p][i].direction;
9314 for (j = 0; j < 8; j++)
9316 int effective_element = element;
9317 int effective_action = action;
9318 int graphic = (direction == MV_NONE ?
9319 el_act2img(effective_element, effective_action) :
9320 el_act_dir2img(effective_element, effective_action,
9322 struct GraphicInfo *g = &graphic_info[graphic];
9323 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9327 int frame = getAnimationFrame(g->anim_frames,
9330 g->anim_start_frame,
9333 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9335 g_em->bitmap = src_bitmap;
9336 g_em->src_x = src_x;
9337 g_em->src_y = src_y;
9338 g_em->src_offset_x = 0;
9339 g_em->src_offset_y = 0;
9340 g_em->dst_offset_x = 0;
9341 g_em->dst_offset_y = 0;
9342 g_em->width = TILEX;
9343 g_em->height = TILEY;
9349 static void CheckSaveEngineSnapshot_EM(int frame,
9350 boolean any_player_moving,
9351 boolean any_player_snapping,
9352 boolean any_player_dropping)
9354 if (frame == 7 && !any_player_dropping)
9356 if (!local_player->was_waiting)
9358 if (!CheckSaveEngineSnapshotToList())
9361 local_player->was_waiting = TRUE;
9364 else if (any_player_moving || any_player_snapping || any_player_dropping)
9366 local_player->was_waiting = FALSE;
9370 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9371 boolean murphy_is_dropping)
9373 if (murphy_is_waiting)
9375 if (!local_player->was_waiting)
9377 if (!CheckSaveEngineSnapshotToList())
9380 local_player->was_waiting = TRUE;
9385 local_player->was_waiting = FALSE;
9389 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9390 boolean button_released)
9392 if (button_released)
9394 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9395 CheckSaveEngineSnapshotToList();
9397 else if (element_clicked)
9399 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9400 CheckSaveEngineSnapshotToList();
9402 game.snapshot.changed_action = TRUE;
9406 boolean CheckSingleStepMode_EM(int frame,
9407 boolean any_player_moving,
9408 boolean any_player_snapping,
9409 boolean any_player_dropping)
9411 if (tape.single_step && tape.recording && !tape.pausing)
9412 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9413 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9415 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9416 any_player_snapping, any_player_dropping);
9418 return tape.pausing;
9421 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9422 boolean murphy_is_dropping)
9424 boolean murphy_starts_dropping = FALSE;
9427 for (i = 0; i < MAX_PLAYERS; i++)
9428 if (stored_player[i].force_dropping)
9429 murphy_starts_dropping = TRUE;
9431 if (tape.single_step && tape.recording && !tape.pausing)
9432 if (murphy_is_waiting && !murphy_starts_dropping)
9433 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9435 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9438 void CheckSingleStepMode_MM(boolean element_clicked,
9439 boolean button_released)
9441 if (tape.single_step && tape.recording && !tape.pausing)
9442 if (button_released)
9443 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9445 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9448 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9449 int graphic, int sync_frame)
9451 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9453 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9456 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9458 return (IS_NEXT_FRAME(sync_frame, graphic));
9461 int getGraphicInfo_Delay(int graphic)
9463 return graphic_info[graphic].anim_delay;
9466 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9468 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9471 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9477 void PlayMenuSoundExt(int sound)
9479 if (sound == SND_UNDEFINED)
9482 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9483 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9486 if (IS_LOOP_SOUND(sound))
9487 PlaySoundLoop(sound);
9492 void PlayMenuSound(void)
9494 PlayMenuSoundExt(menu.sound[game_status]);
9497 void PlayMenuSoundStereo(int sound, int stereo_position)
9499 if (sound == SND_UNDEFINED)
9502 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9503 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9506 if (IS_LOOP_SOUND(sound))
9507 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9509 PlaySoundStereo(sound, stereo_position);
9512 void PlayMenuSoundIfLoopExt(int sound)
9514 if (sound == SND_UNDEFINED)
9517 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9518 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9521 if (IS_LOOP_SOUND(sound))
9522 PlaySoundLoop(sound);
9525 void PlayMenuSoundIfLoop(void)
9527 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9530 void PlayMenuMusicExt(int music)
9532 if (music == MUS_UNDEFINED)
9535 if (!setup.sound_music)
9538 if (IS_LOOP_MUSIC(music))
9539 PlayMusicLoop(music);
9544 void PlayMenuMusic(void)
9546 char *curr_music = getCurrentlyPlayingMusicFilename();
9547 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9549 if (!strEqual(curr_music, next_music))
9550 PlayMenuMusicExt(menu.music[game_status]);
9553 void PlayMenuSoundsAndMusic(void)
9559 static void FadeMenuSounds(void)
9564 static void FadeMenuMusic(void)
9566 char *curr_music = getCurrentlyPlayingMusicFilename();
9567 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9569 if (!strEqual(curr_music, next_music))
9573 void FadeMenuSoundsAndMusic(void)
9579 void PlaySoundActivating(void)
9582 PlaySound(SND_MENU_ITEM_ACTIVATING);
9586 void PlaySoundSelecting(void)
9589 PlaySound(SND_MENU_ITEM_SELECTING);
9593 void ToggleFullscreenIfNeeded(void)
9595 // if setup and video fullscreen state are already matching, nothing do do
9596 if (setup.fullscreen == video.fullscreen_enabled ||
9597 !video.fullscreen_available)
9600 SDLSetWindowFullscreen(setup.fullscreen);
9602 // set setup value according to successfully changed fullscreen mode
9603 setup.fullscreen = video.fullscreen_enabled;
9606 void ChangeWindowScalingIfNeeded(void)
9608 // if setup and video window scaling are already matching, nothing do do
9609 if (setup.window_scaling_percent == video.window_scaling_percent ||
9610 video.fullscreen_enabled)
9613 SDLSetWindowScaling(setup.window_scaling_percent);
9615 // set setup value according to successfully changed window scaling
9616 setup.window_scaling_percent = video.window_scaling_percent;
9619 void ChangeVsyncModeIfNeeded(void)
9621 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9622 int video_vsync_mode = video.vsync_mode;
9624 // if setup and video vsync mode are already matching, nothing do do
9625 if (setup_vsync_mode == video_vsync_mode)
9628 // if renderer is using OpenGL, vsync mode can directly be changed
9629 SDLSetScreenVsyncMode(setup.vsync_mode);
9631 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9632 if (video.vsync_mode == video_vsync_mode)
9634 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9636 // save backbuffer content which gets lost when re-creating screen
9637 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9639 // force re-creating screen and renderer to set new vsync mode
9640 video.fullscreen_enabled = !setup.fullscreen;
9642 // when creating new renderer, destroy textures linked to old renderer
9643 FreeAllImageTextures(); // needs old renderer to free the textures
9645 // re-create screen and renderer (including change of vsync mode)
9646 ChangeVideoModeIfNeeded(setup.fullscreen);
9648 // set setup value according to successfully changed fullscreen mode
9649 setup.fullscreen = video.fullscreen_enabled;
9651 // restore backbuffer content from temporary backbuffer backup bitmap
9652 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9653 FreeBitmap(tmp_backbuffer);
9655 // update visible window/screen
9656 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9658 // when changing vsync mode, re-create textures for new renderer
9659 InitImageTextures();
9662 // set setup value according to successfully changed vsync mode
9663 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9666 static void JoinRectangles(int *x, int *y, int *width, int *height,
9667 int x2, int y2, int width2, int height2)
9669 // do not join with "off-screen" rectangle
9670 if (x2 == -1 || y2 == -1)
9675 *width = MAX(*width, width2);
9676 *height = MAX(*height, height2);
9679 void SetAnimStatus(int anim_status_new)
9681 if (anim_status_new == GAME_MODE_MAIN)
9682 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9683 else if (anim_status_new == GAME_MODE_NAMES)
9684 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9685 else if (anim_status_new == GAME_MODE_SCORES)
9686 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9688 global.anim_status_next = anim_status_new;
9690 // directly set screen modes that are entered without fading
9691 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9692 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9693 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9694 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9695 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9696 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9697 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9698 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9699 global.anim_status = global.anim_status_next;
9702 void SetGameStatus(int game_status_new)
9704 if (game_status_new != game_status)
9705 game_status_last_screen = game_status;
9707 game_status = game_status_new;
9709 SetAnimStatus(game_status_new);
9712 void SetFontStatus(int game_status_new)
9714 static int last_game_status = -1;
9716 if (game_status_new != -1)
9718 // set game status for font use after storing last game status
9719 last_game_status = game_status;
9720 game_status = game_status_new;
9724 // reset game status after font use from last stored game status
9725 game_status = last_game_status;
9729 void ResetFontStatus(void)
9734 void SetLevelSetInfo(char *identifier, int level_nr)
9736 setString(&levelset.identifier, identifier);
9738 levelset.level_nr = level_nr;
9741 boolean CheckIfAllViewportsHaveChanged(void)
9743 // if game status has not changed, viewports have not changed either
9744 if (game_status == game_status_last)
9747 // check if all viewports have changed with current game status
9749 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9750 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9751 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9752 int new_real_sx = vp_playfield->x;
9753 int new_real_sy = vp_playfield->y;
9754 int new_full_sxsize = vp_playfield->width;
9755 int new_full_sysize = vp_playfield->height;
9756 int new_dx = vp_door_1->x;
9757 int new_dy = vp_door_1->y;
9758 int new_dxsize = vp_door_1->width;
9759 int new_dysize = vp_door_1->height;
9760 int new_vx = vp_door_2->x;
9761 int new_vy = vp_door_2->y;
9762 int new_vxsize = vp_door_2->width;
9763 int new_vysize = vp_door_2->height;
9765 boolean playfield_viewport_has_changed =
9766 (new_real_sx != REAL_SX ||
9767 new_real_sy != REAL_SY ||
9768 new_full_sxsize != FULL_SXSIZE ||
9769 new_full_sysize != FULL_SYSIZE);
9771 boolean door_1_viewport_has_changed =
9774 new_dxsize != DXSIZE ||
9775 new_dysize != DYSIZE);
9777 boolean door_2_viewport_has_changed =
9780 new_vxsize != VXSIZE ||
9781 new_vysize != VYSIZE ||
9782 game_status_last == GAME_MODE_EDITOR);
9784 return (playfield_viewport_has_changed &&
9785 door_1_viewport_has_changed &&
9786 door_2_viewport_has_changed);
9789 boolean CheckFadeAll(void)
9791 return (CheckIfGlobalBorderHasChanged() ||
9792 CheckIfAllViewportsHaveChanged());
9795 void ChangeViewportPropertiesIfNeeded(void)
9797 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9798 FALSE : setup.small_game_graphics);
9799 int gfx_game_mode = getGlobalGameStatus(game_status);
9800 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9802 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9803 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9804 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9805 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9806 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9807 int new_win_xsize = vp_window->width;
9808 int new_win_ysize = vp_window->height;
9809 int border_left = vp_playfield->border_left;
9810 int border_right = vp_playfield->border_right;
9811 int border_top = vp_playfield->border_top;
9812 int border_bottom = vp_playfield->border_bottom;
9813 int new_sx = vp_playfield->x + border_left;
9814 int new_sy = vp_playfield->y + border_top;
9815 int new_sxsize = vp_playfield->width - border_left - border_right;
9816 int new_sysize = vp_playfield->height - border_top - border_bottom;
9817 int new_real_sx = vp_playfield->x;
9818 int new_real_sy = vp_playfield->y;
9819 int new_full_sxsize = vp_playfield->width;
9820 int new_full_sysize = vp_playfield->height;
9821 int new_dx = vp_door_1->x;
9822 int new_dy = vp_door_1->y;
9823 int new_dxsize = vp_door_1->width;
9824 int new_dysize = vp_door_1->height;
9825 int new_vx = vp_door_2->x;
9826 int new_vy = vp_door_2->y;
9827 int new_vxsize = vp_door_2->width;
9828 int new_vysize = vp_door_2->height;
9829 int new_ex = vp_door_3->x;
9830 int new_ey = vp_door_3->y;
9831 int new_exsize = vp_door_3->width;
9832 int new_eysize = vp_door_3->height;
9833 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9834 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9835 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9836 int new_scr_fieldx = new_sxsize / tilesize;
9837 int new_scr_fieldy = new_sysize / tilesize;
9838 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9839 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9840 boolean init_gfx_buffers = FALSE;
9841 boolean init_video_buffer = FALSE;
9842 boolean init_gadgets_and_anims = FALSE;
9843 boolean init_em_graphics = FALSE;
9845 if (new_win_xsize != WIN_XSIZE ||
9846 new_win_ysize != WIN_YSIZE)
9848 WIN_XSIZE = new_win_xsize;
9849 WIN_YSIZE = new_win_ysize;
9851 init_video_buffer = TRUE;
9852 init_gfx_buffers = TRUE;
9853 init_gadgets_and_anims = TRUE;
9855 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9858 if (new_scr_fieldx != SCR_FIELDX ||
9859 new_scr_fieldy != SCR_FIELDY)
9861 // this always toggles between MAIN and GAME when using small tile size
9863 SCR_FIELDX = new_scr_fieldx;
9864 SCR_FIELDY = new_scr_fieldy;
9866 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9877 new_sxsize != SXSIZE ||
9878 new_sysize != SYSIZE ||
9879 new_dxsize != DXSIZE ||
9880 new_dysize != DYSIZE ||
9881 new_vxsize != VXSIZE ||
9882 new_vysize != VYSIZE ||
9883 new_exsize != EXSIZE ||
9884 new_eysize != EYSIZE ||
9885 new_real_sx != REAL_SX ||
9886 new_real_sy != REAL_SY ||
9887 new_full_sxsize != FULL_SXSIZE ||
9888 new_full_sysize != FULL_SYSIZE ||
9889 new_tilesize_var != TILESIZE_VAR
9892 // ------------------------------------------------------------------------
9893 // determine next fading area for changed viewport definitions
9894 // ------------------------------------------------------------------------
9896 // start with current playfield area (default fading area)
9899 FADE_SXSIZE = FULL_SXSIZE;
9900 FADE_SYSIZE = FULL_SYSIZE;
9902 // add new playfield area if position or size has changed
9903 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9904 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9906 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9907 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9910 // add current and new door 1 area if position or size has changed
9911 if (new_dx != DX || new_dy != DY ||
9912 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9914 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9915 DX, DY, DXSIZE, DYSIZE);
9916 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9917 new_dx, new_dy, new_dxsize, new_dysize);
9920 // add current and new door 2 area if position or size has changed
9921 if (new_vx != VX || new_vy != VY ||
9922 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9924 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9925 VX, VY, VXSIZE, VYSIZE);
9926 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9927 new_vx, new_vy, new_vxsize, new_vysize);
9930 // ------------------------------------------------------------------------
9931 // handle changed tile size
9932 // ------------------------------------------------------------------------
9934 if (new_tilesize_var != TILESIZE_VAR)
9936 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9938 // changing tile size invalidates scroll values of engine snapshots
9939 FreeEngineSnapshotSingle();
9941 // changing tile size requires update of graphic mapping for EM engine
9942 init_em_graphics = TRUE;
9953 SXSIZE = new_sxsize;
9954 SYSIZE = new_sysize;
9955 DXSIZE = new_dxsize;
9956 DYSIZE = new_dysize;
9957 VXSIZE = new_vxsize;
9958 VYSIZE = new_vysize;
9959 EXSIZE = new_exsize;
9960 EYSIZE = new_eysize;
9961 REAL_SX = new_real_sx;
9962 REAL_SY = new_real_sy;
9963 FULL_SXSIZE = new_full_sxsize;
9964 FULL_SYSIZE = new_full_sysize;
9965 TILESIZE_VAR = new_tilesize_var;
9967 init_gfx_buffers = TRUE;
9968 init_gadgets_and_anims = TRUE;
9970 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9971 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9974 if (init_gfx_buffers)
9976 // Debug("tools:viewport", "init_gfx_buffers");
9978 SCR_FIELDX = new_scr_fieldx_buffers;
9979 SCR_FIELDY = new_scr_fieldy_buffers;
9983 SCR_FIELDX = new_scr_fieldx;
9984 SCR_FIELDY = new_scr_fieldy;
9986 SetDrawDeactivationMask(REDRAW_NONE);
9987 SetDrawBackgroundMask(REDRAW_FIELD);
9990 if (init_video_buffer)
9992 // Debug("tools:viewport", "init_video_buffer");
9994 FreeAllImageTextures(); // needs old renderer to free the textures
9996 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9997 InitImageTextures();
10000 if (init_gadgets_and_anims)
10002 // Debug("tools:viewport", "init_gadgets_and_anims");
10005 InitGlobalAnimations();
10008 if (init_em_graphics)
10010 InitGraphicInfo_EM();
10014 void OpenURL(char *url)
10016 #if SDL_VERSION_ATLEAST(2,0,14)
10019 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
10020 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
10021 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
10025 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
10027 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
10031 // ============================================================================
10033 // ============================================================================
10035 #if defined(PLATFORM_WINDOWS)
10036 /* FILETIME of Jan 1 1970 00:00:00. */
10037 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
10040 * timezone information is stored outside the kernel so tzp isn't used anymore.
10042 * Note: this function is not for Win32 high precision timing purpose. See
10045 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
10047 FILETIME file_time;
10048 SYSTEMTIME system_time;
10049 ULARGE_INTEGER ularge;
10051 GetSystemTime(&system_time);
10052 SystemTimeToFileTime(&system_time, &file_time);
10053 ularge.LowPart = file_time.dwLowDateTime;
10054 ularge.HighPart = file_time.dwHighDateTime;
10056 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10057 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10063 static char *test_init_uuid_random_function_simple(void)
10065 static char seed_text[100];
10066 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10068 sprintf(seed_text, "%d", seed);
10073 static char *test_init_uuid_random_function_better(void)
10075 static char seed_text[100];
10076 struct timeval current_time;
10078 gettimeofday(¤t_time, NULL);
10080 prng_seed_bytes(¤t_time, sizeof(current_time));
10082 sprintf(seed_text, "%ld.%ld",
10083 (long)current_time.tv_sec,
10084 (long)current_time.tv_usec);
10089 #if defined(PLATFORM_WINDOWS)
10090 static char *test_init_uuid_random_function_better_windows(void)
10092 static char seed_text[100];
10093 struct timeval current_time;
10095 gettimeofday_windows(¤t_time, NULL);
10097 prng_seed_bytes(¤t_time, sizeof(current_time));
10099 sprintf(seed_text, "%ld.%ld",
10100 (long)current_time.tv_sec,
10101 (long)current_time.tv_usec);
10107 static unsigned int test_uuid_random_function_simple(int max)
10109 return GetSimpleRandom(max);
10112 static unsigned int test_uuid_random_function_better(int max)
10114 return (max > 0 ? prng_get_uint() % max : 0);
10117 #if defined(PLATFORM_WINDOWS)
10118 #define NUM_UUID_TESTS 3
10120 #define NUM_UUID_TESTS 2
10123 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10125 struct hashtable *hash_seeds =
10126 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10127 struct hashtable *hash_uuids =
10128 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10129 static char message[100];
10132 char *random_name = (nr == 0 ? "simple" : "better");
10133 char *random_type = (always_seed ? "always" : "only once");
10134 char *(*init_random_function)(void) =
10136 test_init_uuid_random_function_simple :
10137 test_init_uuid_random_function_better);
10138 unsigned int (*random_function)(int) =
10140 test_uuid_random_function_simple :
10141 test_uuid_random_function_better);
10144 #if defined(PLATFORM_WINDOWS)
10147 random_name = "windows";
10148 init_random_function = test_init_uuid_random_function_better_windows;
10154 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10155 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10157 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10158 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10159 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10161 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10165 // always initialize random number generator at least once
10166 init_random_function();
10168 unsigned int time_start = SDL_GetTicks();
10170 for (i = 0; i < num_uuids; i++)
10174 char *seed = getStringCopy(init_random_function());
10176 hashtable_remove(hash_seeds, seed);
10177 hashtable_insert(hash_seeds, seed, "1");
10180 char *uuid = getStringCopy(getUUIDExt(random_function));
10182 hashtable_remove(hash_uuids, uuid);
10183 hashtable_insert(hash_uuids, uuid, "1");
10186 int num_unique_seeds = hashtable_count(hash_seeds);
10187 int num_unique_uuids = hashtable_count(hash_uuids);
10189 unsigned int time_needed = SDL_GetTicks() - time_start;
10191 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10193 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10196 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10198 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10199 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10201 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10203 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10205 Request(message, REQ_CONFIRM);
10207 hashtable_destroy(hash_seeds, 0);
10208 hashtable_destroy(hash_uuids, 0);
10211 void TestGeneratingUUIDs(void)
10213 int num_uuids = 1000000;
10216 for (i = 0; i < NUM_UUID_TESTS; i++)
10217 for (j = 0; j < 2; j++)
10218 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10220 CloseAllAndExit(0);