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 // remove every mask before setting mask for window, and
1157 // remove window area mask before setting mask for main or door area
1158 int remove_mask = (redraw_mask == REDRAW_ALL ? 0xffff : REDRAW_ALL);
1160 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
1161 SetBackgroundBitmap(NULL, remove_mask, 0, 0, 0, 0); // !!! FIX THIS !!!
1162 SetBackgroundBitmap(g->bitmap, redraw_mask,
1164 g->width, g->height);
1167 void SetWindowBackgroundImageIfDefined(int graphic)
1169 if (graphic_info[graphic].bitmap)
1170 SetBackgroundImage(graphic, REDRAW_ALL);
1173 void SetMainBackgroundImageIfDefined(int graphic)
1175 if (graphic_info[graphic].bitmap)
1176 SetBackgroundImage(graphic, REDRAW_FIELD);
1179 void SetDoorBackgroundImageIfDefined(int graphic)
1181 if (graphic_info[graphic].bitmap)
1182 SetBackgroundImage(graphic, REDRAW_DOOR_1);
1185 void SetWindowBackgroundImage(int graphic)
1187 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_ALL);
1190 void SetMainBackgroundImage(int graphic)
1192 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_FIELD);
1195 void SetDoorBackgroundImage(int graphic)
1197 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_DOOR_1);
1200 void SetPanelBackground(void)
1202 SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
1205 void DrawBackground(int x, int y, int width, int height)
1207 // "drawto" might still point to playfield buffer here (hall of fame)
1208 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1210 if (IN_GFX_FIELD_FULL(x, y))
1211 redraw_mask |= REDRAW_FIELD;
1212 else if (IN_GFX_DOOR_1(x, y))
1213 redraw_mask |= REDRAW_DOOR_1;
1214 else if (IN_GFX_DOOR_2(x, y))
1215 redraw_mask |= REDRAW_DOOR_2;
1216 else if (IN_GFX_DOOR_3(x, y))
1217 redraw_mask |= REDRAW_DOOR_3;
1220 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1222 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1224 if (font->bitmap == NULL)
1227 DrawBackground(x, y, width, height);
1230 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1232 struct GraphicInfo *g = &graphic_info[graphic];
1234 if (g->bitmap == NULL)
1237 DrawBackground(x, y, width, height);
1240 static int game_status_last = -1;
1241 static Bitmap *global_border_bitmap_last = NULL;
1242 static Bitmap *global_border_bitmap = NULL;
1243 static int real_sx_last = -1, real_sy_last = -1;
1244 static int full_sxsize_last = -1, full_sysize_last = -1;
1245 static int dx_last = -1, dy_last = -1;
1246 static int dxsize_last = -1, dysize_last = -1;
1247 static int vx_last = -1, vy_last = -1;
1248 static int vxsize_last = -1, vysize_last = -1;
1249 static int ex_last = -1, ey_last = -1;
1250 static int exsize_last = -1, eysize_last = -1;
1252 boolean CheckIfGlobalBorderHasChanged(void)
1254 // if game status has not changed, global border has not changed either
1255 if (game_status == game_status_last)
1258 // determine and store new global border bitmap for current game status
1259 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1261 return (global_border_bitmap_last != global_border_bitmap);
1264 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1266 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1267 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1269 // if game status has not changed, nothing has to be redrawn
1270 if (game_status == game_status_last)
1273 // redraw if last screen was title screen
1274 if (game_status_last == GAME_MODE_TITLE)
1277 // redraw if global screen border has changed
1278 if (CheckIfGlobalBorderHasChanged())
1281 // redraw if position or size of playfield area has changed
1282 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1283 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1286 // redraw if position or size of door area has changed
1287 if (dx_last != DX || dy_last != DY ||
1288 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1291 // redraw if position or size of tape area has changed
1292 if (vx_last != VX || vy_last != VY ||
1293 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1296 // redraw if position or size of editor area has changed
1297 if (ex_last != EX || ey_last != EY ||
1298 exsize_last != EXSIZE || eysize_last != EYSIZE)
1305 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1308 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1310 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1313 void RedrawGlobalBorder(void)
1315 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1317 RedrawGlobalBorderFromBitmap(bitmap);
1319 redraw_mask = REDRAW_ALL;
1322 static void RedrawGlobalBorderIfNeeded(void)
1324 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1325 if (game_status == game_status_last)
1329 // copy current draw buffer to later copy back areas that have not changed
1330 if (game_status_last != GAME_MODE_TITLE)
1331 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1333 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1334 if (CheckIfGlobalBorderRedrawIsNeeded())
1336 // determine and store new global border bitmap for current game status
1337 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1340 // redraw global screen border (or clear, if defined to be empty)
1341 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1343 if (game_status == GAME_MODE_EDITOR)
1344 DrawSpecialEditorDoor();
1346 // copy previous playfield and door areas, if they are defined on both
1347 // previous and current screen and if they still have the same size
1349 if (real_sx_last != -1 && real_sy_last != -1 &&
1350 REAL_SX != -1 && REAL_SY != -1 &&
1351 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1352 BlitBitmap(bitmap_db_store_1, backbuffer,
1353 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1356 if (dx_last != -1 && dy_last != -1 &&
1357 DX != -1 && DY != -1 &&
1358 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1359 BlitBitmap(bitmap_db_store_1, backbuffer,
1360 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1362 if (game_status != GAME_MODE_EDITOR)
1364 if (vx_last != -1 && vy_last != -1 &&
1365 VX != -1 && VY != -1 &&
1366 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1367 BlitBitmap(bitmap_db_store_1, backbuffer,
1368 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1372 if (ex_last != -1 && ey_last != -1 &&
1373 EX != -1 && EY != -1 &&
1374 exsize_last == EXSIZE && eysize_last == EYSIZE)
1375 BlitBitmap(bitmap_db_store_1, backbuffer,
1376 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1379 redraw_mask = REDRAW_ALL;
1382 game_status_last = game_status;
1384 global_border_bitmap_last = global_border_bitmap;
1386 real_sx_last = REAL_SX;
1387 real_sy_last = REAL_SY;
1388 full_sxsize_last = FULL_SXSIZE;
1389 full_sysize_last = FULL_SYSIZE;
1392 dxsize_last = DXSIZE;
1393 dysize_last = DYSIZE;
1396 vxsize_last = VXSIZE;
1397 vysize_last = VYSIZE;
1400 exsize_last = EXSIZE;
1401 eysize_last = EYSIZE;
1404 void ClearField(void)
1406 RedrawGlobalBorderIfNeeded();
1408 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1409 // (when entering hall of fame after playing)
1410 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1412 // !!! maybe this should be done before clearing the background !!!
1413 if (game_status == GAME_MODE_PLAYING)
1415 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1416 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1420 SetDrawtoField(DRAW_TO_BACKBUFFER);
1424 void MarkTileDirty(int x, int y)
1426 redraw_mask |= REDRAW_FIELD;
1429 void SetBorderElement(void)
1433 BorderElement = EL_EMPTY;
1435 // only the R'n'D game engine may use an additional steelwall border
1436 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1439 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1441 for (x = 0; x < lev_fieldx; x++)
1443 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1444 BorderElement = EL_STEELWALL;
1446 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1452 void FloodFillLevelExt(int start_x, int start_y, int fill_element,
1453 int max_array_fieldx, int max_array_fieldy,
1454 short field[max_array_fieldx][max_array_fieldy],
1455 int max_fieldx, int max_fieldy)
1457 static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY];
1458 struct XY *check = xy_topdown;
1459 int old_element = field[start_x][start_y];
1462 // do nothing if start field already has the desired content
1463 if (old_element == fill_element)
1466 stack_buffer[stack_pos++] = (struct XY){ start_x, start_y };
1468 while (stack_pos > 0)
1470 struct XY current = stack_buffer[--stack_pos];
1473 field[current.x][current.y] = fill_element;
1475 for (i = 0; i < 4; i++)
1477 int x = current.x + check[i].x;
1478 int y = current.y + check[i].y;
1480 // check for stack buffer overflow (should not happen)
1481 if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY)
1482 Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug.");
1484 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1485 stack_buffer[stack_pos++] = (struct XY){ x, y };
1490 void FloodFillLevel(int from_x, int from_y, int fill_element,
1491 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1492 int max_fieldx, int max_fieldy)
1494 FloodFillLevelExt(from_x, from_y, fill_element,
1495 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1496 max_fieldx, max_fieldy);
1499 void SetRandomAnimationValue(int x, int y)
1501 gfx.anim_random_frame = GfxRandom[x][y];
1504 int getGraphicAnimationFrame(int graphic, int sync_frame)
1506 // animation synchronized with global frame counter, not move position
1507 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1508 sync_frame = FrameCounter;
1509 else if (graphic_info[graphic].anim_global_anim_sync)
1510 sync_frame = getGlobalAnimSyncFrame();
1512 return getAnimationFrame(graphic_info[graphic].anim_frames,
1513 graphic_info[graphic].anim_delay,
1514 graphic_info[graphic].anim_mode,
1515 graphic_info[graphic].anim_start_frame,
1519 int getGraphicAnimationFrameXY(int graphic, int lx, int ly)
1521 if (graphic_info[graphic].anim_mode & ANIM_TILED)
1523 struct GraphicInfo *g = &graphic_info[graphic];
1524 int xsize = MAX(1, g->anim_frames_per_line);
1525 int ysize = MAX(1, g->anim_frames / xsize);
1526 int xoffset = g->anim_start_frame % xsize;
1527 int yoffset = g->anim_start_frame % ysize;
1528 // may be needed if screen field is significantly larger than playfield
1529 int x = (lx + xoffset + SCR_FIELDX * xsize) % xsize;
1530 int y = (ly + yoffset + SCR_FIELDY * ysize) % ysize;
1531 int sync_frame = y * xsize + x;
1533 return sync_frame % g->anim_frames;
1535 else if (graphic_info[graphic].anim_mode & ANIM_RANDOM_STATIC)
1537 struct GraphicInfo *g = &graphic_info[graphic];
1538 // may be needed if screen field is significantly larger than playfield
1539 int x = (lx + SCR_FIELDX * lev_fieldx) % lev_fieldx;
1540 int y = (ly + SCR_FIELDY * lev_fieldy) % lev_fieldy;
1541 int sync_frame = GfxRandomStatic[x][y];
1543 return sync_frame % g->anim_frames;
1547 int sync_frame = (IN_LEV_FIELD(lx, ly) ? GfxFrame[lx][ly] : -1);
1549 return getGraphicAnimationFrame(graphic, sync_frame);
1553 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1555 struct GraphicInfo *g = &graphic_info[graphic];
1556 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1558 if (tilesize == gfx.standard_tile_size)
1559 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1560 else if (tilesize == game.tile_size)
1561 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1563 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1566 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1567 boolean get_backside)
1569 struct GraphicInfo *g = &graphic_info[graphic];
1570 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1571 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1573 if (g->offset_y == 0) // frames are ordered horizontally
1575 int max_width = g->anim_frames_per_line * g->width;
1576 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1578 *x = pos % max_width;
1579 *y = src_y % g->height + pos / max_width * g->height;
1581 else if (g->offset_x == 0) // frames are ordered vertically
1583 int max_height = g->anim_frames_per_line * g->height;
1584 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1586 *x = src_x % g->width + pos / max_height * g->width;
1587 *y = pos % max_height;
1589 else // frames are ordered diagonally
1591 *x = src_x + frame * g->offset_x;
1592 *y = src_y + frame * g->offset_y;
1596 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1597 Bitmap **bitmap, int *x, int *y,
1598 boolean get_backside)
1600 struct GraphicInfo *g = &graphic_info[graphic];
1602 // if no graphics defined at all, use fallback graphics
1603 if (g->bitmaps == NULL)
1604 *g = graphic_info[IMG_CHAR_EXCLAM];
1606 // if no in-game graphics defined, always use standard graphic size
1607 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1608 tilesize = TILESIZE;
1610 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1611 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1613 *x = *x * tilesize / g->tile_size;
1614 *y = *y * tilesize / g->tile_size;
1617 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1618 Bitmap **bitmap, int *x, int *y)
1620 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1623 void getFixedGraphicSource(int graphic, int frame,
1624 Bitmap **bitmap, int *x, int *y)
1626 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1629 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1631 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1634 void getGlobalAnimGraphicSource(int graphic, int frame,
1635 Bitmap **bitmap, int *x, int *y)
1637 struct GraphicInfo *g = &graphic_info[graphic];
1639 // if no graphics defined at all, use fallback graphics
1640 if (g->bitmaps == NULL)
1641 *g = graphic_info[IMG_CHAR_EXCLAM];
1643 // use original size graphics, if existing, else use standard size graphics
1644 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1645 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1647 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1649 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1652 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1653 int *x, int *y, boolean get_backside)
1655 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1659 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1661 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1664 void DrawGraphic(int x, int y, int graphic, int frame)
1667 if (!IN_SCR_FIELD(x, y))
1669 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1670 Debug("draw:DrawGraphic", "This should never happen!");
1676 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1679 MarkTileDirty(x, y);
1682 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1685 if (!IN_SCR_FIELD(x, y))
1687 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1689 Debug("draw:DrawFixedGraphic", "This should never happen!");
1695 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1697 MarkTileDirty(x, y);
1700 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1706 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1708 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1711 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1717 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1718 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1721 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1724 if (!IN_SCR_FIELD(x, y))
1726 Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1728 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1734 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1737 MarkTileDirty(x, y);
1740 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1743 if (!IN_SCR_FIELD(x, y))
1745 Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1747 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1753 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1755 MarkTileDirty(x, y);
1758 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1764 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1766 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1770 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1771 int graphic, int frame)
1776 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1778 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1782 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1784 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1786 MarkTileDirty(x / tilesize, y / tilesize);
1789 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1792 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1793 graphic, frame, tilesize);
1794 MarkTileDirty(x / tilesize, y / tilesize);
1797 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1803 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1804 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1807 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1808 int frame, int tilesize)
1813 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1814 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1817 void DrawMiniGraphic(int x, int y, int graphic)
1819 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1820 MarkTileDirty(x / 2, y / 2);
1823 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1828 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1829 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1832 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1833 int graphic, int frame,
1834 int cut_mode, int mask_mode)
1839 int width = TILEX, height = TILEY;
1842 if (dx || dy) // shifted graphic
1844 if (x < BX1) // object enters playfield from the left
1851 else if (x > BX2) // object enters playfield from the right
1857 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1863 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1865 else if (dx) // general horizontal movement
1866 MarkTileDirty(x + SIGN(dx), y);
1868 if (y < BY1) // object enters playfield from the top
1870 if (cut_mode == CUT_BELOW) // object completely above top border
1878 else if (y > BY2) // object enters playfield from the bottom
1884 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1890 else if (dy > 0 && cut_mode == CUT_ABOVE)
1892 if (y == BY2) // object completely above bottom border
1898 MarkTileDirty(x, y + 1);
1899 } // object leaves playfield to the bottom
1900 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1902 else if (dy) // general vertical movement
1903 MarkTileDirty(x, y + SIGN(dy));
1907 if (!IN_SCR_FIELD(x, y))
1909 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1911 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1917 width = width * TILESIZE_VAR / TILESIZE;
1918 height = height * TILESIZE_VAR / TILESIZE;
1919 cx = cx * TILESIZE_VAR / TILESIZE;
1920 cy = cy * TILESIZE_VAR / TILESIZE;
1921 dx = dx * TILESIZE_VAR / TILESIZE;
1922 dy = dy * TILESIZE_VAR / TILESIZE;
1924 if (width > 0 && height > 0)
1926 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1931 dst_x = FX + x * TILEX_VAR + dx;
1932 dst_y = FY + y * TILEY_VAR + dy;
1934 if (mask_mode == USE_MASKING)
1935 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1938 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1941 MarkTileDirty(x, y);
1945 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1946 int graphic, int frame,
1947 int cut_mode, int mask_mode)
1952 int width = TILEX_VAR, height = TILEY_VAR;
1955 int x2 = x + SIGN(dx);
1956 int y2 = y + SIGN(dy);
1958 // movement with two-tile animations must be sync'ed with movement position,
1959 // not with current GfxFrame (which can be higher when using slow movement)
1960 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1961 int anim_frames = graphic_info[graphic].anim_frames;
1963 // (we also need anim_delay here for movement animations with less frames)
1964 int anim_delay = graphic_info[graphic].anim_delay;
1965 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1967 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1968 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1970 // re-calculate animation frame for two-tile movement animation
1971 frame = getGraphicAnimationFrame(graphic, sync_frame);
1973 // check if movement start graphic inside screen area and should be drawn
1974 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1976 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1978 dst_x = FX + x1 * TILEX_VAR;
1979 dst_y = FY + y1 * TILEY_VAR;
1981 if (mask_mode == USE_MASKING)
1982 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1985 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1988 MarkTileDirty(x1, y1);
1991 // check if movement end graphic inside screen area and should be drawn
1992 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1994 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1996 dst_x = FX + x2 * TILEX_VAR;
1997 dst_y = FY + y2 * TILEY_VAR;
1999 if (mask_mode == USE_MASKING)
2000 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
2003 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
2006 MarkTileDirty(x2, y2);
2010 static void DrawGraphicShifted(int x, int y, int dx, int dy,
2011 int graphic, int frame,
2012 int cut_mode, int mask_mode)
2016 DrawGraphic(x, y, graphic, frame);
2021 if (graphic_info[graphic].double_movement) // EM style movement images
2022 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
2024 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
2027 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
2028 int graphic, int frame, int cut_mode)
2030 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
2033 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
2034 int cut_mode, int mask_mode)
2036 int lx = LEVELX(x), ly = LEVELY(y);
2040 if (IN_LEV_FIELD(lx, ly))
2042 if (element == EL_EMPTY)
2043 element = GfxElementEmpty[lx][ly];
2045 SetRandomAnimationValue(lx, ly);
2047 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2048 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2050 // do not use double (EM style) movement graphic when not moving
2051 if (graphic_info[graphic].double_movement && !dx && !dy)
2053 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2054 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2057 if (game.use_masked_elements && (dx || dy))
2058 mask_mode = USE_MASKING;
2060 else // border element
2062 graphic = el2img(element);
2063 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2066 if (element == EL_EXPANDABLE_WALL)
2068 boolean left_stopped = FALSE, right_stopped = FALSE;
2070 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2071 left_stopped = TRUE;
2072 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2073 right_stopped = TRUE;
2075 if (left_stopped && right_stopped)
2077 else if (left_stopped)
2079 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2080 frame = graphic_info[graphic].anim_frames - 1;
2082 else if (right_stopped)
2084 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2085 frame = graphic_info[graphic].anim_frames - 1;
2090 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2091 else if (mask_mode == USE_MASKING)
2092 DrawGraphicThruMask(x, y, graphic, frame);
2094 DrawGraphic(x, y, graphic, frame);
2097 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2098 int cut_mode, int mask_mode)
2100 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2101 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2102 cut_mode, mask_mode);
2105 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2108 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2111 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2114 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2117 void DrawLevelElementThruMask(int x, int y, int element)
2119 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2122 void DrawLevelFieldThruMask(int x, int y)
2124 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2127 // !!! implementation of quicksand is totally broken !!!
2128 #define IS_CRUMBLED_TILE(x, y, e) \
2129 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2130 !IS_MOVING(x, y) || \
2131 (e) == EL_QUICKSAND_EMPTYING || \
2132 (e) == EL_QUICKSAND_FAST_EMPTYING))
2134 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2139 int width, height, cx, cy;
2140 int sx = SCREENX(x), sy = SCREENY(y);
2141 int crumbled_border_size = graphic_info[graphic].border_size;
2142 int crumbled_tile_size = graphic_info[graphic].tile_size;
2143 int crumbled_border_size_var =
2144 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2147 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2149 for (i = 1; i < 4; i++)
2151 int dxx = (i & 1 ? dx : 0);
2152 int dyy = (i & 2 ? dy : 0);
2155 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2158 // check if neighbour field is of same crumble type
2159 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2160 graphic_info[graphic].class ==
2161 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2163 // return if check prevents inner corner
2164 if (same == (dxx == dx && dyy == dy))
2168 // if we reach this point, we have an inner corner
2170 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2172 width = crumbled_border_size_var;
2173 height = crumbled_border_size_var;
2174 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2175 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2177 if (game.use_masked_elements)
2179 int graphic0 = el2img(EL_EMPTY);
2180 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2181 Bitmap *src_bitmap0;
2184 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2186 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2188 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2190 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2192 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2195 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2197 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2200 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2205 int width, height, bx, by, cx, cy;
2206 int sx = SCREENX(x), sy = SCREENY(y);
2207 int crumbled_border_size = graphic_info[graphic].border_size;
2208 int crumbled_tile_size = graphic_info[graphic].tile_size;
2209 int crumbled_border_size_var =
2210 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2211 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2214 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2216 // only needed when using masked elements
2217 int graphic0 = el2img(EL_EMPTY);
2218 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2219 Bitmap *src_bitmap0;
2222 if (game.use_masked_elements)
2223 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2225 // draw simple, sloppy, non-corner-accurate crumbled border
2227 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2228 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2229 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2230 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2232 if (game.use_masked_elements)
2234 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2236 FX + sx * TILEX_VAR + cx,
2237 FY + sy * TILEY_VAR + cy);
2239 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2241 FX + sx * TILEX_VAR + cx,
2242 FY + sy * TILEY_VAR + cy);
2245 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2247 FX + sx * TILEX_VAR + cx,
2248 FY + sy * TILEY_VAR + cy);
2250 // (remaining middle border part must be at least as big as corner part)
2251 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2252 crumbled_border_size_var >= TILESIZE_VAR / 3)
2255 // correct corners of crumbled border, if needed
2257 for (i = -1; i <= 1; i += 2)
2259 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2260 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2261 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2264 // check if neighbour field is of same crumble type
2265 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2266 graphic_info[graphic].class ==
2267 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2269 // no crumbled corner, but continued crumbled border
2271 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2272 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2273 int b1 = (i == 1 ? crumbled_border_size_var :
2274 TILESIZE_VAR - 2 * crumbled_border_size_var);
2276 width = crumbled_border_size_var;
2277 height = crumbled_border_size_var;
2279 if (dir == 1 || dir == 2)
2294 if (game.use_masked_elements)
2296 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2298 FX + sx * TILEX_VAR + cx,
2299 FY + sy * TILEY_VAR + cy);
2301 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2303 FX + sx * TILEX_VAR + cx,
2304 FY + sy * TILEY_VAR + cy);
2307 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2309 FX + sx * TILEX_VAR + cx,
2310 FY + sy * TILEY_VAR + cy);
2315 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2317 int sx = SCREENX(x), sy = SCREENY(y);
2320 struct XY *xy = xy_topdown;
2322 if (!IN_LEV_FIELD(x, y))
2325 element = TILE_GFX_ELEMENT(x, y);
2327 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2329 if (!IN_SCR_FIELD(sx, sy))
2332 // crumble field borders towards direct neighbour fields
2333 for (i = 0; i < 4; i++)
2335 int xx = x + xy[i].x;
2336 int yy = y + xy[i].y;
2338 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2341 // check if neighbour field is of same crumble type
2342 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2343 graphic_info[graphic].class ==
2344 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2347 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2350 // crumble inner field corners towards corner neighbour fields
2351 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2352 graphic_info[graphic].anim_frames == 2)
2354 for (i = 0; i < 4; i++)
2356 int dx = (i & 1 ? +1 : -1);
2357 int dy = (i & 2 ? +1 : -1);
2359 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2363 MarkTileDirty(sx, sy);
2365 else // center field is not crumbled -- crumble neighbour fields
2367 // crumble field borders of direct neighbour fields
2368 for (i = 0; i < 4; i++)
2370 int xx = x + xy[i].x;
2371 int yy = y + xy[i].y;
2372 int sxx = sx + xy[i].x;
2373 int syy = sy + xy[i].y;
2375 if (!IN_LEV_FIELD(xx, yy) ||
2376 !IN_SCR_FIELD(sxx, syy))
2379 // do not crumble fields that are being digged or snapped
2380 if (Tile[xx][yy] == EL_EMPTY ||
2381 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2384 element = TILE_GFX_ELEMENT(xx, yy);
2386 if (!IS_CRUMBLED_TILE(xx, yy, element))
2389 graphic = el_act2crm(element, ACTION_DEFAULT);
2391 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2393 MarkTileDirty(sxx, syy);
2396 // crumble inner field corners of corner neighbour fields
2397 for (i = 0; i < 4; i++)
2399 int dx = (i & 1 ? +1 : -1);
2400 int dy = (i & 2 ? +1 : -1);
2406 if (!IN_LEV_FIELD(xx, yy) ||
2407 !IN_SCR_FIELD(sxx, syy))
2410 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2413 element = TILE_GFX_ELEMENT(xx, yy);
2415 if (!IS_CRUMBLED_TILE(xx, yy, element))
2418 graphic = el_act2crm(element, ACTION_DEFAULT);
2420 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2421 graphic_info[graphic].anim_frames == 2)
2422 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2424 MarkTileDirty(sxx, syy);
2429 void DrawLevelFieldCrumbled(int x, int y)
2433 if (!IN_LEV_FIELD(x, y))
2436 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2437 GfxElement[x][y] != EL_UNDEFINED &&
2438 GFX_CRUMBLED(GfxElement[x][y]))
2440 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2445 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2447 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2450 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2453 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2454 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2455 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2456 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2457 int sx = SCREENX(x), sy = SCREENY(y);
2459 DrawScreenGraphic(sx, sy, graphic1, frame1);
2460 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2463 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2465 int sx = SCREENX(x), sy = SCREENY(y);
2466 struct XY *xy = xy_topdown;
2469 // crumble direct neighbour fields (required for field borders)
2470 for (i = 0; i < 4; i++)
2472 int xx = x + xy[i].x;
2473 int yy = y + xy[i].y;
2474 int sxx = sx + xy[i].x;
2475 int syy = sy + xy[i].y;
2477 if (!IN_LEV_FIELD(xx, yy) ||
2478 !IN_SCR_FIELD(sxx, syy) ||
2479 !GFX_CRUMBLED(Tile[xx][yy]) ||
2483 DrawLevelField(xx, yy);
2486 // crumble corner neighbour fields (required for inner field corners)
2487 for (i = 0; i < 4; i++)
2489 int dx = (i & 1 ? +1 : -1);
2490 int dy = (i & 2 ? +1 : -1);
2496 if (!IN_LEV_FIELD(xx, yy) ||
2497 !IN_SCR_FIELD(sxx, syy) ||
2498 !GFX_CRUMBLED(Tile[xx][yy]) ||
2502 int element = TILE_GFX_ELEMENT(xx, yy);
2503 int graphic = el_act2crm(element, ACTION_DEFAULT);
2505 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2506 graphic_info[graphic].anim_frames == 2)
2507 DrawLevelField(xx, yy);
2511 static int getBorderElement(int x, int y)
2515 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2516 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2517 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2518 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2519 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2520 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2521 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2523 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2524 int steel_position = (x == -1 && y == -1 ? 0 :
2525 x == lev_fieldx && y == -1 ? 1 :
2526 x == -1 && y == lev_fieldy ? 2 :
2527 x == lev_fieldx && y == lev_fieldy ? 3 :
2528 x == -1 || x == lev_fieldx ? 4 :
2529 y == -1 || y == lev_fieldy ? 5 : 6);
2531 return border[steel_position][steel_type];
2534 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2536 if (game.use_masked_elements)
2538 if (graphic != el2img(EL_EMPTY))
2539 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2541 DrawGraphicThruMask(x, y, graphic, frame);
2545 DrawGraphic(x, y, graphic, frame);
2549 void DrawLevelGraphic(int x, int y, int graphic, int frame)
2551 DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2554 void DrawScreenElement(int x, int y, int element)
2556 int mask_mode = NO_MASKING;
2558 if (game.use_masked_elements)
2560 int lx = LEVELX(x), ly = LEVELY(y);
2562 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2564 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2566 mask_mode = USE_MASKING;
2570 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2571 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2574 void DrawLevelElement(int x, int y, int element)
2576 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2577 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2580 void DrawScreenField(int x, int y)
2582 int lx = LEVELX(x), ly = LEVELY(y);
2583 int element, content;
2585 if (!IN_LEV_FIELD(lx, ly))
2587 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2590 element = getBorderElement(lx, ly);
2592 DrawScreenElement(x, y, element);
2597 element = Tile[lx][ly];
2598 content = Store[lx][ly];
2600 if (IS_MOVING(lx, ly))
2602 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2603 boolean cut_mode = NO_CUTTING;
2605 if (element == EL_QUICKSAND_EMPTYING ||
2606 element == EL_QUICKSAND_FAST_EMPTYING ||
2607 element == EL_MAGIC_WALL_EMPTYING ||
2608 element == EL_BD_MAGIC_WALL_EMPTYING ||
2609 element == EL_DC_MAGIC_WALL_EMPTYING ||
2610 element == EL_AMOEBA_DROPPING)
2611 cut_mode = CUT_ABOVE;
2612 else if (element == EL_QUICKSAND_FILLING ||
2613 element == EL_QUICKSAND_FAST_FILLING ||
2614 element == EL_MAGIC_WALL_FILLING ||
2615 element == EL_BD_MAGIC_WALL_FILLING ||
2616 element == EL_DC_MAGIC_WALL_FILLING)
2617 cut_mode = CUT_BELOW;
2619 if (cut_mode == CUT_ABOVE)
2620 DrawScreenElement(x, y, element);
2622 DrawScreenElement(x, y, EL_EMPTY);
2624 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2626 int dir = MovDir[lx][ly];
2627 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2628 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2630 if (IN_SCR_FIELD(newx, newy))
2631 DrawScreenElement(newx, newy, EL_EMPTY);
2635 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2636 else if (cut_mode == NO_CUTTING)
2637 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2640 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2642 if (cut_mode == CUT_BELOW &&
2643 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2644 DrawLevelElement(lx, ly + 1, element);
2647 if (content == EL_ACID)
2649 int dir = MovDir[lx][ly];
2650 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2651 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2653 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2655 // prevent target field from being drawn again (but without masking)
2656 // (this would happen if target field is scanned after moving element)
2657 Stop[newlx][newly] = TRUE;
2660 else if (IS_BLOCKED(lx, ly))
2665 boolean cut_mode = NO_CUTTING;
2666 int element_old, content_old;
2668 Blocked2Moving(lx, ly, &oldx, &oldy);
2671 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2672 MovDir[oldx][oldy] == MV_RIGHT);
2674 element_old = Tile[oldx][oldy];
2675 content_old = Store[oldx][oldy];
2677 if (element_old == EL_QUICKSAND_EMPTYING ||
2678 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2679 element_old == EL_MAGIC_WALL_EMPTYING ||
2680 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2681 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2682 element_old == EL_AMOEBA_DROPPING)
2683 cut_mode = CUT_ABOVE;
2685 DrawScreenElement(x, y, EL_EMPTY);
2688 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2690 else if (cut_mode == NO_CUTTING)
2691 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2694 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2697 else if (IS_DRAWABLE(element))
2698 DrawScreenElement(x, y, element);
2700 DrawScreenElement(x, y, EL_EMPTY);
2703 void DrawLevelField(int x, int y)
2705 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2706 DrawScreenField(SCREENX(x), SCREENY(y));
2707 else if (IS_MOVING(x, y))
2711 Moving2Blocked(x, y, &newx, &newy);
2712 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2713 DrawScreenField(SCREENX(newx), SCREENY(newy));
2715 else if (IS_BLOCKED(x, y))
2719 Blocked2Moving(x, y, &oldx, &oldy);
2720 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2721 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2725 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2726 int (*el2img_function)(int), boolean masked,
2727 int element_bits_draw)
2729 int element_base = map_mm_wall_element(element);
2730 int element_bits = (IS_DF_WALL(element) ?
2731 element - EL_DF_WALL_START :
2732 IS_MM_WALL(element) ?
2733 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2734 int graphic = el2img_function(element_base);
2735 int tilesize_draw = tilesize / 2;
2740 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2742 for (i = 0; i < 4; i++)
2744 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2745 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2747 if (!(element_bits_draw & (1 << i)))
2750 if (element_bits & (1 << i))
2753 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2754 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2756 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2757 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2762 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2763 tilesize_draw, tilesize_draw);
2768 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2769 boolean masked, int element_bits_draw)
2771 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2772 element, tilesize, el2edimg, masked, element_bits_draw);
2775 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2776 int (*el2img_function)(int))
2778 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2782 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2785 if (IS_MM_WALL(element))
2787 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2788 element, tilesize, el2edimg, masked, 0x000f);
2792 int graphic = el2edimg(element);
2795 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2797 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2801 void DrawSizedElement(int x, int y, int element, int tilesize)
2803 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2806 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2808 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2811 void DrawMiniElement(int x, int y, int element)
2815 graphic = el2edimg(element);
2816 DrawMiniGraphic(x, y, graphic);
2819 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2822 int x = sx + scroll_x, y = sy + scroll_y;
2824 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2825 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2826 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2827 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2829 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2832 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2834 int x = sx + scroll_x, y = sy + scroll_y;
2836 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2837 DrawMiniElement(sx, sy, EL_EMPTY);
2838 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2839 DrawMiniElement(sx, sy, Tile[x][y]);
2841 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2844 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2845 int x, int y, int xsize, int ysize,
2846 int tile_width, int tile_height)
2850 int dst_x = startx + x * tile_width;
2851 int dst_y = starty + y * tile_height;
2852 int width = graphic_info[graphic].width;
2853 int height = graphic_info[graphic].height;
2854 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2855 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2856 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2857 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2858 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2859 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2860 boolean draw_masked = graphic_info[graphic].draw_masked;
2862 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2864 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2866 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2870 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2871 inner_sx + (x - 1) * tile_width % inner_width);
2872 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2873 inner_sy + (y - 1) * tile_height % inner_height);
2876 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2879 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2883 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2884 int x, int y, int xsize, int ysize,
2887 int font_width = getFontWidth(font_nr);
2888 int font_height = getFontHeight(font_nr);
2890 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2891 font_width, font_height);
2894 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2896 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2897 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2898 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2899 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2900 boolean no_delay = (tape.warp_forward);
2901 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2902 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2903 DelayCounter anim_delay = { anim_delay_value };
2904 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2905 int font_width = getFontWidth(font_nr);
2906 int font_height = getFontHeight(font_nr);
2907 int max_xsize = level.envelope[envelope_nr].xsize;
2908 int max_ysize = level.envelope[envelope_nr].ysize;
2909 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2910 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2911 int xend = max_xsize;
2912 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2913 int xstep = (xstart < xend ? 1 : 0);
2914 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2916 int end = MAX(xend - xstart, yend - ystart);
2919 for (i = start; i <= end; i++)
2921 int last_frame = end; // last frame of this "for" loop
2922 int x = xstart + i * xstep;
2923 int y = ystart + i * ystep;
2924 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2925 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2926 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2927 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2930 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2932 BlitScreenToBitmap(backbuffer);
2934 SetDrawtoField(DRAW_TO_BACKBUFFER);
2936 for (yy = 0; yy < ysize; yy++)
2937 for (xx = 0; xx < xsize; xx++)
2938 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2940 DrawTextArea(sx + font_width, sy + font_height,
2941 level.envelope[envelope_nr].text, font_nr, max_xsize,
2942 xsize - 2, ysize - 2, 0, mask_mode,
2943 level.envelope[envelope_nr].autowrap,
2944 level.envelope[envelope_nr].centered, FALSE);
2946 redraw_mask |= REDRAW_FIELD;
2949 SkipUntilDelayReached(&anim_delay, &i, last_frame);
2952 ClearAutoRepeatKeyEvents();
2955 void ShowEnvelope(int envelope_nr)
2957 int element = EL_ENVELOPE_1 + envelope_nr;
2958 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2959 int sound_opening = element_info[element].sound[ACTION_OPENING];
2960 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2961 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2962 boolean no_delay = (tape.warp_forward);
2963 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2964 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2965 int anim_mode = graphic_info[graphic].anim_mode;
2966 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2967 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2968 boolean overlay_enabled = GetOverlayEnabled();
2970 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2972 SetOverlayEnabled(FALSE);
2975 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2977 if (anim_mode == ANIM_DEFAULT)
2978 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2980 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2983 Delay_WithScreenUpdates(wait_delay_value);
2985 WaitForEventToContinue();
2988 SetOverlayEnabled(overlay_enabled);
2990 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2992 if (anim_mode != ANIM_NONE)
2993 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2995 if (anim_mode == ANIM_DEFAULT)
2996 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2998 game.envelope_active = FALSE;
3000 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3002 redraw_mask |= REDRAW_FIELD;
3006 void ShowEnvelope_MM(int envelope_nr)
3008 BlitBitmap(backbuffer, bitmap_db_field, REAL_SX, REAL_SY,
3009 FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
3011 ShowEnvelope(envelope_nr);
3013 SetDrawtoField(DRAW_TO_BACKBUFFER);
3015 BlitBitmap(bitmap_db_field, backbuffer, REAL_SX, REAL_SY,
3016 FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
3019 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
3020 int xsize, int ysize)
3022 if (!global.use_envelope_request ||
3023 request.sort_priority <= 0)
3026 if (request.bitmap == NULL ||
3027 xsize > request.xsize ||
3028 ysize > request.ysize)
3030 if (request.bitmap != NULL)
3031 FreeBitmap(request.bitmap);
3033 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
3035 SDL_Surface *surface = request.bitmap->surface;
3037 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
3038 Fail("SDLGetNativeSurface() failed");
3041 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3043 SDLFreeBitmapTextures(request.bitmap);
3044 SDLCreateBitmapTextures(request.bitmap);
3046 // set envelope request run-time values
3049 request.xsize = xsize;
3050 request.ysize = ysize;
3053 void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage)
3055 if (global.use_envelope_request &&
3056 game.request_active_or_moving &&
3057 request.sort_priority > 0 &&
3058 drawing_target == DRAW_TO_SCREEN &&
3059 drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
3061 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3062 request.sx, request.sy);
3066 static void setRequestBasePosition(int *x, int *y)
3068 int sx_base, sy_base;
3070 if (request.x != -1)
3071 sx_base = request.x;
3072 else if (request.align == ALIGN_LEFT)
3074 else if (request.align == ALIGN_RIGHT)
3075 sx_base = SX + SXSIZE;
3077 sx_base = SX + SXSIZE / 2;
3079 if (request.y != -1)
3080 sy_base = request.y;
3081 else if (request.valign == VALIGN_TOP)
3083 else if (request.valign == VALIGN_BOTTOM)
3084 sy_base = SY + SYSIZE;
3086 sy_base = SY + SYSIZE / 2;
3092 static void setRequestPositionExt(int *x, int *y, int width, int height,
3093 boolean add_border_size)
3095 int border_size = request.border_size;
3096 int sx_base, sy_base;
3099 setRequestBasePosition(&sx_base, &sy_base);
3101 if (request.align == ALIGN_LEFT)
3103 else if (request.align == ALIGN_RIGHT)
3104 sx = sx_base - width;
3106 sx = sx_base - width / 2;
3108 if (request.valign == VALIGN_TOP)
3110 else if (request.valign == VALIGN_BOTTOM)
3111 sy = sy_base - height;
3113 sy = sy_base - height / 2;
3115 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3116 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3118 if (add_border_size)
3128 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3130 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3133 static void DrawEnvelopeRequest(char *text)
3135 char *text_final = text;
3136 char *text_door_style = NULL;
3137 int graphic = IMG_BACKGROUND_REQUEST;
3138 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3139 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3140 int font_nr = FONT_REQUEST;
3141 int font_width = getFontWidth(font_nr);
3142 int font_height = getFontHeight(font_nr);
3143 int border_size = request.border_size;
3144 int line_spacing = request.line_spacing;
3145 int line_height = font_height + line_spacing;
3146 int max_text_width = request.width - 2 * border_size;
3147 int max_text_height = request.height - 2 * border_size;
3148 int line_length = max_text_width / font_width;
3149 int max_lines = max_text_height / line_height;
3150 int text_width = line_length * font_width;
3151 int width = request.width;
3152 int height = request.height;
3153 int tile_size = MAX(request.step_offset, 1);
3154 int x_steps = width / tile_size;
3155 int y_steps = height / tile_size;
3156 int sx_offset = border_size;
3157 int sy_offset = border_size;
3161 if (request.centered)
3162 sx_offset = (request.width - text_width) / 2;
3164 if (request.wrap_single_words && !request.autowrap)
3166 char *src_text_ptr, *dst_text_ptr;
3168 text_door_style = checked_malloc(2 * strlen(text) + 1);
3170 src_text_ptr = text;
3171 dst_text_ptr = text_door_style;
3173 while (*src_text_ptr)
3175 if (*src_text_ptr == ' ' ||
3176 *src_text_ptr == '?' ||
3177 *src_text_ptr == '!')
3178 *dst_text_ptr++ = '\n';
3180 if (*src_text_ptr != ' ')
3181 *dst_text_ptr++ = *src_text_ptr;
3186 *dst_text_ptr = '\0';
3188 text_final = text_door_style;
3191 setRequestPosition(&sx, &sy, FALSE);
3193 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3195 for (y = 0; y < y_steps; y++)
3196 for (x = 0; x < x_steps; x++)
3197 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3198 x, y, x_steps, y_steps,
3199 tile_size, tile_size);
3201 // force DOOR font inside door area
3202 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3204 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3205 line_length, -1, max_lines, line_spacing, mask_mode,
3206 request.autowrap, request.centered, FALSE);
3210 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3211 RedrawGadget(tool_gadget[i]);
3213 // store readily prepared envelope request for later use when animating
3214 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3216 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3218 if (text_door_style)
3219 free(text_door_style);
3222 static void AnimateEnvelopeRequest(int anim_mode, int action)
3224 int graphic = IMG_BACKGROUND_REQUEST;
3225 boolean draw_masked = graphic_info[graphic].draw_masked;
3226 int delay_value_normal = request.step_delay;
3227 int delay_value_fast = delay_value_normal / 2;
3228 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3229 boolean no_delay = (tape.warp_forward);
3230 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3231 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3232 DelayCounter anim_delay = { anim_delay_value };
3234 int tile_size = MAX(request.step_offset, 1);
3235 int max_xsize = request.width / tile_size;
3236 int max_ysize = request.height / tile_size;
3237 int max_xsize_inner = max_xsize - 2;
3238 int max_ysize_inner = max_ysize - 2;
3240 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3241 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3242 int xend = max_xsize_inner;
3243 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3244 int xstep = (xstart < xend ? 1 : 0);
3245 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3247 int end = MAX(xend - xstart, yend - ystart);
3250 if (setup.quick_doors)
3257 for (i = start; i <= end; i++)
3259 int last_frame = end; // last frame of this "for" loop
3260 int x = xstart + i * xstep;
3261 int y = ystart + i * ystep;
3262 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3263 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3264 int xsize_size_left = (xsize - 1) * tile_size;
3265 int ysize_size_top = (ysize - 1) * tile_size;
3266 int max_xsize_pos = (max_xsize - 1) * tile_size;
3267 int max_ysize_pos = (max_ysize - 1) * tile_size;
3268 int width = xsize * tile_size;
3269 int height = ysize * tile_size;
3274 setRequestPosition(&src_x, &src_y, FALSE);
3275 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3277 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3279 for (yy = 0; yy < 2; yy++)
3281 for (xx = 0; xx < 2; xx++)
3283 int src_xx = src_x + xx * max_xsize_pos;
3284 int src_yy = src_y + yy * max_ysize_pos;
3285 int dst_xx = dst_x + xx * xsize_size_left;
3286 int dst_yy = dst_y + yy * ysize_size_top;
3287 int xx_size = (xx ? tile_size : xsize_size_left);
3288 int yy_size = (yy ? tile_size : ysize_size_top);
3291 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3292 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3294 BlitBitmap(bitmap_db_store_2, backbuffer,
3295 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3299 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3301 redraw_mask |= REDRAW_FIELD;
3305 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3308 ClearAutoRepeatKeyEvents();
3311 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3313 int graphic = IMG_BACKGROUND_REQUEST;
3314 int sound_opening = SND_REQUEST_OPENING;
3315 int sound_closing = SND_REQUEST_CLOSING;
3316 int anim_mode_1 = request.anim_mode; // (higher priority)
3317 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3318 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3319 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3320 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3322 if (game_status == GAME_MODE_PLAYING)
3323 BlitScreenToBitmap(backbuffer);
3325 SetDrawtoField(DRAW_TO_BACKBUFFER);
3327 // SetDrawBackgroundMask(REDRAW_NONE);
3329 if (action == ACTION_OPENING)
3331 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3333 if (req_state & REQ_ASK)
3335 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3336 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3337 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3338 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3340 else if (req_state & REQ_CONFIRM)
3342 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3343 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3345 else if (req_state & REQ_PLAYER)
3347 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3348 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3349 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3350 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3353 DrawEnvelopeRequest(text);
3356 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3358 if (action == ACTION_OPENING)
3360 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3362 if (anim_mode == ANIM_DEFAULT)
3363 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3365 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3369 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3371 if (anim_mode != ANIM_NONE)
3372 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3374 if (anim_mode == ANIM_DEFAULT)
3375 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3378 game.envelope_active = FALSE;
3380 if (action == ACTION_CLOSING)
3381 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3383 // SetDrawBackgroundMask(last_draw_background_mask);
3385 redraw_mask |= REDRAW_FIELD;
3389 if (action == ACTION_CLOSING &&
3390 game_status == GAME_MODE_PLAYING &&
3391 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3392 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3395 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3397 if (IS_MM_WALL(element))
3399 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3405 int graphic = el2preimg(element);
3407 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3408 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3413 void DrawLevel(int draw_background_mask)
3417 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3418 SetDrawBackgroundMask(draw_background_mask);
3422 for (x = BX1; x <= BX2; x++)
3423 for (y = BY1; y <= BY2; y++)
3424 DrawScreenField(x, y);
3426 redraw_mask |= REDRAW_FIELD;
3429 void DrawSizedLevel(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 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3438 redraw_mask |= REDRAW_FIELD;
3441 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3445 for (x = 0; x < size_x; x++)
3446 for (y = 0; y < size_y; y++)
3447 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3449 redraw_mask |= REDRAW_FIELD;
3452 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3454 boolean show_level_border = (BorderElement != EL_EMPTY);
3455 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3456 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3457 int tile_size = preview.tile_size;
3458 int preview_width = preview.xsize * tile_size;
3459 int preview_height = preview.ysize * tile_size;
3460 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3461 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3462 int real_preview_width = real_preview_xsize * tile_size;
3463 int real_preview_height = real_preview_ysize * tile_size;
3464 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3465 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3468 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3471 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3473 dst_x += (preview_width - real_preview_width) / 2;
3474 dst_y += (preview_height - real_preview_height) / 2;
3476 for (x = 0; x < real_preview_xsize; x++)
3478 for (y = 0; y < real_preview_ysize; y++)
3480 int lx = from_x + x + (show_level_border ? -1 : 0);
3481 int ly = from_y + y + (show_level_border ? -1 : 0);
3482 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3483 getBorderElement(lx, ly));
3485 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3486 element, tile_size);
3490 redraw_mask |= REDRAW_FIELD;
3493 #define MICROLABEL_EMPTY 0
3494 #define MICROLABEL_LEVEL_NAME 1
3495 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3496 #define MICROLABEL_LEVEL_AUTHOR 3
3497 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3498 #define MICROLABEL_IMPORTED_FROM 5
3499 #define MICROLABEL_IMPORTED_BY_HEAD 6
3500 #define MICROLABEL_IMPORTED_BY 7
3502 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3504 int max_text_width = SXSIZE;
3505 int font_width = getFontWidth(font_nr);
3507 if (pos->align == ALIGN_CENTER)
3508 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3509 else if (pos->align == ALIGN_RIGHT)
3510 max_text_width = pos->x;
3512 max_text_width = SXSIZE - pos->x;
3514 return max_text_width / font_width;
3517 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3519 char label_text[MAX_OUTPUT_LINESIZE + 1];
3520 int max_len_label_text;
3521 int font_nr = pos->font;
3524 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3527 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3528 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3529 mode == MICROLABEL_IMPORTED_BY_HEAD)
3530 font_nr = pos->font_alt;
3532 max_len_label_text = getMaxTextLength(pos, font_nr);
3534 if (pos->size != -1)
3535 max_len_label_text = pos->size;
3537 for (i = 0; i < max_len_label_text; i++)
3538 label_text[i] = ' ';
3539 label_text[max_len_label_text] = '\0';
3541 if (strlen(label_text) > 0)
3542 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3545 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3546 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3547 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3548 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3549 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3550 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3551 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3552 max_len_label_text);
3553 label_text[max_len_label_text] = '\0';
3555 if (strlen(label_text) > 0)
3556 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3558 redraw_mask |= REDRAW_FIELD;
3561 static void DrawPreviewLevelLabel(int mode)
3563 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3566 static void DrawPreviewLevelInfo(int mode)
3568 if (mode == MICROLABEL_LEVEL_NAME)
3569 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3570 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3571 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3574 static void DrawPreviewLevelExt(boolean restart)
3576 static DelayCounter scroll_delay = { 0 };
3577 static DelayCounter label_delay = { 0 };
3578 static int from_x, from_y, scroll_direction;
3579 static int label_state, label_counter;
3580 boolean show_level_border = (BorderElement != EL_EMPTY);
3581 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3582 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3584 scroll_delay.value = preview.step_delay;
3585 label_delay.value = MICROLEVEL_LABEL_DELAY;
3592 if (preview.anim_mode == ANIM_CENTERED)
3594 if (level_xsize > preview.xsize)
3595 from_x = (level_xsize - preview.xsize) / 2;
3596 if (level_ysize > preview.ysize)
3597 from_y = (level_ysize - preview.ysize) / 2;
3600 from_x += preview.xoffset;
3601 from_y += preview.yoffset;
3603 scroll_direction = MV_RIGHT;
3607 DrawPreviewLevelPlayfield(from_x, from_y);
3608 DrawPreviewLevelLabel(label_state);
3610 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3611 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3613 // initialize delay counters
3614 ResetDelayCounter(&scroll_delay);
3615 ResetDelayCounter(&label_delay);
3617 if (leveldir_current->name)
3619 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3620 char label_text[MAX_OUTPUT_LINESIZE + 1];
3621 int font_nr = pos->font;
3622 int max_len_label_text = getMaxTextLength(pos, font_nr);
3624 if (pos->size != -1)
3625 max_len_label_text = pos->size;
3627 strncpy(label_text, leveldir_current->name, max_len_label_text);
3628 label_text[max_len_label_text] = '\0';
3630 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3631 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3637 // scroll preview level, if needed
3638 if (preview.anim_mode != ANIM_NONE &&
3639 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3640 DelayReached(&scroll_delay))
3642 switch (scroll_direction)
3647 from_x -= preview.step_offset;
3648 from_x = (from_x < 0 ? 0 : from_x);
3651 scroll_direction = MV_UP;
3655 if (from_x < level_xsize - preview.xsize)
3657 from_x += preview.step_offset;
3658 from_x = (from_x > level_xsize - preview.xsize ?
3659 level_xsize - preview.xsize : from_x);
3662 scroll_direction = MV_DOWN;
3668 from_y -= preview.step_offset;
3669 from_y = (from_y < 0 ? 0 : from_y);
3672 scroll_direction = MV_RIGHT;
3676 if (from_y < level_ysize - preview.ysize)
3678 from_y += preview.step_offset;
3679 from_y = (from_y > level_ysize - preview.ysize ?
3680 level_ysize - preview.ysize : from_y);
3683 scroll_direction = MV_LEFT;
3690 DrawPreviewLevelPlayfield(from_x, from_y);
3693 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3694 // redraw micro level label, if needed
3695 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3696 !strEqual(level.author, ANONYMOUS_NAME) &&
3697 !strEqual(level.author, leveldir_current->name) &&
3698 DelayReached(&label_delay))
3700 int max_label_counter = 23;
3702 if (leveldir_current->imported_from != NULL &&
3703 strlen(leveldir_current->imported_from) > 0)
3704 max_label_counter += 14;
3705 if (leveldir_current->imported_by != NULL &&
3706 strlen(leveldir_current->imported_by) > 0)
3707 max_label_counter += 14;
3709 label_counter = (label_counter + 1) % max_label_counter;
3710 label_state = (label_counter >= 0 && label_counter <= 7 ?
3711 MICROLABEL_LEVEL_NAME :
3712 label_counter >= 9 && label_counter <= 12 ?
3713 MICROLABEL_LEVEL_AUTHOR_HEAD :
3714 label_counter >= 14 && label_counter <= 21 ?
3715 MICROLABEL_LEVEL_AUTHOR :
3716 label_counter >= 23 && label_counter <= 26 ?
3717 MICROLABEL_IMPORTED_FROM_HEAD :
3718 label_counter >= 28 && label_counter <= 35 ?
3719 MICROLABEL_IMPORTED_FROM :
3720 label_counter >= 37 && label_counter <= 40 ?
3721 MICROLABEL_IMPORTED_BY_HEAD :
3722 label_counter >= 42 && label_counter <= 49 ?
3723 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3725 if (leveldir_current->imported_from == NULL &&
3726 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3727 label_state == MICROLABEL_IMPORTED_FROM))
3728 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3729 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3731 DrawPreviewLevelLabel(label_state);
3735 void DrawPreviewPlayers(void)
3737 if (game_status != GAME_MODE_MAIN)
3740 // do not draw preview players if level preview redefined, but players aren't
3741 if (preview.redefined && !menu.main.preview_players.redefined)
3744 boolean player_found[MAX_PLAYERS];
3745 int num_players = 0;
3748 for (i = 0; i < MAX_PLAYERS; i++)
3749 player_found[i] = FALSE;
3751 // check which players can be found in the level (simple approach)
3752 for (x = 0; x < lev_fieldx; x++)
3754 for (y = 0; y < lev_fieldy; y++)
3756 int element = level.field[x][y];
3758 if (IS_PLAYER_ELEMENT(element))
3760 int player_nr = GET_PLAYER_NR(element);
3762 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3764 if (!player_found[player_nr])
3767 player_found[player_nr] = TRUE;
3772 struct TextPosInfo *pos = &menu.main.preview_players;
3773 int tile_size = pos->tile_size;
3774 int border_size = pos->border_size;
3775 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3776 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3777 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3778 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3779 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3780 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3781 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3782 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3783 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3784 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3785 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3786 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3788 // clear area in which the players will be drawn
3789 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3790 max_players_width, max_players_height);
3792 if (!network.enabled && !setup.team_mode)
3795 // only draw players if level is suited for team mode
3796 if (num_players < 2)
3799 // draw all players that were found in the level
3800 for (i = 0; i < MAX_PLAYERS; i++)
3802 if (player_found[i])
3804 int graphic = el2img(EL_PLAYER_1 + i);
3806 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3808 xpos += player_xoffset;
3809 ypos += player_yoffset;
3814 void DrawPreviewLevelInitial(void)
3816 DrawPreviewLevelExt(TRUE);
3817 DrawPreviewPlayers();
3820 void DrawPreviewLevelAnimation(void)
3822 DrawPreviewLevelExt(FALSE);
3825 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3826 int border_size, int font_nr)
3828 int graphic = el2img(EL_PLAYER_1 + player_nr);
3829 int font_height = getFontHeight(font_nr);
3830 int player_height = MAX(tile_size, font_height);
3831 int xoffset_text = tile_size + border_size;
3832 int yoffset_text = (player_height - font_height) / 2;
3833 int yoffset_graphic = (player_height - tile_size) / 2;
3834 char *player_name = getNetworkPlayerName(player_nr + 1);
3836 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3838 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3841 static void DrawNetworkPlayersExt(boolean force)
3843 if (game_status != GAME_MODE_MAIN)
3846 if (!network.connected && !force)
3849 // do not draw network players if level preview redefined, but players aren't
3850 if (preview.redefined && !menu.main.network_players.redefined)
3853 int num_players = 0;
3856 for (i = 0; i < MAX_PLAYERS; i++)
3857 if (stored_player[i].connected_network)
3860 struct TextPosInfo *pos = &menu.main.network_players;
3861 int tile_size = pos->tile_size;
3862 int border_size = pos->border_size;
3863 int xoffset_text = tile_size + border_size;
3864 int font_nr = pos->font;
3865 int font_width = getFontWidth(font_nr);
3866 int font_height = getFontHeight(font_nr);
3867 int player_height = MAX(tile_size, font_height);
3868 int player_yoffset = player_height + border_size;
3869 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3870 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3871 int all_players_height = num_players * player_yoffset - border_size;
3872 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3873 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3874 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3876 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3877 max_players_width, max_players_height);
3879 // first draw local network player ...
3880 for (i = 0; i < MAX_PLAYERS; i++)
3882 if (stored_player[i].connected_network &&
3883 stored_player[i].connected_locally)
3885 char *player_name = getNetworkPlayerName(i + 1);
3886 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3887 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3889 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3891 ypos += player_yoffset;
3895 // ... then draw all other network players
3896 for (i = 0; i < MAX_PLAYERS; i++)
3898 if (stored_player[i].connected_network &&
3899 !stored_player[i].connected_locally)
3901 char *player_name = getNetworkPlayerName(i + 1);
3902 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3903 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3905 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3907 ypos += player_yoffset;
3912 void DrawNetworkPlayers(void)
3914 DrawNetworkPlayersExt(FALSE);
3917 void ClearNetworkPlayers(void)
3919 DrawNetworkPlayersExt(TRUE);
3922 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3923 int graphic, int lx, int ly,
3926 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3928 if (mask_mode == USE_MASKING)
3929 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3931 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3934 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3935 int graphic, int sync_frame, int mask_mode)
3937 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3939 if (mask_mode == USE_MASKING)
3940 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3942 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3945 static void DrawGraphicAnimation(int x, int y, int graphic)
3947 int lx = LEVELX(x), ly = LEVELY(y);
3948 int mask_mode = NO_MASKING;
3950 if (!IN_SCR_FIELD(x, y))
3953 if (game.use_masked_elements)
3955 if (Tile[lx][ly] != EL_EMPTY)
3957 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3959 mask_mode = USE_MASKING;
3963 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3964 graphic, lx, ly, mask_mode);
3966 MarkTileDirty(x, y);
3969 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3971 int lx = LEVELX(x), ly = LEVELY(y);
3972 int mask_mode = NO_MASKING;
3974 if (!IN_SCR_FIELD(x, y))
3977 if (game.use_masked_elements)
3979 if (Tile[lx][ly] != EL_EMPTY)
3981 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3983 mask_mode = USE_MASKING;
3987 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3988 graphic, lx, ly, mask_mode);
3990 MarkTileDirty(x, y);
3993 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3995 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3998 void DrawLevelElementAnimation(int x, int y, int element)
4000 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4002 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4005 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
4007 int sx = SCREENX(x), sy = SCREENY(y);
4009 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4012 if (Tile[x][y] == EL_EMPTY)
4013 graphic = el2img(GfxElementEmpty[x][y]);
4015 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4018 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4021 DrawGraphicAnimation(sx, sy, graphic);
4024 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4025 DrawLevelFieldCrumbled(x, y);
4027 if (GFX_CRUMBLED(Tile[x][y]))
4028 DrawLevelFieldCrumbled(x, y);
4032 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4034 int sx = SCREENX(x), sy = SCREENY(y);
4037 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4040 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4042 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4045 DrawGraphicAnimation(sx, sy, graphic);
4047 if (GFX_CRUMBLED(element))
4048 DrawLevelFieldCrumbled(x, y);
4051 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4053 if (player->use_murphy)
4055 // this works only because currently only one player can be "murphy" ...
4056 static int last_horizontal_dir = MV_LEFT;
4057 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4059 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4060 last_horizontal_dir = move_dir;
4062 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4064 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4066 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4072 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
4075 static boolean equalGraphics(int graphic1, int graphic2)
4077 struct GraphicInfo *g1 = &graphic_info[graphic1];
4078 struct GraphicInfo *g2 = &graphic_info[graphic2];
4080 return (g1->bitmap == g2->bitmap &&
4081 g1->src_x == g2->src_x &&
4082 g1->src_y == g2->src_y &&
4083 g1->anim_frames == g2->anim_frames &&
4084 g1->anim_delay == g2->anim_delay &&
4085 g1->anim_mode == g2->anim_mode);
4088 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4092 DRAW_PLAYER_STAGE_INIT = 0,
4093 DRAW_PLAYER_STAGE_LAST_FIELD,
4094 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4095 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4096 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4097 DRAW_PLAYER_STAGE_PLAYER,
4099 DRAW_PLAYER_STAGE_PLAYER,
4100 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4102 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4103 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4105 NUM_DRAW_PLAYER_STAGES
4108 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4110 static int static_last_player_graphic[MAX_PLAYERS];
4111 static int static_last_player_frame[MAX_PLAYERS];
4112 static boolean static_player_is_opaque[MAX_PLAYERS];
4113 static boolean draw_player[MAX_PLAYERS];
4114 int pnr = player->index_nr;
4116 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4118 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4119 static_last_player_frame[pnr] = player->Frame;
4120 static_player_is_opaque[pnr] = FALSE;
4122 draw_player[pnr] = TRUE;
4125 if (!draw_player[pnr])
4129 if (!IN_LEV_FIELD(player->jx, player->jy))
4131 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4132 Debug("draw:DrawPlayerExt", "This should never happen!");
4134 draw_player[pnr] = FALSE;
4140 int last_player_graphic = static_last_player_graphic[pnr];
4141 int last_player_frame = static_last_player_frame[pnr];
4142 boolean player_is_opaque = static_player_is_opaque[pnr];
4144 int jx = player->jx;
4145 int jy = player->jy;
4146 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4147 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4148 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4149 int last_jx = (player->is_moving ? jx - dx : jx);
4150 int last_jy = (player->is_moving ? jy - dy : jy);
4151 int next_jx = jx + dx;
4152 int next_jy = jy + dy;
4153 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4154 int sx = SCREENX(jx);
4155 int sy = SCREENY(jy);
4156 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4157 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4158 int element = Tile[jx][jy];
4159 int last_element = Tile[last_jx][last_jy];
4160 int action = (player->is_pushing ? ACTION_PUSHING :
4161 player->is_digging ? ACTION_DIGGING :
4162 player->is_collecting ? ACTION_COLLECTING :
4163 player->is_moving ? ACTION_MOVING :
4164 player->is_snapping ? ACTION_SNAPPING :
4165 player->is_dropping ? ACTION_DROPPING :
4166 player->is_waiting ? player->action_waiting :
4169 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4171 // ------------------------------------------------------------------------
4172 // initialize drawing the player
4173 // ------------------------------------------------------------------------
4175 draw_player[pnr] = FALSE;
4177 // GfxElement[][] is set to the element the player is digging or collecting;
4178 // remove also for off-screen player if the player is not moving anymore
4179 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4180 GfxElement[jx][jy] = EL_UNDEFINED;
4182 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4185 if (element == EL_EXPLOSION)
4188 InitPlayerGfxAnimation(player, action, move_dir);
4190 draw_player[pnr] = TRUE;
4192 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4194 // ------------------------------------------------------------------------
4195 // draw things in the field the player is leaving, if needed
4196 // ------------------------------------------------------------------------
4198 if (!IN_SCR_FIELD(sx, sy))
4199 draw_player[pnr] = FALSE;
4201 if (!player->is_moving)
4204 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4206 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4208 if (last_element == EL_DYNAMITE_ACTIVE ||
4209 last_element == EL_EM_DYNAMITE_ACTIVE ||
4210 last_element == EL_SP_DISK_RED_ACTIVE)
4211 DrawDynamite(last_jx, last_jy);
4213 DrawLevelFieldThruMask(last_jx, last_jy);
4215 else if (last_element == EL_DYNAMITE_ACTIVE ||
4216 last_element == EL_EM_DYNAMITE_ACTIVE ||
4217 last_element == EL_SP_DISK_RED_ACTIVE)
4218 DrawDynamite(last_jx, last_jy);
4220 DrawLevelField(last_jx, last_jy);
4222 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4224 // ------------------------------------------------------------------------
4225 // draw things behind the player, if needed
4226 // ------------------------------------------------------------------------
4230 DrawLevelElement(jx, jy, Back[jx][jy]);
4235 if (IS_ACTIVE_BOMB(element))
4237 DrawLevelElement(jx, jy, EL_EMPTY);
4242 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4244 int old_element = GfxElement[jx][jy];
4245 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4246 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4248 if (GFX_CRUMBLED(old_element))
4249 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4251 DrawScreenGraphic(sx, sy, old_graphic, frame);
4253 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4254 static_player_is_opaque[pnr] = TRUE;
4258 GfxElement[jx][jy] = EL_UNDEFINED;
4260 // make sure that pushed elements are drawn with correct frame rate
4261 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4263 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4264 GfxFrame[jx][jy] = player->StepFrame;
4266 DrawLevelField(jx, jy);
4269 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4271 // ------------------------------------------------------------------------
4272 // draw things the player is pushing, if needed
4273 // ------------------------------------------------------------------------
4275 if (!player->is_pushing || !player->is_moving)
4278 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4281 int gfx_frame = GfxFrame[jx][jy];
4283 if (!IS_MOVING(jx, jy)) // push movement already finished
4285 element = Tile[next_jx][next_jy];
4286 gfx_frame = GfxFrame[next_jx][next_jy];
4289 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4290 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4291 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4293 // draw background element under pushed element (like the Sokoban field)
4294 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4296 // this allows transparent pushing animation over non-black background
4299 DrawLevelElement(jx, jy, Back[jx][jy]);
4301 DrawLevelElement(jx, jy, EL_EMPTY);
4304 if (Back[next_jx][next_jy])
4305 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4307 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4309 int px = SCREENX(jx), py = SCREENY(jy);
4310 int pxx = (TILEX - ABS(sxx)) * dx;
4311 int pyy = (TILEY - ABS(syy)) * dy;
4314 // do not draw (EM style) pushing animation when pushing is finished
4315 // (two-tile animations usually do not contain start and end frame)
4316 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4317 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4319 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4321 // masked drawing is needed for EMC style (double) movement graphics
4322 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4323 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4326 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4328 // ------------------------------------------------------------------------
4329 // draw player himself
4330 // ------------------------------------------------------------------------
4332 int graphic = getPlayerGraphic(player, move_dir);
4334 // in the case of changed player action or direction, prevent the current
4335 // animation frame from being restarted for identical animations
4336 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4337 player->Frame = last_player_frame;
4339 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4341 if (player_is_opaque)
4342 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4344 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4346 if (SHIELD_ON(player))
4348 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4349 IMG_SHIELD_NORMAL_ACTIVE);
4350 frame = getGraphicAnimationFrame(graphic, -1);
4352 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4355 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4357 // ------------------------------------------------------------------------
4358 // draw things in front of player (active dynamite or dynabombs)
4359 // ------------------------------------------------------------------------
4361 if (IS_ACTIVE_BOMB(element))
4363 int graphic = el2img(element);
4364 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4366 if (game.emulation == EMU_SUPAPLEX)
4367 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4369 DrawGraphicThruMask(sx, sy, graphic, frame);
4372 if (player_is_moving && last_element == EL_EXPLOSION)
4374 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4375 GfxElement[last_jx][last_jy] : EL_EMPTY);
4376 int graphic = el_act2img(element, ACTION_EXPLODING);
4377 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4378 int phase = ExplodePhase[last_jx][last_jy] - 1;
4379 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4382 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4385 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4387 // ------------------------------------------------------------------------
4388 // draw elements the player is just walking/passing through/under
4389 // ------------------------------------------------------------------------
4391 if (player_is_moving)
4393 // handle the field the player is leaving ...
4394 if (IS_ACCESSIBLE_INSIDE(last_element))
4395 DrawLevelField(last_jx, last_jy);
4396 else if (IS_ACCESSIBLE_UNDER(last_element))
4397 DrawLevelFieldThruMask(last_jx, last_jy);
4400 // do not redraw accessible elements if the player is just pushing them
4401 if (!player_is_moving || !player->is_pushing)
4403 // ... and the field the player is entering
4404 if (IS_ACCESSIBLE_INSIDE(element))
4405 DrawLevelField(jx, jy);
4406 else if (IS_ACCESSIBLE_UNDER(element))
4407 DrawLevelFieldThruMask(jx, jy);
4410 MarkTileDirty(sx, sy);
4414 void DrawPlayer(struct PlayerInfo *player)
4418 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4419 DrawPlayerExt(player, i);
4422 void DrawAllPlayers(void)
4426 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4427 for (j = 0; j < MAX_PLAYERS; j++)
4428 if (stored_player[j].active)
4429 DrawPlayerExt(&stored_player[j], i);
4432 void DrawPlayerField(int x, int y)
4434 if (!IS_PLAYER(x, y))
4437 DrawPlayer(PLAYERINFO(x, y));
4440 // ----------------------------------------------------------------------------
4442 void WaitForEventToContinue(void)
4444 boolean first_wait = TRUE;
4445 boolean still_wait = TRUE;
4447 if (program.headless)
4450 // simulate releasing mouse button over last gadget, if still pressed
4452 HandleGadgets(-1, -1, 0);
4454 button_status = MB_RELEASED;
4457 ClearPlayerAction();
4463 if (NextValidEvent(&event))
4467 case EVENT_BUTTONPRESS:
4468 case EVENT_FINGERPRESS:
4472 case EVENT_BUTTONRELEASE:
4473 case EVENT_FINGERRELEASE:
4474 still_wait = first_wait;
4477 case EVENT_KEYPRESS:
4478 case SDL_CONTROLLERBUTTONDOWN:
4479 case SDL_JOYBUTTONDOWN:
4484 HandleOtherEvents(&event);
4488 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4493 if (!PendingEvent())
4498 #define MAX_REQUEST_LINES 13
4499 #define MAX_REQUEST_LINE_FONT1_LEN 7
4500 #define MAX_REQUEST_LINE_FONT2_LEN 10
4502 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4504 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4506 int draw_buffer_last = GetDrawtoField();
4507 int width = request.width;
4508 int height = request.height;
4512 // when showing request dialog after game ended, deactivate game panel
4513 if (game_just_ended)
4514 game.panel.active = FALSE;
4516 game.request_active = TRUE;
4518 setRequestPosition(&sx, &sy, FALSE);
4520 button_status = MB_RELEASED;
4522 request_gadget_id = -1;
4527 boolean event_handled = FALSE;
4529 if (game_just_ended)
4531 SetDrawtoField(draw_buffer_game);
4533 HandleGameActions();
4535 SetDrawtoField(DRAW_TO_BACKBUFFER);
4537 if (global.use_envelope_request)
4539 // copy current state of request area to middle of playfield area
4540 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4548 while (NextValidEvent(&event))
4550 event_handled = TRUE;
4554 case EVENT_BUTTONPRESS:
4555 case EVENT_BUTTONRELEASE:
4556 case EVENT_MOTIONNOTIFY:
4560 if (event.type == EVENT_MOTIONNOTIFY)
4565 motion_status = TRUE;
4566 mx = ((MotionEvent *) &event)->x;
4567 my = ((MotionEvent *) &event)->y;
4571 motion_status = FALSE;
4572 mx = ((ButtonEvent *) &event)->x;
4573 my = ((ButtonEvent *) &event)->y;
4574 if (event.type == EVENT_BUTTONPRESS)
4575 button_status = ((ButtonEvent *) &event)->button;
4577 button_status = MB_RELEASED;
4580 // this sets 'request_gadget_id'
4581 HandleGadgets(mx, my, button_status);
4583 switch (request_gadget_id)
4585 case TOOL_CTRL_ID_YES:
4586 case TOOL_CTRL_ID_TOUCH_YES:
4589 case TOOL_CTRL_ID_NO:
4590 case TOOL_CTRL_ID_TOUCH_NO:
4593 case TOOL_CTRL_ID_CONFIRM:
4594 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4595 result = TRUE | FALSE;
4598 case TOOL_CTRL_ID_PLAYER_1:
4601 case TOOL_CTRL_ID_PLAYER_2:
4604 case TOOL_CTRL_ID_PLAYER_3:
4607 case TOOL_CTRL_ID_PLAYER_4:
4612 // only check clickable animations if no request gadget clicked
4613 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4620 case SDL_WINDOWEVENT:
4621 HandleWindowEvent((WindowEvent *) &event);
4624 case SDL_APP_WILLENTERBACKGROUND:
4625 case SDL_APP_DIDENTERBACKGROUND:
4626 case SDL_APP_WILLENTERFOREGROUND:
4627 case SDL_APP_DIDENTERFOREGROUND:
4628 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4631 case EVENT_KEYPRESS:
4633 Key key = GetEventKey((KeyEvent *)&event);
4638 if (req_state & REQ_CONFIRM)
4647 #if defined(KSYM_Rewind)
4648 case KSYM_Rewind: // for Amazon Fire TV remote
4657 #if defined(KSYM_FastForward)
4658 case KSYM_FastForward: // for Amazon Fire TV remote
4664 HandleKeysDebug(key, KEY_PRESSED);
4668 if (req_state & REQ_PLAYER)
4670 int old_player_nr = setup.network_player_nr;
4673 result = old_player_nr + 1;
4678 result = old_player_nr + 1;
4709 case EVENT_FINGERRELEASE:
4710 case EVENT_KEYRELEASE:
4711 ClearPlayerAction();
4714 case SDL_CONTROLLERBUTTONDOWN:
4715 switch (event.cbutton.button)
4717 case SDL_CONTROLLER_BUTTON_A:
4718 case SDL_CONTROLLER_BUTTON_X:
4719 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4720 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4724 case SDL_CONTROLLER_BUTTON_B:
4725 case SDL_CONTROLLER_BUTTON_Y:
4726 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4727 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4728 case SDL_CONTROLLER_BUTTON_BACK:
4733 if (req_state & REQ_PLAYER)
4735 int old_player_nr = setup.network_player_nr;
4738 result = old_player_nr + 1;
4740 switch (event.cbutton.button)
4742 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4743 case SDL_CONTROLLER_BUTTON_Y:
4747 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4748 case SDL_CONTROLLER_BUTTON_B:
4752 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4753 case SDL_CONTROLLER_BUTTON_A:
4757 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4758 case SDL_CONTROLLER_BUTTON_X:
4769 case SDL_CONTROLLERBUTTONUP:
4770 HandleJoystickEvent(&event);
4771 ClearPlayerAction();
4775 HandleOtherEvents(&event);
4780 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4782 int joy = AnyJoystick();
4784 if (joy & JOY_BUTTON_1)
4786 else if (joy & JOY_BUTTON_2)
4789 else if (AnyJoystick())
4791 int joy = AnyJoystick();
4793 if (req_state & REQ_PLAYER)
4797 else if (joy & JOY_RIGHT)
4799 else if (joy & JOY_DOWN)
4801 else if (joy & JOY_LEFT)
4808 if (game_just_ended)
4810 if (global.use_envelope_request)
4812 // copy back current state of pressed buttons inside request area
4813 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4817 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4823 SetDrawtoField(draw_buffer_last);
4825 game.request_active = FALSE;
4830 static boolean RequestDoor(char *text, unsigned int req_state)
4832 int draw_buffer_last = GetDrawtoField();
4833 unsigned int old_door_state;
4834 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4835 int font_nr = FONT_TEXT_2;
4840 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4842 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4843 font_nr = FONT_TEXT_1;
4846 if (game_status == GAME_MODE_PLAYING)
4847 BlitScreenToBitmap(backbuffer);
4849 // disable deactivated drawing when quick-loading level tape recording
4850 if (tape.playing && tape.deactivate_display)
4851 TapeDeactivateDisplayOff(TRUE);
4853 SetMouseCursor(CURSOR_DEFAULT);
4855 // pause network game while waiting for request to answer
4856 if (network.enabled &&
4857 game_status == GAME_MODE_PLAYING &&
4858 !game.all_players_gone &&
4859 req_state & REQUEST_WAIT_FOR_INPUT)
4860 SendToServer_PausePlaying();
4862 old_door_state = GetDoorState();
4864 // simulate releasing mouse button over last gadget, if still pressed
4866 HandleGadgets(-1, -1, 0);
4870 // draw released gadget before proceeding
4873 if (old_door_state & DOOR_OPEN_1)
4875 CloseDoor(DOOR_CLOSE_1);
4877 // save old door content
4878 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4879 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4882 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4883 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4885 // clear door drawing field
4886 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4888 // force DOOR font inside door area
4889 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4891 // write text for request
4892 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4894 char text_line[max_request_line_len + 1];
4900 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4902 tc = *(text_ptr + tx);
4903 // if (!tc || tc == ' ')
4904 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4908 if ((tc == '?' || tc == '!') && tl == 0)
4918 strncpy(text_line, text_ptr, tl);
4921 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4922 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4923 text_line, font_nr);
4925 text_ptr += tl + (tc == ' ' ? 1 : 0);
4926 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4931 if (req_state & REQ_ASK)
4933 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4934 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4935 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4936 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4938 else if (req_state & REQ_CONFIRM)
4940 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4941 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4943 else if (req_state & REQ_PLAYER)
4945 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4946 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4947 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4948 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4951 // copy request gadgets to door backbuffer
4952 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4954 OpenDoor(DOOR_OPEN_1);
4956 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4958 if (game_status == GAME_MODE_PLAYING)
4960 SetPanelBackground();
4961 SetDrawBackgroundMask(REDRAW_DOOR_1);
4965 SetDrawBackgroundMask(REDRAW_FIELD);
4971 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4973 // ---------- handle request buttons ----------
4974 result = RequestHandleEvents(req_state, draw_buffer_last);
4978 if (!(req_state & REQ_STAY_OPEN))
4980 CloseDoor(DOOR_CLOSE_1);
4982 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4983 (req_state & REQ_REOPEN))
4984 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4989 if (game_status == GAME_MODE_PLAYING)
4991 SetPanelBackground();
4992 SetDrawBackgroundMask(REDRAW_DOOR_1);
4996 SetDrawBackgroundMask(REDRAW_FIELD);
4999 // continue network game after request
5000 if (network.enabled &&
5001 game_status == GAME_MODE_PLAYING &&
5002 !game.all_players_gone &&
5003 req_state & REQUEST_WAIT_FOR_INPUT)
5004 SendToServer_ContinuePlaying();
5006 // restore deactivated drawing when quick-loading level tape recording
5007 if (tape.playing && tape.deactivate_display)
5008 TapeDeactivateDisplayOn();
5013 static boolean RequestEnvelope(char *text, unsigned int req_state)
5015 int draw_buffer_last = GetDrawtoField();
5018 if (game_status == GAME_MODE_PLAYING)
5019 BlitScreenToBitmap(backbuffer);
5021 // disable deactivated drawing when quick-loading level tape recording
5022 if (tape.playing && tape.deactivate_display)
5023 TapeDeactivateDisplayOff(TRUE);
5025 SetMouseCursor(CURSOR_DEFAULT);
5027 // pause network game while waiting for request to answer
5028 if (network.enabled &&
5029 game_status == GAME_MODE_PLAYING &&
5030 !game.all_players_gone &&
5031 req_state & REQUEST_WAIT_FOR_INPUT)
5032 SendToServer_PausePlaying();
5034 // simulate releasing mouse button over last gadget, if still pressed
5036 HandleGadgets(-1, -1, 0);
5040 // (replace with setting corresponding request background)
5041 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5042 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5044 // clear door drawing field
5045 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5047 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5049 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5051 if (game_status == GAME_MODE_PLAYING)
5053 SetPanelBackground();
5054 SetDrawBackgroundMask(REDRAW_DOOR_1);
5058 SetDrawBackgroundMask(REDRAW_FIELD);
5064 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5066 // ---------- handle request buttons ----------
5067 result = RequestHandleEvents(req_state, draw_buffer_last);
5071 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5075 if (game_status == GAME_MODE_PLAYING)
5077 SetPanelBackground();
5078 SetDrawBackgroundMask(REDRAW_DOOR_1);
5082 SetDrawBackgroundMask(REDRAW_FIELD);
5085 // continue network game after request
5086 if (network.enabled &&
5087 game_status == GAME_MODE_PLAYING &&
5088 !game.all_players_gone &&
5089 req_state & REQUEST_WAIT_FOR_INPUT)
5090 SendToServer_ContinuePlaying();
5092 // restore deactivated drawing when quick-loading level tape recording
5093 if (tape.playing && tape.deactivate_display)
5094 TapeDeactivateDisplayOn();
5099 boolean Request(char *text, unsigned int req_state)
5101 boolean overlay_enabled = GetOverlayEnabled();
5104 game.request_active_or_moving = TRUE;
5106 SetOverlayEnabled(FALSE);
5108 if (global.use_envelope_request)
5109 result = RequestEnvelope(text, req_state);
5111 result = RequestDoor(text, req_state);
5113 SetOverlayEnabled(overlay_enabled);
5115 game.request_active_or_moving = FALSE;
5120 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5122 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5123 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5126 if (dpo1->sort_priority != dpo2->sort_priority)
5127 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5129 compare_result = dpo1->nr - dpo2->nr;
5131 return compare_result;
5134 void InitGraphicCompatibilityInfo_Doors(void)
5140 struct DoorInfo *door;
5144 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5145 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5147 { -1, -1, -1, NULL }
5149 struct Rect door_rect_list[] =
5151 { DX, DY, DXSIZE, DYSIZE },
5152 { VX, VY, VXSIZE, VYSIZE }
5156 for (i = 0; doors[i].door_token != -1; i++)
5158 int door_token = doors[i].door_token;
5159 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5160 int part_1 = doors[i].part_1;
5161 int part_8 = doors[i].part_8;
5162 int part_2 = part_1 + 1;
5163 int part_3 = part_1 + 2;
5164 struct DoorInfo *door = doors[i].door;
5165 struct Rect *door_rect = &door_rect_list[door_index];
5166 boolean door_gfx_redefined = FALSE;
5168 // check if any door part graphic definitions have been redefined
5170 for (j = 0; door_part_controls[j].door_token != -1; j++)
5172 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5173 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5175 if (dpc->door_token == door_token && fi->redefined)
5176 door_gfx_redefined = TRUE;
5179 // check for old-style door graphic/animation modifications
5181 if (!door_gfx_redefined)
5183 if (door->anim_mode & ANIM_STATIC_PANEL)
5185 door->panel.step_xoffset = 0;
5186 door->panel.step_yoffset = 0;
5189 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5191 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5192 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5193 int num_door_steps, num_panel_steps;
5195 // remove door part graphics other than the two default wings
5197 for (j = 0; door_part_controls[j].door_token != -1; j++)
5199 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5200 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5202 if (dpc->graphic >= part_3 &&
5203 dpc->graphic <= part_8)
5207 // set graphics and screen positions of the default wings
5209 g_part_1->width = door_rect->width;
5210 g_part_1->height = door_rect->height;
5211 g_part_2->width = door_rect->width;
5212 g_part_2->height = door_rect->height;
5213 g_part_2->src_x = door_rect->width;
5214 g_part_2->src_y = g_part_1->src_y;
5216 door->part_2.x = door->part_1.x;
5217 door->part_2.y = door->part_1.y;
5219 if (door->width != -1)
5221 g_part_1->width = door->width;
5222 g_part_2->width = door->width;
5224 // special treatment for graphics and screen position of right wing
5225 g_part_2->src_x += door_rect->width - door->width;
5226 door->part_2.x += door_rect->width - door->width;
5229 if (door->height != -1)
5231 g_part_1->height = door->height;
5232 g_part_2->height = door->height;
5234 // special treatment for graphics and screen position of bottom wing
5235 g_part_2->src_y += door_rect->height - door->height;
5236 door->part_2.y += door_rect->height - door->height;
5239 // set animation delays for the default wings and panels
5241 door->part_1.step_delay = door->step_delay;
5242 door->part_2.step_delay = door->step_delay;
5243 door->panel.step_delay = door->step_delay;
5245 // set animation draw order for the default wings
5247 door->part_1.sort_priority = 2; // draw left wing over ...
5248 door->part_2.sort_priority = 1; // ... right wing
5250 // set animation draw offset for the default wings
5252 if (door->anim_mode & ANIM_HORIZONTAL)
5254 door->part_1.step_xoffset = door->step_offset;
5255 door->part_1.step_yoffset = 0;
5256 door->part_2.step_xoffset = door->step_offset * -1;
5257 door->part_2.step_yoffset = 0;
5259 num_door_steps = g_part_1->width / door->step_offset;
5261 else // ANIM_VERTICAL
5263 door->part_1.step_xoffset = 0;
5264 door->part_1.step_yoffset = door->step_offset;
5265 door->part_2.step_xoffset = 0;
5266 door->part_2.step_yoffset = door->step_offset * -1;
5268 num_door_steps = g_part_1->height / door->step_offset;
5271 // set animation draw offset for the default panels
5273 if (door->step_offset > 1)
5275 num_panel_steps = 2 * door_rect->height / door->step_offset;
5276 door->panel.start_step = num_panel_steps - num_door_steps;
5277 door->panel.start_step_closing = door->panel.start_step;
5281 num_panel_steps = door_rect->height / door->step_offset;
5282 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5283 door->panel.start_step_closing = door->panel.start_step;
5284 door->panel.step_delay *= 2;
5291 void InitDoors(void)
5295 for (i = 0; door_part_controls[i].door_token != -1; i++)
5297 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5298 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5300 // initialize "start_step_opening" and "start_step_closing", if needed
5301 if (dpc->pos->start_step_opening == 0 &&
5302 dpc->pos->start_step_closing == 0)
5304 // dpc->pos->start_step_opening = dpc->pos->start_step;
5305 dpc->pos->start_step_closing = dpc->pos->start_step;
5308 // fill structure for door part draw order (sorted below)
5310 dpo->sort_priority = dpc->pos->sort_priority;
5313 // sort door part controls according to sort_priority and graphic number
5314 qsort(door_part_order, MAX_DOOR_PARTS,
5315 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5318 unsigned int OpenDoor(unsigned int door_state)
5320 if (door_state & DOOR_COPY_BACK)
5322 if (door_state & DOOR_OPEN_1)
5323 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5324 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5326 if (door_state & DOOR_OPEN_2)
5327 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5328 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5330 door_state &= ~DOOR_COPY_BACK;
5333 return MoveDoor(door_state);
5336 unsigned int CloseDoor(unsigned int door_state)
5338 unsigned int old_door_state = GetDoorState();
5340 if (!(door_state & DOOR_NO_COPY_BACK))
5342 if (old_door_state & DOOR_OPEN_1)
5343 BlitBitmap(backbuffer, bitmap_db_door_1,
5344 DX, DY, DXSIZE, DYSIZE, 0, 0);
5346 if (old_door_state & DOOR_OPEN_2)
5347 BlitBitmap(backbuffer, bitmap_db_door_2,
5348 VX, VY, VXSIZE, VYSIZE, 0, 0);
5350 door_state &= ~DOOR_NO_COPY_BACK;
5353 return MoveDoor(door_state);
5356 unsigned int GetDoorState(void)
5358 return MoveDoor(DOOR_GET_STATE);
5361 unsigned int SetDoorState(unsigned int door_state)
5363 return MoveDoor(door_state | DOOR_SET_STATE);
5366 static int euclid(int a, int b)
5368 return (b ? euclid(b, a % b) : a);
5371 unsigned int MoveDoor(unsigned int door_state)
5373 struct Rect door_rect_list[] =
5375 { DX, DY, DXSIZE, DYSIZE },
5376 { VX, VY, VXSIZE, VYSIZE }
5378 static int door1 = DOOR_CLOSE_1;
5379 static int door2 = DOOR_CLOSE_2;
5380 DelayCounter door_delay = { 0 };
5383 if (door_state == DOOR_GET_STATE)
5384 return (door1 | door2);
5386 if (door_state & DOOR_SET_STATE)
5388 if (door_state & DOOR_ACTION_1)
5389 door1 = door_state & DOOR_ACTION_1;
5390 if (door_state & DOOR_ACTION_2)
5391 door2 = door_state & DOOR_ACTION_2;
5393 return (door1 | door2);
5396 if (!(door_state & DOOR_FORCE_REDRAW))
5398 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5399 door_state &= ~DOOR_OPEN_1;
5400 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5401 door_state &= ~DOOR_CLOSE_1;
5402 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5403 door_state &= ~DOOR_OPEN_2;
5404 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5405 door_state &= ~DOOR_CLOSE_2;
5408 if (global.autoplay_leveldir)
5410 door_state |= DOOR_NO_DELAY;
5411 door_state &= ~DOOR_CLOSE_ALL;
5414 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5415 door_state |= DOOR_NO_DELAY;
5417 if (door_state & DOOR_ACTION)
5419 boolean door_panel_drawn[NUM_DOORS];
5420 boolean panel_has_doors[NUM_DOORS];
5421 boolean door_part_skip[MAX_DOOR_PARTS];
5422 boolean door_part_done[MAX_DOOR_PARTS];
5423 boolean door_part_done_all;
5424 int num_steps[MAX_DOOR_PARTS];
5425 int max_move_delay = 0; // delay for complete animations of all doors
5426 int max_step_delay = 0; // delay (ms) between two animation frames
5427 int num_move_steps = 0; // number of animation steps for all doors
5428 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5429 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5433 for (i = 0; i < NUM_DOORS; i++)
5434 panel_has_doors[i] = FALSE;
5436 for (i = 0; i < MAX_DOOR_PARTS; i++)
5438 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5439 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5440 int door_token = dpc->door_token;
5442 door_part_done[i] = FALSE;
5443 door_part_skip[i] = (!(door_state & door_token) ||
5447 for (i = 0; i < MAX_DOOR_PARTS; i++)
5449 int nr = door_part_order[i].nr;
5450 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5451 struct DoorPartPosInfo *pos = dpc->pos;
5452 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5453 int door_token = dpc->door_token;
5454 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5455 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5456 int step_xoffset = ABS(pos->step_xoffset);
5457 int step_yoffset = ABS(pos->step_yoffset);
5458 int step_delay = pos->step_delay;
5459 int current_door_state = door_state & door_token;
5460 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5461 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5462 boolean part_opening = (is_panel ? door_closing : door_opening);
5463 int start_step = (part_opening ? pos->start_step_opening :
5464 pos->start_step_closing);
5465 float move_xsize = (step_xoffset ? g->width : 0);
5466 float move_ysize = (step_yoffset ? g->height : 0);
5467 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5468 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5469 int move_steps = (move_xsteps && move_ysteps ?
5470 MIN(move_xsteps, move_ysteps) :
5471 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5472 int move_delay = move_steps * step_delay;
5474 if (door_part_skip[nr])
5477 max_move_delay = MAX(max_move_delay, move_delay);
5478 max_step_delay = (max_step_delay == 0 ? step_delay :
5479 euclid(max_step_delay, step_delay));
5480 num_steps[nr] = move_steps;
5484 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5486 panel_has_doors[door_index] = TRUE;
5490 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5492 num_move_steps = max_move_delay / max_step_delay;
5493 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5495 door_delay.value = max_step_delay;
5497 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5499 start = num_move_steps - 1;
5503 // opening door sound has priority over simultaneously closing door
5504 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5506 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5508 if (door_state & DOOR_OPEN_1)
5509 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5510 if (door_state & DOOR_OPEN_2)
5511 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5513 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5515 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5517 if (door_state & DOOR_CLOSE_1)
5518 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5519 if (door_state & DOOR_CLOSE_2)
5520 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5524 for (k = start; k < num_move_steps; k++)
5526 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5528 door_part_done_all = TRUE;
5530 for (i = 0; i < NUM_DOORS; i++)
5531 door_panel_drawn[i] = FALSE;
5533 for (i = 0; i < MAX_DOOR_PARTS; i++)
5535 int nr = door_part_order[i].nr;
5536 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5537 struct DoorPartPosInfo *pos = dpc->pos;
5538 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5539 int door_token = dpc->door_token;
5540 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5541 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5542 boolean is_panel_and_door_has_closed = FALSE;
5543 struct Rect *door_rect = &door_rect_list[door_index];
5544 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5546 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5547 int current_door_state = door_state & door_token;
5548 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5549 boolean door_closing = !door_opening;
5550 boolean part_opening = (is_panel ? door_closing : door_opening);
5551 boolean part_closing = !part_opening;
5552 int start_step = (part_opening ? pos->start_step_opening :
5553 pos->start_step_closing);
5554 int step_delay = pos->step_delay;
5555 int step_factor = step_delay / max_step_delay;
5556 int k1 = (step_factor ? k / step_factor + 1 : k);
5557 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5558 int kk = MAX(0, k2);
5561 int src_x, src_y, src_xx, src_yy;
5562 int dst_x, dst_y, dst_xx, dst_yy;
5565 if (door_part_skip[nr])
5568 if (!(door_state & door_token))
5576 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5577 int kk_door = MAX(0, k2_door);
5578 int sync_frame = kk_door * door_delay.value;
5579 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5581 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5582 &g_src_x, &g_src_y);
5587 if (!door_panel_drawn[door_index])
5589 ClearRectangle(drawto, door_rect->x, door_rect->y,
5590 door_rect->width, door_rect->height);
5592 door_panel_drawn[door_index] = TRUE;
5595 // draw opening or closing door parts
5597 if (pos->step_xoffset < 0) // door part on right side
5600 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5603 if (dst_xx + width > door_rect->width)
5604 width = door_rect->width - dst_xx;
5606 else // door part on left side
5609 dst_xx = pos->x - kk * pos->step_xoffset;
5613 src_xx = ABS(dst_xx);
5617 width = g->width - src_xx;
5619 if (width > door_rect->width)
5620 width = door_rect->width;
5622 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5625 if (pos->step_yoffset < 0) // door part on bottom side
5628 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5631 if (dst_yy + height > door_rect->height)
5632 height = door_rect->height - dst_yy;
5634 else // door part on top side
5637 dst_yy = pos->y - kk * pos->step_yoffset;
5641 src_yy = ABS(dst_yy);
5645 height = g->height - src_yy;
5648 src_x = g_src_x + src_xx;
5649 src_y = g_src_y + src_yy;
5651 dst_x = door_rect->x + dst_xx;
5652 dst_y = door_rect->y + dst_yy;
5654 is_panel_and_door_has_closed =
5657 panel_has_doors[door_index] &&
5658 k >= num_move_steps_doors_only - 1);
5660 if (width >= 0 && width <= g->width &&
5661 height >= 0 && height <= g->height &&
5662 !is_panel_and_door_has_closed)
5664 if (is_panel || !pos->draw_masked)
5665 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5668 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5672 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5674 if ((part_opening && (width < 0 || height < 0)) ||
5675 (part_closing && (width >= g->width && height >= g->height)))
5676 door_part_done[nr] = TRUE;
5678 // continue door part animations, but not panel after door has closed
5679 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5680 door_part_done_all = FALSE;
5683 if (!(door_state & DOOR_NO_DELAY))
5687 SkipUntilDelayReached(&door_delay, &k, last_frame);
5689 // prevent OS (Windows) from complaining about program not responding
5693 if (door_part_done_all)
5697 if (!(door_state & DOOR_NO_DELAY))
5699 // wait for specified door action post delay
5700 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5701 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5702 else if (door_state & DOOR_ACTION_1)
5703 door_delay.value = door_1.post_delay;
5704 else if (door_state & DOOR_ACTION_2)
5705 door_delay.value = door_2.post_delay;
5707 while (!DelayReached(&door_delay))
5712 if (door_state & DOOR_ACTION_1)
5713 door1 = door_state & DOOR_ACTION_1;
5714 if (door_state & DOOR_ACTION_2)
5715 door2 = door_state & DOOR_ACTION_2;
5717 // draw masked border over door area
5718 DrawMaskedBorder(REDRAW_DOOR_1);
5719 DrawMaskedBorder(REDRAW_DOOR_2);
5721 ClearAutoRepeatKeyEvents();
5723 return (door1 | door2);
5726 static boolean useSpecialEditorDoor(void)
5728 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5729 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5731 // do not draw special editor door if editor border defined or redefined
5732 if (graphic_info[graphic].bitmap != NULL || redefined)
5735 // do not draw special editor door if global border defined to be empty
5736 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5739 // do not draw special editor door if viewport definitions do not match
5743 EY + EYSIZE != VY + VYSIZE)
5749 void DrawSpecialEditorDoor(void)
5751 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5752 int top_border_width = gfx1->width;
5753 int top_border_height = gfx1->height;
5754 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5755 int ex = EX - outer_border;
5756 int ey = EY - outer_border;
5757 int vy = VY - outer_border;
5758 int exsize = EXSIZE + 2 * outer_border;
5760 if (!useSpecialEditorDoor())
5763 // draw bigger level editor toolbox window
5764 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5765 top_border_width, top_border_height, ex, ey - top_border_height);
5766 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5767 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5769 redraw_mask |= REDRAW_ALL;
5772 void UndrawSpecialEditorDoor(void)
5774 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5775 int top_border_width = gfx1->width;
5776 int top_border_height = gfx1->height;
5777 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5778 int ex = EX - outer_border;
5779 int ey = EY - outer_border;
5780 int ey_top = ey - top_border_height;
5781 int exsize = EXSIZE + 2 * outer_border;
5782 int eysize = EYSIZE + 2 * outer_border;
5784 if (!useSpecialEditorDoor())
5787 // draw normal tape recorder window
5788 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5790 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5791 ex, ey_top, top_border_width, top_border_height,
5793 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5794 ex, ey, exsize, eysize, ex, ey);
5798 // if screen background is set to "[NONE]", clear editor toolbox window
5799 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5800 ClearRectangle(drawto, ex, ey, exsize, eysize);
5803 redraw_mask |= REDRAW_ALL;
5807 // ---------- new tool button stuff -------------------------------------------
5812 struct TextPosInfo *pos;
5814 boolean is_touch_button;
5816 } toolbutton_info[NUM_TOOL_BUTTONS] =
5819 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5820 TOOL_CTRL_ID_YES, FALSE, "yes"
5823 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5824 TOOL_CTRL_ID_NO, FALSE, "no"
5827 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5828 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5831 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5832 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5835 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5836 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5839 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5840 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5843 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5844 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5847 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5848 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5851 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5852 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5855 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5856 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5860 void CreateToolButtons(void)
5864 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5866 int graphic = toolbutton_info[i].graphic;
5867 struct GraphicInfo *gfx = &graphic_info[graphic];
5868 struct TextPosInfo *pos = toolbutton_info[i].pos;
5869 struct GadgetInfo *gi;
5870 Bitmap *deco_bitmap = None;
5871 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5872 unsigned int event_mask = GD_EVENT_RELEASED;
5873 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5874 int base_x = (is_touch_button ? 0 : DX);
5875 int base_y = (is_touch_button ? 0 : DY);
5876 int gd_x = gfx->src_x;
5877 int gd_y = gfx->src_y;
5878 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5879 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5884 // do not use touch buttons if overlay touch buttons are disabled
5885 if (is_touch_button && !setup.touch.overlay_buttons)
5888 if (global.use_envelope_request && !is_touch_button)
5890 setRequestPosition(&base_x, &base_y, TRUE);
5892 // check if request buttons are outside of envelope and fix, if needed
5893 if (x < 0 || x + gfx->width > request.width ||
5894 y < 0 || y + gfx->height > request.height)
5896 if (id == TOOL_CTRL_ID_YES)
5899 y = request.height - 2 * request.border_size - gfx->height;
5901 else if (id == TOOL_CTRL_ID_NO)
5903 x = request.width - 2 * request.border_size - gfx->width;
5904 y = request.height - 2 * request.border_size - gfx->height;
5906 else if (id == TOOL_CTRL_ID_CONFIRM)
5908 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5909 y = request.height - 2 * request.border_size - gfx->height;
5911 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5913 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5915 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5916 y = request.height - 2 * request.border_size - gfx->height * 2;
5918 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5919 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5924 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5927 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5929 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5930 pos->size, &deco_bitmap, &deco_x, &deco_y);
5931 deco_xpos = (gfx->width - pos->size) / 2;
5932 deco_ypos = (gfx->height - pos->size) / 2;
5935 gi = CreateGadget(GDI_CUSTOM_ID, id,
5936 GDI_IMAGE_ID, graphic,
5937 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5940 GDI_WIDTH, gfx->width,
5941 GDI_HEIGHT, gfx->height,
5942 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5943 GDI_STATE, GD_BUTTON_UNPRESSED,
5944 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5945 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5946 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5947 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5948 GDI_DECORATION_SIZE, pos->size, pos->size,
5949 GDI_DECORATION_SHIFTING, 1, 1,
5950 GDI_DIRECT_DRAW, FALSE,
5951 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5952 GDI_EVENT_MASK, event_mask,
5953 GDI_CALLBACK_ACTION, HandleToolButtons,
5957 Fail("cannot create gadget");
5959 tool_gadget[id] = gi;
5963 void FreeToolButtons(void)
5967 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5968 FreeGadget(tool_gadget[i]);
5971 static void UnmapToolButtons(void)
5975 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5976 UnmapGadget(tool_gadget[i]);
5979 static void HandleToolButtons(struct GadgetInfo *gi)
5981 request_gadget_id = gi->custom_id;
5984 static struct Mapping_EM_to_RND_object
5987 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5988 boolean is_backside; // backside of moving element
5994 em_object_mapping_list[GAME_TILE_MAX + 1] =
5997 Zborder, FALSE, FALSE,
6001 Zplayer, FALSE, FALSE,
6010 Ztank, FALSE, FALSE,
6014 Zeater, FALSE, FALSE,
6018 Zdynamite, FALSE, FALSE,
6022 Zboom, FALSE, FALSE,
6027 Xchain, FALSE, FALSE,
6028 EL_DEFAULT, ACTION_EXPLODING, -1
6031 Xboom_bug, FALSE, FALSE,
6032 EL_BUG, ACTION_EXPLODING, -1
6035 Xboom_tank, FALSE, FALSE,
6036 EL_SPACESHIP, ACTION_EXPLODING, -1
6039 Xboom_android, FALSE, FALSE,
6040 EL_EMC_ANDROID, ACTION_OTHER, -1
6043 Xboom_1, FALSE, FALSE,
6044 EL_DEFAULT, ACTION_EXPLODING, -1
6047 Xboom_2, FALSE, FALSE,
6048 EL_DEFAULT, ACTION_EXPLODING, -1
6052 Xblank, TRUE, FALSE,
6057 Xsplash_e, FALSE, FALSE,
6058 EL_ACID_SPLASH_RIGHT, -1, -1
6061 Xsplash_w, FALSE, FALSE,
6062 EL_ACID_SPLASH_LEFT, -1, -1
6066 Xplant, TRUE, FALSE,
6067 EL_EMC_PLANT, -1, -1
6070 Yplant, FALSE, FALSE,
6071 EL_EMC_PLANT, -1, -1
6075 Xacid_1, TRUE, FALSE,
6079 Xacid_2, FALSE, FALSE,
6083 Xacid_3, FALSE, FALSE,
6087 Xacid_4, FALSE, FALSE,
6091 Xacid_5, FALSE, FALSE,
6095 Xacid_6, FALSE, FALSE,
6099 Xacid_7, FALSE, FALSE,
6103 Xacid_8, FALSE, FALSE,
6108 Xfake_acid_1, TRUE, FALSE,
6109 EL_EMC_FAKE_ACID, -1, -1
6112 Xfake_acid_2, FALSE, FALSE,
6113 EL_EMC_FAKE_ACID, -1, -1
6116 Xfake_acid_3, FALSE, FALSE,
6117 EL_EMC_FAKE_ACID, -1, -1
6120 Xfake_acid_4, FALSE, FALSE,
6121 EL_EMC_FAKE_ACID, -1, -1
6124 Xfake_acid_5, FALSE, FALSE,
6125 EL_EMC_FAKE_ACID, -1, -1
6128 Xfake_acid_6, FALSE, FALSE,
6129 EL_EMC_FAKE_ACID, -1, -1
6132 Xfake_acid_7, FALSE, FALSE,
6133 EL_EMC_FAKE_ACID, -1, -1
6136 Xfake_acid_8, FALSE, FALSE,
6137 EL_EMC_FAKE_ACID, -1, -1
6141 Xfake_acid_1_player, FALSE, FALSE,
6142 EL_EMC_FAKE_ACID, -1, -1
6145 Xfake_acid_2_player, FALSE, FALSE,
6146 EL_EMC_FAKE_ACID, -1, -1
6149 Xfake_acid_3_player, FALSE, FALSE,
6150 EL_EMC_FAKE_ACID, -1, -1
6153 Xfake_acid_4_player, FALSE, FALSE,
6154 EL_EMC_FAKE_ACID, -1, -1
6157 Xfake_acid_5_player, FALSE, FALSE,
6158 EL_EMC_FAKE_ACID, -1, -1
6161 Xfake_acid_6_player, FALSE, FALSE,
6162 EL_EMC_FAKE_ACID, -1, -1
6165 Xfake_acid_7_player, FALSE, FALSE,
6166 EL_EMC_FAKE_ACID, -1, -1
6169 Xfake_acid_8_player, FALSE, FALSE,
6170 EL_EMC_FAKE_ACID, -1, -1
6174 Xgrass, TRUE, FALSE,
6175 EL_EMC_GRASS, -1, -1
6178 Ygrass_nB, FALSE, FALSE,
6179 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6182 Ygrass_eB, FALSE, FALSE,
6183 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6186 Ygrass_sB, FALSE, FALSE,
6187 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6190 Ygrass_wB, FALSE, FALSE,
6191 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6199 Ydirt_nB, FALSE, FALSE,
6200 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6203 Ydirt_eB, FALSE, FALSE,
6204 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6207 Ydirt_sB, FALSE, FALSE,
6208 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6211 Ydirt_wB, FALSE, FALSE,
6212 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6216 Xandroid, TRUE, FALSE,
6217 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6220 Xandroid_1_n, FALSE, FALSE,
6221 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6224 Xandroid_2_n, FALSE, FALSE,
6225 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6228 Xandroid_1_e, FALSE, FALSE,
6229 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6232 Xandroid_2_e, FALSE, FALSE,
6233 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6236 Xandroid_1_w, FALSE, FALSE,
6237 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6240 Xandroid_2_w, FALSE, FALSE,
6241 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6244 Xandroid_1_s, FALSE, FALSE,
6245 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6248 Xandroid_2_s, FALSE, FALSE,
6249 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6252 Yandroid_n, FALSE, FALSE,
6253 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6256 Yandroid_nB, FALSE, TRUE,
6257 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6260 Yandroid_ne, FALSE, FALSE,
6261 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6264 Yandroid_neB, FALSE, TRUE,
6265 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6268 Yandroid_e, FALSE, FALSE,
6269 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6272 Yandroid_eB, FALSE, TRUE,
6273 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6276 Yandroid_se, FALSE, FALSE,
6277 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6280 Yandroid_seB, FALSE, TRUE,
6281 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6284 Yandroid_s, FALSE, FALSE,
6285 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6288 Yandroid_sB, FALSE, TRUE,
6289 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6292 Yandroid_sw, FALSE, FALSE,
6293 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6296 Yandroid_swB, FALSE, TRUE,
6297 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6300 Yandroid_w, FALSE, FALSE,
6301 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6304 Yandroid_wB, FALSE, TRUE,
6305 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6308 Yandroid_nw, FALSE, FALSE,
6309 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6312 Yandroid_nwB, FALSE, TRUE,
6313 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6317 Xeater_n, TRUE, FALSE,
6318 EL_YAMYAM_UP, -1, -1
6321 Xeater_e, TRUE, FALSE,
6322 EL_YAMYAM_RIGHT, -1, -1
6325 Xeater_w, TRUE, FALSE,
6326 EL_YAMYAM_LEFT, -1, -1
6329 Xeater_s, TRUE, FALSE,
6330 EL_YAMYAM_DOWN, -1, -1
6333 Yeater_n, FALSE, FALSE,
6334 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6337 Yeater_nB, FALSE, TRUE,
6338 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6341 Yeater_e, FALSE, FALSE,
6342 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6345 Yeater_eB, FALSE, TRUE,
6346 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6349 Yeater_s, FALSE, FALSE,
6350 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6353 Yeater_sB, FALSE, TRUE,
6354 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6357 Yeater_w, FALSE, FALSE,
6358 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6361 Yeater_wB, FALSE, TRUE,
6362 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6365 Yeater_stone, FALSE, FALSE,
6366 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6369 Yeater_spring, FALSE, FALSE,
6370 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6374 Xalien, TRUE, FALSE,
6378 Xalien_pause, FALSE, FALSE,
6382 Yalien_n, FALSE, FALSE,
6383 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6386 Yalien_nB, FALSE, TRUE,
6387 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6390 Yalien_e, FALSE, FALSE,
6391 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6394 Yalien_eB, FALSE, TRUE,
6395 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6398 Yalien_s, FALSE, FALSE,
6399 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6402 Yalien_sB, FALSE, TRUE,
6403 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6406 Yalien_w, FALSE, FALSE,
6407 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6410 Yalien_wB, FALSE, TRUE,
6411 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6414 Yalien_stone, FALSE, FALSE,
6415 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6418 Yalien_spring, FALSE, FALSE,
6419 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6423 Xbug_1_n, TRUE, FALSE,
6427 Xbug_1_e, TRUE, FALSE,
6428 EL_BUG_RIGHT, -1, -1
6431 Xbug_1_s, TRUE, FALSE,
6435 Xbug_1_w, TRUE, FALSE,
6439 Xbug_2_n, FALSE, FALSE,
6443 Xbug_2_e, FALSE, FALSE,
6444 EL_BUG_RIGHT, -1, -1
6447 Xbug_2_s, FALSE, FALSE,
6451 Xbug_2_w, FALSE, FALSE,
6455 Ybug_n, FALSE, FALSE,
6456 EL_BUG, ACTION_MOVING, MV_BIT_UP
6459 Ybug_nB, FALSE, TRUE,
6460 EL_BUG, ACTION_MOVING, MV_BIT_UP
6463 Ybug_e, FALSE, FALSE,
6464 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6467 Ybug_eB, FALSE, TRUE,
6468 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6471 Ybug_s, FALSE, FALSE,
6472 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6475 Ybug_sB, FALSE, TRUE,
6476 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6479 Ybug_w, FALSE, FALSE,
6480 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6483 Ybug_wB, FALSE, TRUE,
6484 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6487 Ybug_w_n, FALSE, FALSE,
6488 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6491 Ybug_n_e, FALSE, FALSE,
6492 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6495 Ybug_e_s, FALSE, FALSE,
6496 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6499 Ybug_s_w, FALSE, FALSE,
6500 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6503 Ybug_e_n, FALSE, FALSE,
6504 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6507 Ybug_s_e, FALSE, FALSE,
6508 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6511 Ybug_w_s, FALSE, FALSE,
6512 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6515 Ybug_n_w, FALSE, FALSE,
6516 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6519 Ybug_stone, FALSE, FALSE,
6520 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6523 Ybug_spring, FALSE, FALSE,
6524 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6528 Xtank_1_n, TRUE, FALSE,
6529 EL_SPACESHIP_UP, -1, -1
6532 Xtank_1_e, TRUE, FALSE,
6533 EL_SPACESHIP_RIGHT, -1, -1
6536 Xtank_1_s, TRUE, FALSE,
6537 EL_SPACESHIP_DOWN, -1, -1
6540 Xtank_1_w, TRUE, FALSE,
6541 EL_SPACESHIP_LEFT, -1, -1
6544 Xtank_2_n, FALSE, FALSE,
6545 EL_SPACESHIP_UP, -1, -1
6548 Xtank_2_e, FALSE, FALSE,
6549 EL_SPACESHIP_RIGHT, -1, -1
6552 Xtank_2_s, FALSE, FALSE,
6553 EL_SPACESHIP_DOWN, -1, -1
6556 Xtank_2_w, FALSE, FALSE,
6557 EL_SPACESHIP_LEFT, -1, -1
6560 Ytank_n, FALSE, FALSE,
6561 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6564 Ytank_nB, FALSE, TRUE,
6565 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6568 Ytank_e, FALSE, FALSE,
6569 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6572 Ytank_eB, FALSE, TRUE,
6573 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6576 Ytank_s, FALSE, FALSE,
6577 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6580 Ytank_sB, FALSE, TRUE,
6581 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6584 Ytank_w, FALSE, FALSE,
6585 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6588 Ytank_wB, FALSE, TRUE,
6589 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6592 Ytank_w_n, FALSE, FALSE,
6593 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6596 Ytank_n_e, FALSE, FALSE,
6597 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6600 Ytank_e_s, FALSE, FALSE,
6601 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6604 Ytank_s_w, FALSE, FALSE,
6605 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6608 Ytank_e_n, FALSE, FALSE,
6609 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6612 Ytank_s_e, FALSE, FALSE,
6613 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6616 Ytank_w_s, FALSE, FALSE,
6617 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6620 Ytank_n_w, FALSE, FALSE,
6621 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6624 Ytank_stone, FALSE, FALSE,
6625 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6628 Ytank_spring, FALSE, FALSE,
6629 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6633 Xemerald, TRUE, FALSE,
6637 Xemerald_pause, FALSE, FALSE,
6641 Xemerald_fall, FALSE, FALSE,
6645 Xemerald_shine, FALSE, FALSE,
6646 EL_EMERALD, ACTION_TWINKLING, -1
6649 Yemerald_s, FALSE, FALSE,
6650 EL_EMERALD, ACTION_FALLING, -1
6653 Yemerald_sB, FALSE, TRUE,
6654 EL_EMERALD, ACTION_FALLING, -1
6657 Yemerald_e, FALSE, FALSE,
6658 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6661 Yemerald_eB, FALSE, TRUE,
6662 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6665 Yemerald_w, FALSE, FALSE,
6666 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6669 Yemerald_wB, FALSE, TRUE,
6670 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6673 Yemerald_blank, FALSE, FALSE,
6674 EL_EMERALD, ACTION_COLLECTING, -1
6678 Xdiamond, TRUE, FALSE,
6682 Xdiamond_pause, FALSE, FALSE,
6686 Xdiamond_fall, FALSE, FALSE,
6690 Xdiamond_shine, FALSE, FALSE,
6691 EL_DIAMOND, ACTION_TWINKLING, -1
6694 Ydiamond_s, FALSE, FALSE,
6695 EL_DIAMOND, ACTION_FALLING, -1
6698 Ydiamond_sB, FALSE, TRUE,
6699 EL_DIAMOND, ACTION_FALLING, -1
6702 Ydiamond_e, FALSE, FALSE,
6703 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6706 Ydiamond_eB, FALSE, TRUE,
6707 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6710 Ydiamond_w, FALSE, FALSE,
6711 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6714 Ydiamond_wB, FALSE, TRUE,
6715 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6718 Ydiamond_blank, FALSE, FALSE,
6719 EL_DIAMOND, ACTION_COLLECTING, -1
6722 Ydiamond_stone, FALSE, FALSE,
6723 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6727 Xstone, TRUE, FALSE,
6731 Xstone_pause, FALSE, FALSE,
6735 Xstone_fall, FALSE, FALSE,
6739 Ystone_s, FALSE, FALSE,
6740 EL_ROCK, ACTION_FALLING, -1
6743 Ystone_sB, FALSE, TRUE,
6744 EL_ROCK, ACTION_FALLING, -1
6747 Ystone_e, FALSE, FALSE,
6748 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6751 Ystone_eB, FALSE, TRUE,
6752 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6755 Ystone_w, FALSE, FALSE,
6756 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6759 Ystone_wB, FALSE, TRUE,
6760 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6768 Xbomb_pause, FALSE, FALSE,
6772 Xbomb_fall, FALSE, FALSE,
6776 Ybomb_s, FALSE, FALSE,
6777 EL_BOMB, ACTION_FALLING, -1
6780 Ybomb_sB, FALSE, TRUE,
6781 EL_BOMB, ACTION_FALLING, -1
6784 Ybomb_e, FALSE, FALSE,
6785 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6788 Ybomb_eB, FALSE, TRUE,
6789 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6792 Ybomb_w, FALSE, FALSE,
6793 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6796 Ybomb_wB, FALSE, TRUE,
6797 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6800 Ybomb_blank, FALSE, FALSE,
6801 EL_BOMB, ACTION_ACTIVATING, -1
6809 Xnut_pause, FALSE, FALSE,
6813 Xnut_fall, FALSE, FALSE,
6817 Ynut_s, FALSE, FALSE,
6818 EL_NUT, ACTION_FALLING, -1
6821 Ynut_sB, FALSE, TRUE,
6822 EL_NUT, ACTION_FALLING, -1
6825 Ynut_e, FALSE, FALSE,
6826 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6829 Ynut_eB, FALSE, TRUE,
6830 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6833 Ynut_w, FALSE, FALSE,
6834 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6837 Ynut_wB, FALSE, TRUE,
6838 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6841 Ynut_stone, FALSE, FALSE,
6842 EL_NUT, ACTION_BREAKING, -1
6846 Xspring, TRUE, FALSE,
6850 Xspring_pause, FALSE, FALSE,
6854 Xspring_e, TRUE, FALSE,
6855 EL_SPRING_RIGHT, -1, -1
6858 Xspring_w, TRUE, FALSE,
6859 EL_SPRING_LEFT, -1, -1
6862 Xspring_fall, FALSE, FALSE,
6866 Yspring_s, FALSE, FALSE,
6867 EL_SPRING, ACTION_FALLING, -1
6870 Yspring_sB, FALSE, TRUE,
6871 EL_SPRING, ACTION_FALLING, -1
6874 Yspring_e, FALSE, FALSE,
6875 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6878 Yspring_eB, FALSE, TRUE,
6879 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6882 Yspring_w, FALSE, FALSE,
6883 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6886 Yspring_wB, FALSE, TRUE,
6887 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6890 Yspring_alien_e, FALSE, FALSE,
6891 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6894 Yspring_alien_eB, FALSE, TRUE,
6895 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6898 Yspring_alien_w, FALSE, FALSE,
6899 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6902 Yspring_alien_wB, FALSE, TRUE,
6903 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6907 Xpush_emerald_e, FALSE, FALSE,
6908 EL_EMERALD, -1, MV_BIT_RIGHT
6911 Xpush_emerald_w, FALSE, FALSE,
6912 EL_EMERALD, -1, MV_BIT_LEFT
6915 Xpush_diamond_e, FALSE, FALSE,
6916 EL_DIAMOND, -1, MV_BIT_RIGHT
6919 Xpush_diamond_w, FALSE, FALSE,
6920 EL_DIAMOND, -1, MV_BIT_LEFT
6923 Xpush_stone_e, FALSE, FALSE,
6924 EL_ROCK, -1, MV_BIT_RIGHT
6927 Xpush_stone_w, FALSE, FALSE,
6928 EL_ROCK, -1, MV_BIT_LEFT
6931 Xpush_bomb_e, FALSE, FALSE,
6932 EL_BOMB, -1, MV_BIT_RIGHT
6935 Xpush_bomb_w, FALSE, FALSE,
6936 EL_BOMB, -1, MV_BIT_LEFT
6939 Xpush_nut_e, FALSE, FALSE,
6940 EL_NUT, -1, MV_BIT_RIGHT
6943 Xpush_nut_w, FALSE, FALSE,
6944 EL_NUT, -1, MV_BIT_LEFT
6947 Xpush_spring_e, FALSE, FALSE,
6948 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6951 Xpush_spring_w, FALSE, FALSE,
6952 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6956 Xdynamite, TRUE, FALSE,
6957 EL_EM_DYNAMITE, -1, -1
6960 Ydynamite_blank, FALSE, FALSE,
6961 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6964 Xdynamite_1, TRUE, FALSE,
6965 EL_EM_DYNAMITE_ACTIVE, -1, -1
6968 Xdynamite_2, FALSE, FALSE,
6969 EL_EM_DYNAMITE_ACTIVE, -1, -1
6972 Xdynamite_3, FALSE, FALSE,
6973 EL_EM_DYNAMITE_ACTIVE, -1, -1
6976 Xdynamite_4, FALSE, FALSE,
6977 EL_EM_DYNAMITE_ACTIVE, -1, -1
6981 Xkey_1, TRUE, FALSE,
6985 Xkey_2, TRUE, FALSE,
6989 Xkey_3, TRUE, FALSE,
6993 Xkey_4, TRUE, FALSE,
6997 Xkey_5, TRUE, FALSE,
6998 EL_EMC_KEY_5, -1, -1
7001 Xkey_6, TRUE, FALSE,
7002 EL_EMC_KEY_6, -1, -1
7005 Xkey_7, TRUE, FALSE,
7006 EL_EMC_KEY_7, -1, -1
7009 Xkey_8, TRUE, FALSE,
7010 EL_EMC_KEY_8, -1, -1
7014 Xdoor_1, TRUE, FALSE,
7015 EL_EM_GATE_1, -1, -1
7018 Xdoor_2, TRUE, FALSE,
7019 EL_EM_GATE_2, -1, -1
7022 Xdoor_3, TRUE, FALSE,
7023 EL_EM_GATE_3, -1, -1
7026 Xdoor_4, TRUE, FALSE,
7027 EL_EM_GATE_4, -1, -1
7030 Xdoor_5, TRUE, FALSE,
7031 EL_EMC_GATE_5, -1, -1
7034 Xdoor_6, TRUE, FALSE,
7035 EL_EMC_GATE_6, -1, -1
7038 Xdoor_7, TRUE, FALSE,
7039 EL_EMC_GATE_7, -1, -1
7042 Xdoor_8, TRUE, FALSE,
7043 EL_EMC_GATE_8, -1, -1
7047 Xfake_door_1, TRUE, FALSE,
7048 EL_EM_GATE_1_GRAY, -1, -1
7051 Xfake_door_2, TRUE, FALSE,
7052 EL_EM_GATE_2_GRAY, -1, -1
7055 Xfake_door_3, TRUE, FALSE,
7056 EL_EM_GATE_3_GRAY, -1, -1
7059 Xfake_door_4, TRUE, FALSE,
7060 EL_EM_GATE_4_GRAY, -1, -1
7063 Xfake_door_5, TRUE, FALSE,
7064 EL_EMC_GATE_5_GRAY, -1, -1
7067 Xfake_door_6, TRUE, FALSE,
7068 EL_EMC_GATE_6_GRAY, -1, -1
7071 Xfake_door_7, TRUE, FALSE,
7072 EL_EMC_GATE_7_GRAY, -1, -1
7075 Xfake_door_8, TRUE, FALSE,
7076 EL_EMC_GATE_8_GRAY, -1, -1
7080 Xballoon, TRUE, FALSE,
7084 Yballoon_n, FALSE, FALSE,
7085 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7088 Yballoon_nB, FALSE, TRUE,
7089 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7092 Yballoon_e, FALSE, FALSE,
7093 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7096 Yballoon_eB, FALSE, TRUE,
7097 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7100 Yballoon_s, FALSE, FALSE,
7101 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7104 Yballoon_sB, FALSE, TRUE,
7105 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7108 Yballoon_w, FALSE, FALSE,
7109 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7112 Yballoon_wB, FALSE, TRUE,
7113 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7117 Xball_1, TRUE, FALSE,
7118 EL_EMC_MAGIC_BALL, -1, -1
7121 Yball_1, FALSE, FALSE,
7122 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7125 Xball_2, FALSE, FALSE,
7126 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7129 Yball_2, FALSE, FALSE,
7130 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7133 Yball_blank, FALSE, FALSE,
7134 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7138 Xamoeba_1, TRUE, FALSE,
7139 EL_AMOEBA_DRY, ACTION_OTHER, -1
7142 Xamoeba_2, FALSE, FALSE,
7143 EL_AMOEBA_DRY, ACTION_OTHER, -1
7146 Xamoeba_3, FALSE, FALSE,
7147 EL_AMOEBA_DRY, ACTION_OTHER, -1
7150 Xamoeba_4, FALSE, FALSE,
7151 EL_AMOEBA_DRY, ACTION_OTHER, -1
7154 Xamoeba_5, TRUE, FALSE,
7155 EL_AMOEBA_WET, ACTION_OTHER, -1
7158 Xamoeba_6, FALSE, FALSE,
7159 EL_AMOEBA_WET, ACTION_OTHER, -1
7162 Xamoeba_7, FALSE, FALSE,
7163 EL_AMOEBA_WET, ACTION_OTHER, -1
7166 Xamoeba_8, FALSE, FALSE,
7167 EL_AMOEBA_WET, ACTION_OTHER, -1
7172 EL_AMOEBA_DROP, ACTION_GROWING, -1
7175 Xdrip_fall, FALSE, FALSE,
7176 EL_AMOEBA_DROP, -1, -1
7179 Xdrip_stretch, FALSE, FALSE,
7180 EL_AMOEBA_DROP, ACTION_FALLING, -1
7183 Xdrip_stretchB, FALSE, TRUE,
7184 EL_AMOEBA_DROP, ACTION_FALLING, -1
7187 Ydrip_1_s, FALSE, FALSE,
7188 EL_AMOEBA_DROP, ACTION_FALLING, -1
7191 Ydrip_1_sB, FALSE, TRUE,
7192 EL_AMOEBA_DROP, ACTION_FALLING, -1
7195 Ydrip_2_s, FALSE, FALSE,
7196 EL_AMOEBA_DROP, ACTION_FALLING, -1
7199 Ydrip_2_sB, FALSE, TRUE,
7200 EL_AMOEBA_DROP, ACTION_FALLING, -1
7204 Xwonderwall, TRUE, FALSE,
7205 EL_MAGIC_WALL, -1, -1
7208 Ywonderwall, FALSE, FALSE,
7209 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7213 Xwheel, TRUE, FALSE,
7214 EL_ROBOT_WHEEL, -1, -1
7217 Ywheel, FALSE, FALSE,
7218 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7222 Xswitch, TRUE, FALSE,
7223 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7226 Yswitch, FALSE, FALSE,
7227 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7231 Xbumper, TRUE, FALSE,
7232 EL_EMC_SPRING_BUMPER, -1, -1
7235 Ybumper, FALSE, FALSE,
7236 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7240 Xacid_nw, TRUE, FALSE,
7241 EL_ACID_POOL_TOPLEFT, -1, -1
7244 Xacid_ne, TRUE, FALSE,
7245 EL_ACID_POOL_TOPRIGHT, -1, -1
7248 Xacid_sw, TRUE, FALSE,
7249 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7252 Xacid_s, TRUE, FALSE,
7253 EL_ACID_POOL_BOTTOM, -1, -1
7256 Xacid_se, TRUE, FALSE,
7257 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7261 Xfake_blank, TRUE, FALSE,
7262 EL_INVISIBLE_WALL, -1, -1
7265 Yfake_blank, FALSE, FALSE,
7266 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7270 Xfake_grass, TRUE, FALSE,
7271 EL_EMC_FAKE_GRASS, -1, -1
7274 Yfake_grass, FALSE, FALSE,
7275 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7279 Xfake_amoeba, TRUE, FALSE,
7280 EL_EMC_DRIPPER, -1, -1
7283 Yfake_amoeba, FALSE, FALSE,
7284 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7288 Xlenses, TRUE, FALSE,
7289 EL_EMC_LENSES, -1, -1
7293 Xmagnify, TRUE, FALSE,
7294 EL_EMC_MAGNIFIER, -1, -1
7299 EL_QUICKSAND_EMPTY, -1, -1
7302 Xsand_stone, TRUE, FALSE,
7303 EL_QUICKSAND_FULL, -1, -1
7306 Xsand_stonein_1, FALSE, TRUE,
7307 EL_ROCK, ACTION_FILLING, -1
7310 Xsand_stonein_2, FALSE, TRUE,
7311 EL_ROCK, ACTION_FILLING, -1
7314 Xsand_stonein_3, FALSE, TRUE,
7315 EL_ROCK, ACTION_FILLING, -1
7318 Xsand_stonein_4, FALSE, TRUE,
7319 EL_ROCK, ACTION_FILLING, -1
7322 Xsand_sandstone_1, FALSE, FALSE,
7323 EL_QUICKSAND_FILLING, -1, -1
7326 Xsand_sandstone_2, FALSE, FALSE,
7327 EL_QUICKSAND_FILLING, -1, -1
7330 Xsand_sandstone_3, FALSE, FALSE,
7331 EL_QUICKSAND_FILLING, -1, -1
7334 Xsand_sandstone_4, FALSE, FALSE,
7335 EL_QUICKSAND_FILLING, -1, -1
7338 Xsand_stonesand_1, FALSE, FALSE,
7339 EL_QUICKSAND_EMPTYING, -1, -1
7342 Xsand_stonesand_2, FALSE, FALSE,
7343 EL_QUICKSAND_EMPTYING, -1, -1
7346 Xsand_stonesand_3, FALSE, FALSE,
7347 EL_QUICKSAND_EMPTYING, -1, -1
7350 Xsand_stonesand_4, FALSE, FALSE,
7351 EL_QUICKSAND_EMPTYING, -1, -1
7354 Xsand_stoneout_1, FALSE, FALSE,
7355 EL_ROCK, ACTION_EMPTYING, -1
7358 Xsand_stoneout_2, FALSE, FALSE,
7359 EL_ROCK, ACTION_EMPTYING, -1
7362 Xsand_stonesand_quickout_1, FALSE, FALSE,
7363 EL_QUICKSAND_EMPTYING, -1, -1
7366 Xsand_stonesand_quickout_2, FALSE, FALSE,
7367 EL_QUICKSAND_EMPTYING, -1, -1
7371 Xslide_ns, TRUE, FALSE,
7372 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7375 Yslide_ns_blank, FALSE, FALSE,
7376 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7379 Xslide_ew, TRUE, FALSE,
7380 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7383 Yslide_ew_blank, FALSE, FALSE,
7384 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7388 Xwind_n, TRUE, FALSE,
7389 EL_BALLOON_SWITCH_UP, -1, -1
7392 Xwind_e, TRUE, FALSE,
7393 EL_BALLOON_SWITCH_RIGHT, -1, -1
7396 Xwind_s, TRUE, FALSE,
7397 EL_BALLOON_SWITCH_DOWN, -1, -1
7400 Xwind_w, TRUE, FALSE,
7401 EL_BALLOON_SWITCH_LEFT, -1, -1
7404 Xwind_any, TRUE, FALSE,
7405 EL_BALLOON_SWITCH_ANY, -1, -1
7408 Xwind_stop, TRUE, FALSE,
7409 EL_BALLOON_SWITCH_NONE, -1, -1
7414 EL_EM_EXIT_CLOSED, -1, -1
7417 Xexit_1, TRUE, FALSE,
7418 EL_EM_EXIT_OPEN, -1, -1
7421 Xexit_2, FALSE, FALSE,
7422 EL_EM_EXIT_OPEN, -1, -1
7425 Xexit_3, FALSE, FALSE,
7426 EL_EM_EXIT_OPEN, -1, -1
7430 Xpause, FALSE, FALSE,
7435 Xwall_1, TRUE, FALSE,
7439 Xwall_2, TRUE, FALSE,
7440 EL_EMC_WALL_14, -1, -1
7443 Xwall_3, TRUE, FALSE,
7444 EL_EMC_WALL_15, -1, -1
7447 Xwall_4, TRUE, FALSE,
7448 EL_EMC_WALL_16, -1, -1
7452 Xroundwall_1, TRUE, FALSE,
7453 EL_WALL_SLIPPERY, -1, -1
7456 Xroundwall_2, TRUE, FALSE,
7457 EL_EMC_WALL_SLIPPERY_2, -1, -1
7460 Xroundwall_3, TRUE, FALSE,
7461 EL_EMC_WALL_SLIPPERY_3, -1, -1
7464 Xroundwall_4, TRUE, FALSE,
7465 EL_EMC_WALL_SLIPPERY_4, -1, -1
7469 Xsteel_1, TRUE, FALSE,
7470 EL_STEELWALL, -1, -1
7473 Xsteel_2, TRUE, FALSE,
7474 EL_EMC_STEELWALL_2, -1, -1
7477 Xsteel_3, TRUE, FALSE,
7478 EL_EMC_STEELWALL_3, -1, -1
7481 Xsteel_4, TRUE, FALSE,
7482 EL_EMC_STEELWALL_4, -1, -1
7486 Xdecor_1, TRUE, FALSE,
7487 EL_EMC_WALL_8, -1, -1
7490 Xdecor_2, TRUE, FALSE,
7491 EL_EMC_WALL_6, -1, -1
7494 Xdecor_3, TRUE, FALSE,
7495 EL_EMC_WALL_4, -1, -1
7498 Xdecor_4, TRUE, FALSE,
7499 EL_EMC_WALL_7, -1, -1
7502 Xdecor_5, TRUE, FALSE,
7503 EL_EMC_WALL_5, -1, -1
7506 Xdecor_6, TRUE, FALSE,
7507 EL_EMC_WALL_9, -1, -1
7510 Xdecor_7, TRUE, FALSE,
7511 EL_EMC_WALL_10, -1, -1
7514 Xdecor_8, TRUE, FALSE,
7515 EL_EMC_WALL_1, -1, -1
7518 Xdecor_9, TRUE, FALSE,
7519 EL_EMC_WALL_2, -1, -1
7522 Xdecor_10, TRUE, FALSE,
7523 EL_EMC_WALL_3, -1, -1
7526 Xdecor_11, TRUE, FALSE,
7527 EL_EMC_WALL_11, -1, -1
7530 Xdecor_12, TRUE, FALSE,
7531 EL_EMC_WALL_12, -1, -1
7535 Xalpha_0, TRUE, FALSE,
7536 EL_CHAR('0'), -1, -1
7539 Xalpha_1, TRUE, FALSE,
7540 EL_CHAR('1'), -1, -1
7543 Xalpha_2, TRUE, FALSE,
7544 EL_CHAR('2'), -1, -1
7547 Xalpha_3, TRUE, FALSE,
7548 EL_CHAR('3'), -1, -1
7551 Xalpha_4, TRUE, FALSE,
7552 EL_CHAR('4'), -1, -1
7555 Xalpha_5, TRUE, FALSE,
7556 EL_CHAR('5'), -1, -1
7559 Xalpha_6, TRUE, FALSE,
7560 EL_CHAR('6'), -1, -1
7563 Xalpha_7, TRUE, FALSE,
7564 EL_CHAR('7'), -1, -1
7567 Xalpha_8, TRUE, FALSE,
7568 EL_CHAR('8'), -1, -1
7571 Xalpha_9, TRUE, FALSE,
7572 EL_CHAR('9'), -1, -1
7575 Xalpha_excla, TRUE, FALSE,
7576 EL_CHAR('!'), -1, -1
7579 Xalpha_apost, TRUE, FALSE,
7580 EL_CHAR('\''), -1, -1
7583 Xalpha_comma, TRUE, FALSE,
7584 EL_CHAR(','), -1, -1
7587 Xalpha_minus, TRUE, FALSE,
7588 EL_CHAR('-'), -1, -1
7591 Xalpha_perio, TRUE, FALSE,
7592 EL_CHAR('.'), -1, -1
7595 Xalpha_colon, TRUE, FALSE,
7596 EL_CHAR(':'), -1, -1
7599 Xalpha_quest, TRUE, FALSE,
7600 EL_CHAR('?'), -1, -1
7603 Xalpha_a, TRUE, FALSE,
7604 EL_CHAR('A'), -1, -1
7607 Xalpha_b, TRUE, FALSE,
7608 EL_CHAR('B'), -1, -1
7611 Xalpha_c, TRUE, FALSE,
7612 EL_CHAR('C'), -1, -1
7615 Xalpha_d, TRUE, FALSE,
7616 EL_CHAR('D'), -1, -1
7619 Xalpha_e, TRUE, FALSE,
7620 EL_CHAR('E'), -1, -1
7623 Xalpha_f, TRUE, FALSE,
7624 EL_CHAR('F'), -1, -1
7627 Xalpha_g, TRUE, FALSE,
7628 EL_CHAR('G'), -1, -1
7631 Xalpha_h, TRUE, FALSE,
7632 EL_CHAR('H'), -1, -1
7635 Xalpha_i, TRUE, FALSE,
7636 EL_CHAR('I'), -1, -1
7639 Xalpha_j, TRUE, FALSE,
7640 EL_CHAR('J'), -1, -1
7643 Xalpha_k, TRUE, FALSE,
7644 EL_CHAR('K'), -1, -1
7647 Xalpha_l, TRUE, FALSE,
7648 EL_CHAR('L'), -1, -1
7651 Xalpha_m, TRUE, FALSE,
7652 EL_CHAR('M'), -1, -1
7655 Xalpha_n, TRUE, FALSE,
7656 EL_CHAR('N'), -1, -1
7659 Xalpha_o, TRUE, FALSE,
7660 EL_CHAR('O'), -1, -1
7663 Xalpha_p, TRUE, FALSE,
7664 EL_CHAR('P'), -1, -1
7667 Xalpha_q, TRUE, FALSE,
7668 EL_CHAR('Q'), -1, -1
7671 Xalpha_r, TRUE, FALSE,
7672 EL_CHAR('R'), -1, -1
7675 Xalpha_s, TRUE, FALSE,
7676 EL_CHAR('S'), -1, -1
7679 Xalpha_t, TRUE, FALSE,
7680 EL_CHAR('T'), -1, -1
7683 Xalpha_u, TRUE, FALSE,
7684 EL_CHAR('U'), -1, -1
7687 Xalpha_v, TRUE, FALSE,
7688 EL_CHAR('V'), -1, -1
7691 Xalpha_w, TRUE, FALSE,
7692 EL_CHAR('W'), -1, -1
7695 Xalpha_x, TRUE, FALSE,
7696 EL_CHAR('X'), -1, -1
7699 Xalpha_y, TRUE, FALSE,
7700 EL_CHAR('Y'), -1, -1
7703 Xalpha_z, TRUE, FALSE,
7704 EL_CHAR('Z'), -1, -1
7707 Xalpha_arrow_e, TRUE, FALSE,
7708 EL_CHAR('>'), -1, -1
7711 Xalpha_arrow_w, TRUE, FALSE,
7712 EL_CHAR('<'), -1, -1
7715 Xalpha_copyr, TRUE, FALSE,
7716 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7720 Ykey_1_blank, FALSE, FALSE,
7721 EL_EM_KEY_1, ACTION_COLLECTING, -1
7724 Ykey_2_blank, FALSE, FALSE,
7725 EL_EM_KEY_2, ACTION_COLLECTING, -1
7728 Ykey_3_blank, FALSE, FALSE,
7729 EL_EM_KEY_3, ACTION_COLLECTING, -1
7732 Ykey_4_blank, FALSE, FALSE,
7733 EL_EM_KEY_4, ACTION_COLLECTING, -1
7736 Ykey_5_blank, FALSE, FALSE,
7737 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7740 Ykey_6_blank, FALSE, FALSE,
7741 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7744 Ykey_7_blank, FALSE, FALSE,
7745 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7748 Ykey_8_blank, FALSE, FALSE,
7749 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7752 Ylenses_blank, FALSE, FALSE,
7753 EL_EMC_LENSES, ACTION_COLLECTING, -1
7756 Ymagnify_blank, FALSE, FALSE,
7757 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7760 Ygrass_blank, FALSE, FALSE,
7761 EL_EMC_GRASS, ACTION_SNAPPING, -1
7764 Ydirt_blank, FALSE, FALSE,
7765 EL_SAND, ACTION_SNAPPING, -1
7774 static struct Mapping_EM_to_RND_player
7783 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7787 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7791 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7795 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7799 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7803 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7807 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7811 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7815 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7819 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7823 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7827 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7831 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7835 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7839 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7843 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7847 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7851 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7855 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7859 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7863 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7867 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7871 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7875 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7879 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7883 EL_PLAYER_1, ACTION_DEFAULT, -1,
7887 EL_PLAYER_2, ACTION_DEFAULT, -1,
7891 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7895 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7899 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7903 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7907 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7911 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7915 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7919 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7923 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7927 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7931 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7935 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7939 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7943 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7947 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7951 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7955 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7959 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7963 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7967 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7971 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7975 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7979 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7983 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7987 EL_PLAYER_3, ACTION_DEFAULT, -1,
7991 EL_PLAYER_4, ACTION_DEFAULT, -1,
8000 int map_element_RND_to_EM_cave(int element_rnd)
8002 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
8003 static boolean mapping_initialized = FALSE;
8005 if (!mapping_initialized)
8009 // return "Xalpha_quest" for all undefined elements in mapping array
8010 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8011 mapping_RND_to_EM[i] = Xalpha_quest;
8013 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8014 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
8015 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
8016 em_object_mapping_list[i].element_em;
8018 mapping_initialized = TRUE;
8021 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
8023 Warn("invalid RND level element %d", element_rnd);
8028 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
8031 int map_element_EM_to_RND_cave(int element_em_cave)
8033 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8034 static boolean mapping_initialized = FALSE;
8036 if (!mapping_initialized)
8040 // return "EL_UNKNOWN" for all undefined elements in mapping array
8041 for (i = 0; i < GAME_TILE_MAX; i++)
8042 mapping_EM_to_RND[i] = EL_UNKNOWN;
8044 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8045 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8046 em_object_mapping_list[i].element_rnd;
8048 mapping_initialized = TRUE;
8051 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8053 Warn("invalid EM cave element %d", element_em_cave);
8058 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8061 int map_element_EM_to_RND_game(int element_em_game)
8063 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8064 static boolean mapping_initialized = FALSE;
8066 if (!mapping_initialized)
8070 // return "EL_UNKNOWN" for all undefined elements in mapping array
8071 for (i = 0; i < GAME_TILE_MAX; i++)
8072 mapping_EM_to_RND[i] = EL_UNKNOWN;
8074 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8075 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8076 em_object_mapping_list[i].element_rnd;
8078 mapping_initialized = TRUE;
8081 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8083 Warn("invalid EM game element %d", element_em_game);
8088 return mapping_EM_to_RND[element_em_game];
8091 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8093 struct LevelInfo_EM *level_em = level->native_em_level;
8094 struct CAVE *cav = level_em->cav;
8097 for (i = 0; i < GAME_TILE_MAX; i++)
8098 cav->android_array[i] = Cblank;
8100 for (i = 0; i < level->num_android_clone_elements; i++)
8102 int element_rnd = level->android_clone_element[i];
8103 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8105 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8106 if (em_object_mapping_list[j].element_rnd == element_rnd)
8107 cav->android_array[em_object_mapping_list[j].element_em] =
8112 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8114 struct LevelInfo_EM *level_em = level->native_em_level;
8115 struct CAVE *cav = level_em->cav;
8118 level->num_android_clone_elements = 0;
8120 for (i = 0; i < GAME_TILE_MAX; i++)
8122 int element_em_cave = cav->android_array[i];
8124 boolean element_found = FALSE;
8126 if (element_em_cave == Cblank)
8129 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8131 for (j = 0; j < level->num_android_clone_elements; j++)
8132 if (level->android_clone_element[j] == element_rnd)
8133 element_found = TRUE;
8137 level->android_clone_element[level->num_android_clone_elements++] =
8140 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8145 if (level->num_android_clone_elements == 0)
8147 level->num_android_clone_elements = 1;
8148 level->android_clone_element[0] = EL_EMPTY;
8152 int map_direction_RND_to_EM(int direction)
8154 return (direction == MV_UP ? 0 :
8155 direction == MV_RIGHT ? 1 :
8156 direction == MV_DOWN ? 2 :
8157 direction == MV_LEFT ? 3 :
8161 int map_direction_EM_to_RND(int direction)
8163 return (direction == 0 ? MV_UP :
8164 direction == 1 ? MV_RIGHT :
8165 direction == 2 ? MV_DOWN :
8166 direction == 3 ? MV_LEFT :
8170 int map_element_RND_to_SP(int element_rnd)
8172 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8174 if (element_rnd >= EL_SP_START &&
8175 element_rnd <= EL_SP_END)
8176 element_sp = element_rnd - EL_SP_START;
8177 else if (element_rnd == EL_EMPTY_SPACE)
8179 else if (element_rnd == EL_INVISIBLE_WALL)
8185 int map_element_SP_to_RND(int element_sp)
8187 int element_rnd = EL_UNKNOWN;
8189 if (element_sp >= 0x00 &&
8191 element_rnd = EL_SP_START + element_sp;
8192 else if (element_sp == 0x28)
8193 element_rnd = EL_INVISIBLE_WALL;
8198 int map_action_SP_to_RND(int action_sp)
8202 case actActive: return ACTION_ACTIVE;
8203 case actImpact: return ACTION_IMPACT;
8204 case actExploding: return ACTION_EXPLODING;
8205 case actDigging: return ACTION_DIGGING;
8206 case actSnapping: return ACTION_SNAPPING;
8207 case actCollecting: return ACTION_COLLECTING;
8208 case actPassing: return ACTION_PASSING;
8209 case actPushing: return ACTION_PUSHING;
8210 case actDropping: return ACTION_DROPPING;
8212 default: return ACTION_DEFAULT;
8216 int map_element_RND_to_MM(int element_rnd)
8218 return (element_rnd >= EL_MM_START_1 &&
8219 element_rnd <= EL_MM_END_1 ?
8220 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8222 element_rnd >= EL_MM_START_2 &&
8223 element_rnd <= EL_MM_END_2 ?
8224 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8226 element_rnd >= EL_CHAR_START &&
8227 element_rnd <= EL_CHAR_END ?
8228 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8230 element_rnd >= EL_MM_RUNTIME_START &&
8231 element_rnd <= EL_MM_RUNTIME_END ?
8232 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8234 EL_MM_EMPTY_NATIVE);
8237 int map_element_MM_to_RND(int element_mm)
8239 return (element_mm == EL_MM_EMPTY_NATIVE ||
8240 element_mm == EL_DF_EMPTY_NATIVE ?
8243 element_mm >= EL_MM_START_1_NATIVE &&
8244 element_mm <= EL_MM_END_1_NATIVE ?
8245 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8247 element_mm >= EL_MM_START_2_NATIVE &&
8248 element_mm <= EL_MM_END_2_NATIVE ?
8249 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8251 element_mm >= EL_MM_CHAR_START_NATIVE &&
8252 element_mm <= EL_MM_CHAR_END_NATIVE ?
8253 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8255 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8256 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8257 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8262 int map_action_MM_to_RND(int action_mm)
8264 // all MM actions are defined to exactly match their RND counterparts
8268 int map_sound_MM_to_RND(int sound_mm)
8272 case SND_MM_GAME_LEVELTIME_CHARGING:
8273 return SND_GAME_LEVELTIME_CHARGING;
8275 case SND_MM_GAME_HEALTH_CHARGING:
8276 return SND_GAME_HEALTH_CHARGING;
8279 return SND_UNDEFINED;
8283 int map_mm_wall_element(int element)
8285 return (element >= EL_MM_STEEL_WALL_START &&
8286 element <= EL_MM_STEEL_WALL_END ?
8289 element >= EL_MM_WOODEN_WALL_START &&
8290 element <= EL_MM_WOODEN_WALL_END ?
8293 element >= EL_MM_ICE_WALL_START &&
8294 element <= EL_MM_ICE_WALL_END ?
8297 element >= EL_MM_AMOEBA_WALL_START &&
8298 element <= EL_MM_AMOEBA_WALL_END ?
8301 element >= EL_DF_STEEL_WALL_START &&
8302 element <= EL_DF_STEEL_WALL_END ?
8305 element >= EL_DF_WOODEN_WALL_START &&
8306 element <= EL_DF_WOODEN_WALL_END ?
8312 int map_mm_wall_element_editor(int element)
8316 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8317 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8318 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8319 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8320 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8321 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8323 default: return element;
8327 int get_next_element(int element)
8331 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8332 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8333 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8334 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8335 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8336 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8337 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8338 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8339 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8340 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8341 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8343 default: return element;
8347 int el2img_mm(int element_mm)
8349 return el2img(map_element_MM_to_RND(element_mm));
8352 int el_act2img_mm(int element_mm, int action)
8354 return el_act2img(map_element_MM_to_RND(element_mm), action);
8357 int el_act_dir2img(int element, int action, int direction)
8359 element = GFX_ELEMENT(element);
8360 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8362 // direction_graphic[][] == graphic[] for undefined direction graphics
8363 return element_info[element].direction_graphic[action][direction];
8366 static int el_act_dir2crm(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_crumbled[action][direction];
8375 int el_act2img(int element, int action)
8377 element = GFX_ELEMENT(element);
8379 return element_info[element].graphic[action];
8382 int el_act2crm(int element, int action)
8384 element = GFX_ELEMENT(element);
8386 return element_info[element].crumbled[action];
8389 int el_dir2img(int element, int direction)
8391 element = GFX_ELEMENT(element);
8393 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8396 int el2baseimg(int element)
8398 return element_info[element].graphic[ACTION_DEFAULT];
8401 int el2img(int element)
8403 element = GFX_ELEMENT(element);
8405 return element_info[element].graphic[ACTION_DEFAULT];
8408 int el2edimg(int element)
8410 element = GFX_ELEMENT(element);
8412 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8415 int el2preimg(int element)
8417 element = GFX_ELEMENT(element);
8419 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8422 int el2panelimg(int element)
8424 element = GFX_ELEMENT(element);
8426 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8429 int font2baseimg(int font_nr)
8431 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8434 int getBeltNrFromBeltElement(int element)
8436 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8437 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8438 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8441 int getBeltNrFromBeltActiveElement(int element)
8443 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8444 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8445 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8448 int getBeltNrFromBeltSwitchElement(int element)
8450 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8451 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8452 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8455 int getBeltDirNrFromBeltElement(int element)
8457 static int belt_base_element[4] =
8459 EL_CONVEYOR_BELT_1_LEFT,
8460 EL_CONVEYOR_BELT_2_LEFT,
8461 EL_CONVEYOR_BELT_3_LEFT,
8462 EL_CONVEYOR_BELT_4_LEFT
8465 int belt_nr = getBeltNrFromBeltElement(element);
8466 int belt_dir_nr = element - belt_base_element[belt_nr];
8468 return (belt_dir_nr % 3);
8471 int getBeltDirNrFromBeltSwitchElement(int element)
8473 static int belt_base_element[4] =
8475 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8476 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8477 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8478 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8481 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8482 int belt_dir_nr = element - belt_base_element[belt_nr];
8484 return (belt_dir_nr % 3);
8487 int getBeltDirFromBeltElement(int element)
8489 static int belt_move_dir[3] =
8496 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8498 return belt_move_dir[belt_dir_nr];
8501 int getBeltDirFromBeltSwitchElement(int element)
8503 static int belt_move_dir[3] =
8510 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8512 return belt_move_dir[belt_dir_nr];
8515 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8517 static int belt_base_element[4] =
8519 EL_CONVEYOR_BELT_1_LEFT,
8520 EL_CONVEYOR_BELT_2_LEFT,
8521 EL_CONVEYOR_BELT_3_LEFT,
8522 EL_CONVEYOR_BELT_4_LEFT
8525 return belt_base_element[belt_nr] + belt_dir_nr;
8528 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8530 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8532 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8535 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8537 static int belt_base_element[4] =
8539 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8540 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8541 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8542 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8545 return belt_base_element[belt_nr] + belt_dir_nr;
8548 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8550 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8552 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8555 boolean swapTiles_EM(boolean is_pre_emc_cave)
8557 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8560 boolean getTeamMode_EM(void)
8562 return game.team_mode || network_playing;
8565 boolean isActivePlayer_EM(int player_nr)
8567 return stored_player[player_nr].active;
8570 unsigned int InitRND(int seed)
8572 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8573 return InitEngineRandom_EM(seed);
8574 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8575 return InitEngineRandom_SP(seed);
8576 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8577 return InitEngineRandom_MM(seed);
8579 return InitEngineRandom_RND(seed);
8582 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8583 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8585 static int get_effective_element_EM(int tile, int frame_em)
8587 int element = object_mapping[tile].element_rnd;
8588 int action = object_mapping[tile].action;
8589 boolean is_backside = object_mapping[tile].is_backside;
8590 boolean action_removing = (action == ACTION_DIGGING ||
8591 action == ACTION_SNAPPING ||
8592 action == ACTION_COLLECTING);
8600 return (frame_em > 5 ? EL_EMPTY : element);
8606 else // frame_em == 7
8617 case Ydiamond_stone:
8621 case Xdrip_stretchB:
8637 case Ymagnify_blank:
8640 case Xsand_stonein_1:
8641 case Xsand_stonein_2:
8642 case Xsand_stonein_3:
8643 case Xsand_stonein_4:
8647 return (is_backside || action_removing ? EL_EMPTY : element);
8652 static boolean check_linear_animation_EM(int tile)
8656 case Xsand_stonesand_1:
8657 case Xsand_stonesand_quickout_1:
8658 case Xsand_sandstone_1:
8659 case Xsand_stonein_1:
8660 case Xsand_stoneout_1:
8688 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8689 boolean has_crumbled_graphics,
8690 int crumbled, int sync_frame)
8692 // if element can be crumbled, but certain action graphics are just empty
8693 // space (like instantly snapping sand to empty space in 1 frame), do not
8694 // treat these empty space graphics as crumbled graphics in EMC engine
8695 if (crumbled == IMG_EMPTY_SPACE)
8696 has_crumbled_graphics = FALSE;
8698 if (has_crumbled_graphics)
8700 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8701 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8702 g_crumbled->anim_delay,
8703 g_crumbled->anim_mode,
8704 g_crumbled->anim_start_frame,
8707 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8708 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8710 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8711 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8713 g_em->has_crumbled_graphics = TRUE;
8717 g_em->crumbled_bitmap = NULL;
8718 g_em->crumbled_src_x = 0;
8719 g_em->crumbled_src_y = 0;
8720 g_em->crumbled_border_size = 0;
8721 g_em->crumbled_tile_size = 0;
8723 g_em->has_crumbled_graphics = FALSE;
8728 void ResetGfxAnimation_EM(int x, int y, int tile)
8734 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8735 int tile, int frame_em, int x, int y)
8737 int action = object_mapping[tile].action;
8738 int direction = object_mapping[tile].direction;
8739 int effective_element = get_effective_element_EM(tile, frame_em);
8740 int graphic = (direction == MV_NONE ?
8741 el_act2img(effective_element, action) :
8742 el_act_dir2img(effective_element, action, direction));
8743 struct GraphicInfo *g = &graphic_info[graphic];
8745 boolean action_removing = (action == ACTION_DIGGING ||
8746 action == ACTION_SNAPPING ||
8747 action == ACTION_COLLECTING);
8748 boolean action_moving = (action == ACTION_FALLING ||
8749 action == ACTION_MOVING ||
8750 action == ACTION_PUSHING ||
8751 action == ACTION_EATING ||
8752 action == ACTION_FILLING ||
8753 action == ACTION_EMPTYING);
8754 boolean action_falling = (action == ACTION_FALLING ||
8755 action == ACTION_FILLING ||
8756 action == ACTION_EMPTYING);
8758 // special case: graphic uses "2nd movement tile" and has defined
8759 // 7 frames for movement animation (or less) => use default graphic
8760 // for last (8th) frame which ends the movement animation
8761 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8763 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8764 graphic = (direction == MV_NONE ?
8765 el_act2img(effective_element, action) :
8766 el_act_dir2img(effective_element, action, direction));
8768 g = &graphic_info[graphic];
8771 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8775 else if (action_moving)
8777 boolean is_backside = object_mapping[tile].is_backside;
8781 int direction = object_mapping[tile].direction;
8782 int move_dir = (action_falling ? MV_DOWN : direction);
8787 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8788 if (g->double_movement && frame_em == 0)
8792 if (move_dir == MV_LEFT)
8793 GfxFrame[x - 1][y] = GfxFrame[x][y];
8794 else if (move_dir == MV_RIGHT)
8795 GfxFrame[x + 1][y] = GfxFrame[x][y];
8796 else if (move_dir == MV_UP)
8797 GfxFrame[x][y - 1] = GfxFrame[x][y];
8798 else if (move_dir == MV_DOWN)
8799 GfxFrame[x][y + 1] = GfxFrame[x][y];
8806 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8807 if (tile == Xsand_stonesand_quickout_1 ||
8808 tile == Xsand_stonesand_quickout_2)
8812 if (graphic_info[graphic].anim_global_sync)
8813 sync_frame = FrameCounter;
8814 else if (graphic_info[graphic].anim_global_anim_sync)
8815 sync_frame = getGlobalAnimSyncFrame();
8816 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8817 sync_frame = GfxFrame[x][y];
8819 sync_frame = 0; // playfield border (pseudo steel)
8821 SetRandomAnimationValue(x, y);
8823 int frame = getAnimationFrame(g->anim_frames,
8826 g->anim_start_frame,
8829 g_em->unique_identifier =
8830 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8833 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8834 int tile, int frame_em, int x, int y)
8836 int action = object_mapping[tile].action;
8837 int direction = object_mapping[tile].direction;
8838 boolean is_backside = object_mapping[tile].is_backside;
8839 int effective_element = get_effective_element_EM(tile, frame_em);
8840 int effective_action = action;
8841 int graphic = (direction == MV_NONE ?
8842 el_act2img(effective_element, effective_action) :
8843 el_act_dir2img(effective_element, effective_action,
8845 int crumbled = (direction == MV_NONE ?
8846 el_act2crm(effective_element, effective_action) :
8847 el_act_dir2crm(effective_element, effective_action,
8849 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8850 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8851 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8852 struct GraphicInfo *g = &graphic_info[graphic];
8855 // special case: graphic uses "2nd movement tile" and has defined
8856 // 7 frames for movement animation (or less) => use default graphic
8857 // for last (8th) frame which ends the movement animation
8858 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8860 effective_action = ACTION_DEFAULT;
8861 graphic = (direction == MV_NONE ?
8862 el_act2img(effective_element, effective_action) :
8863 el_act_dir2img(effective_element, effective_action,
8865 crumbled = (direction == MV_NONE ?
8866 el_act2crm(effective_element, effective_action) :
8867 el_act_dir2crm(effective_element, effective_action,
8870 g = &graphic_info[graphic];
8873 if (graphic_info[graphic].anim_global_sync)
8874 sync_frame = FrameCounter;
8875 else if (graphic_info[graphic].anim_global_anim_sync)
8876 sync_frame = getGlobalAnimSyncFrame();
8877 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8878 sync_frame = GfxFrame[x][y];
8880 sync_frame = 0; // playfield border (pseudo steel)
8882 SetRandomAnimationValue(x, y);
8884 int frame = getAnimationFrame(g->anim_frames,
8887 g->anim_start_frame,
8890 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8891 g->double_movement && is_backside);
8893 // (updating the "crumbled" graphic definitions is probably not really needed,
8894 // as animations for crumbled graphics can't be longer than one EMC cycle)
8895 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8899 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8900 int player_nr, int anim, int frame_em)
8902 int element = player_mapping[player_nr][anim].element_rnd;
8903 int action = player_mapping[player_nr][anim].action;
8904 int direction = player_mapping[player_nr][anim].direction;
8905 int graphic = (direction == MV_NONE ?
8906 el_act2img(element, action) :
8907 el_act_dir2img(element, action, direction));
8908 struct GraphicInfo *g = &graphic_info[graphic];
8911 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8913 stored_player[player_nr].StepFrame = frame_em;
8915 sync_frame = stored_player[player_nr].Frame;
8917 int frame = getAnimationFrame(g->anim_frames,
8920 g->anim_start_frame,
8923 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8924 &g_em->src_x, &g_em->src_y, FALSE);
8927 void InitGraphicInfo_EM(void)
8931 // always start with reliable default values
8932 for (i = 0; i < GAME_TILE_MAX; i++)
8934 object_mapping[i].element_rnd = EL_UNKNOWN;
8935 object_mapping[i].is_backside = FALSE;
8936 object_mapping[i].action = ACTION_DEFAULT;
8937 object_mapping[i].direction = MV_NONE;
8940 // always start with reliable default values
8941 for (p = 0; p < MAX_PLAYERS; p++)
8943 for (i = 0; i < PLY_MAX; i++)
8945 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8946 player_mapping[p][i].action = ACTION_DEFAULT;
8947 player_mapping[p][i].direction = MV_NONE;
8951 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8953 int e = em_object_mapping_list[i].element_em;
8955 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8956 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8958 if (em_object_mapping_list[i].action != -1)
8959 object_mapping[e].action = em_object_mapping_list[i].action;
8961 if (em_object_mapping_list[i].direction != -1)
8962 object_mapping[e].direction =
8963 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8966 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8968 int a = em_player_mapping_list[i].action_em;
8969 int p = em_player_mapping_list[i].player_nr;
8971 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8973 if (em_player_mapping_list[i].action != -1)
8974 player_mapping[p][a].action = em_player_mapping_list[i].action;
8976 if (em_player_mapping_list[i].direction != -1)
8977 player_mapping[p][a].direction =
8978 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8981 for (i = 0; i < GAME_TILE_MAX; i++)
8983 int element = object_mapping[i].element_rnd;
8984 int action = object_mapping[i].action;
8985 int direction = object_mapping[i].direction;
8986 boolean is_backside = object_mapping[i].is_backside;
8987 boolean action_exploding = ((action == ACTION_EXPLODING ||
8988 action == ACTION_SMASHED_BY_ROCK ||
8989 action == ACTION_SMASHED_BY_SPRING) &&
8990 element != EL_DIAMOND);
8991 boolean action_active = (action == ACTION_ACTIVE);
8992 boolean action_other = (action == ACTION_OTHER);
8994 for (j = 0; j < 8; j++)
8996 int effective_element = get_effective_element_EM(i, j);
8997 int effective_action = (j < 7 ? action :
8998 i == Xdrip_stretch ? action :
8999 i == Xdrip_stretchB ? action :
9000 i == Ydrip_1_s ? action :
9001 i == Ydrip_1_sB ? action :
9002 i == Yball_1 ? action :
9003 i == Xball_2 ? action :
9004 i == Yball_2 ? action :
9005 i == Yball_blank ? action :
9006 i == Ykey_1_blank ? action :
9007 i == Ykey_2_blank ? action :
9008 i == Ykey_3_blank ? action :
9009 i == Ykey_4_blank ? action :
9010 i == Ykey_5_blank ? action :
9011 i == Ykey_6_blank ? action :
9012 i == Ykey_7_blank ? action :
9013 i == Ykey_8_blank ? action :
9014 i == Ylenses_blank ? action :
9015 i == Ymagnify_blank ? action :
9016 i == Ygrass_blank ? action :
9017 i == Ydirt_blank ? action :
9018 i == Xsand_stonein_1 ? action :
9019 i == Xsand_stonein_2 ? action :
9020 i == Xsand_stonein_3 ? action :
9021 i == Xsand_stonein_4 ? action :
9022 i == Xsand_stoneout_1 ? action :
9023 i == Xsand_stoneout_2 ? action :
9024 i == Xboom_android ? ACTION_EXPLODING :
9025 action_exploding ? ACTION_EXPLODING :
9026 action_active ? action :
9027 action_other ? action :
9029 int graphic = (el_act_dir2img(effective_element, effective_action,
9031 int crumbled = (el_act_dir2crm(effective_element, effective_action,
9033 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
9034 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
9035 boolean has_action_graphics = (graphic != base_graphic);
9036 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9037 struct GraphicInfo *g = &graphic_info[graphic];
9038 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9041 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9042 boolean special_animation = (action != ACTION_DEFAULT &&
9043 g->anim_frames == 3 &&
9044 g->anim_delay == 2 &&
9045 g->anim_mode & ANIM_LINEAR);
9046 int sync_frame = (i == Xdrip_stretch ? 7 :
9047 i == Xdrip_stretchB ? 7 :
9048 i == Ydrip_2_s ? j + 8 :
9049 i == Ydrip_2_sB ? j + 8 :
9058 i == Xfake_acid_1 ? 0 :
9059 i == Xfake_acid_2 ? 10 :
9060 i == Xfake_acid_3 ? 20 :
9061 i == Xfake_acid_4 ? 30 :
9062 i == Xfake_acid_5 ? 40 :
9063 i == Xfake_acid_6 ? 50 :
9064 i == Xfake_acid_7 ? 60 :
9065 i == Xfake_acid_8 ? 70 :
9066 i == Xfake_acid_1_player ? 0 :
9067 i == Xfake_acid_2_player ? 10 :
9068 i == Xfake_acid_3_player ? 20 :
9069 i == Xfake_acid_4_player ? 30 :
9070 i == Xfake_acid_5_player ? 40 :
9071 i == Xfake_acid_6_player ? 50 :
9072 i == Xfake_acid_7_player ? 60 :
9073 i == Xfake_acid_8_player ? 70 :
9075 i == Yball_2 ? j + 8 :
9076 i == Yball_blank ? j + 1 :
9077 i == Ykey_1_blank ? j + 1 :
9078 i == Ykey_2_blank ? j + 1 :
9079 i == Ykey_3_blank ? j + 1 :
9080 i == Ykey_4_blank ? j + 1 :
9081 i == Ykey_5_blank ? j + 1 :
9082 i == Ykey_6_blank ? j + 1 :
9083 i == Ykey_7_blank ? j + 1 :
9084 i == Ykey_8_blank ? j + 1 :
9085 i == Ylenses_blank ? j + 1 :
9086 i == Ymagnify_blank ? j + 1 :
9087 i == Ygrass_blank ? j + 1 :
9088 i == Ydirt_blank ? j + 1 :
9089 i == Xamoeba_1 ? 0 :
9090 i == Xamoeba_2 ? 1 :
9091 i == Xamoeba_3 ? 2 :
9092 i == Xamoeba_4 ? 3 :
9093 i == Xamoeba_5 ? 0 :
9094 i == Xamoeba_6 ? 1 :
9095 i == Xamoeba_7 ? 2 :
9096 i == Xamoeba_8 ? 3 :
9097 i == Xexit_2 ? j + 8 :
9098 i == Xexit_3 ? j + 16 :
9099 i == Xdynamite_1 ? 0 :
9100 i == Xdynamite_2 ? 8 :
9101 i == Xdynamite_3 ? 16 :
9102 i == Xdynamite_4 ? 24 :
9103 i == Xsand_stonein_1 ? j + 1 :
9104 i == Xsand_stonein_2 ? j + 9 :
9105 i == Xsand_stonein_3 ? j + 17 :
9106 i == Xsand_stonein_4 ? j + 25 :
9107 i == Xsand_stoneout_1 && j == 0 ? 0 :
9108 i == Xsand_stoneout_1 && j == 1 ? 0 :
9109 i == Xsand_stoneout_1 && j == 2 ? 1 :
9110 i == Xsand_stoneout_1 && j == 3 ? 2 :
9111 i == Xsand_stoneout_1 && j == 4 ? 2 :
9112 i == Xsand_stoneout_1 && j == 5 ? 3 :
9113 i == Xsand_stoneout_1 && j == 6 ? 4 :
9114 i == Xsand_stoneout_1 && j == 7 ? 4 :
9115 i == Xsand_stoneout_2 && j == 0 ? 5 :
9116 i == Xsand_stoneout_2 && j == 1 ? 6 :
9117 i == Xsand_stoneout_2 && j == 2 ? 7 :
9118 i == Xsand_stoneout_2 && j == 3 ? 8 :
9119 i == Xsand_stoneout_2 && j == 4 ? 9 :
9120 i == Xsand_stoneout_2 && j == 5 ? 11 :
9121 i == Xsand_stoneout_2 && j == 6 ? 13 :
9122 i == Xsand_stoneout_2 && j == 7 ? 15 :
9123 i == Xboom_bug && j == 1 ? 2 :
9124 i == Xboom_bug && j == 2 ? 2 :
9125 i == Xboom_bug && j == 3 ? 4 :
9126 i == Xboom_bug && j == 4 ? 4 :
9127 i == Xboom_bug && j == 5 ? 2 :
9128 i == Xboom_bug && j == 6 ? 2 :
9129 i == Xboom_bug && j == 7 ? 0 :
9130 i == Xboom_tank && j == 1 ? 2 :
9131 i == Xboom_tank && j == 2 ? 2 :
9132 i == Xboom_tank && j == 3 ? 4 :
9133 i == Xboom_tank && j == 4 ? 4 :
9134 i == Xboom_tank && j == 5 ? 2 :
9135 i == Xboom_tank && j == 6 ? 2 :
9136 i == Xboom_tank && j == 7 ? 0 :
9137 i == Xboom_android && j == 7 ? 6 :
9138 i == Xboom_1 && j == 1 ? 2 :
9139 i == Xboom_1 && j == 2 ? 2 :
9140 i == Xboom_1 && j == 3 ? 4 :
9141 i == Xboom_1 && j == 4 ? 4 :
9142 i == Xboom_1 && j == 5 ? 6 :
9143 i == Xboom_1 && j == 6 ? 6 :
9144 i == Xboom_1 && j == 7 ? 8 :
9145 i == Xboom_2 && j == 0 ? 8 :
9146 i == Xboom_2 && j == 1 ? 8 :
9147 i == Xboom_2 && j == 2 ? 10 :
9148 i == Xboom_2 && j == 3 ? 10 :
9149 i == Xboom_2 && j == 4 ? 10 :
9150 i == Xboom_2 && j == 5 ? 12 :
9151 i == Xboom_2 && j == 6 ? 12 :
9152 i == Xboom_2 && j == 7 ? 12 :
9153 special_animation && j == 4 ? 3 :
9154 effective_action != action ? 0 :
9156 int frame = getAnimationFrame(g->anim_frames,
9159 g->anim_start_frame,
9162 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9163 g->double_movement && is_backside);
9165 g_em->bitmap = src_bitmap;
9166 g_em->src_x = src_x;
9167 g_em->src_y = src_y;
9168 g_em->src_offset_x = 0;
9169 g_em->src_offset_y = 0;
9170 g_em->dst_offset_x = 0;
9171 g_em->dst_offset_y = 0;
9172 g_em->width = TILEX;
9173 g_em->height = TILEY;
9175 g_em->preserve_background = FALSE;
9177 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9180 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9181 effective_action == ACTION_MOVING ||
9182 effective_action == ACTION_PUSHING ||
9183 effective_action == ACTION_EATING)) ||
9184 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9185 effective_action == ACTION_EMPTYING)))
9188 (effective_action == ACTION_FALLING ||
9189 effective_action == ACTION_FILLING ||
9190 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9191 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9192 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9193 int num_steps = (i == Ydrip_1_s ? 16 :
9194 i == Ydrip_1_sB ? 16 :
9195 i == Ydrip_2_s ? 16 :
9196 i == Ydrip_2_sB ? 16 :
9197 i == Xsand_stonein_1 ? 32 :
9198 i == Xsand_stonein_2 ? 32 :
9199 i == Xsand_stonein_3 ? 32 :
9200 i == Xsand_stonein_4 ? 32 :
9201 i == Xsand_stoneout_1 ? 16 :
9202 i == Xsand_stoneout_2 ? 16 : 8);
9203 int cx = ABS(dx) * (TILEX / num_steps);
9204 int cy = ABS(dy) * (TILEY / num_steps);
9205 int step_frame = (i == Ydrip_2_s ? j + 8 :
9206 i == Ydrip_2_sB ? j + 8 :
9207 i == Xsand_stonein_2 ? j + 8 :
9208 i == Xsand_stonein_3 ? j + 16 :
9209 i == Xsand_stonein_4 ? j + 24 :
9210 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9211 int step = (is_backside ? step_frame : num_steps - step_frame);
9213 if (is_backside) // tile where movement starts
9215 if (dx < 0 || dy < 0)
9217 g_em->src_offset_x = cx * step;
9218 g_em->src_offset_y = cy * step;
9222 g_em->dst_offset_x = cx * step;
9223 g_em->dst_offset_y = cy * step;
9226 else // tile where movement ends
9228 if (dx < 0 || dy < 0)
9230 g_em->dst_offset_x = cx * step;
9231 g_em->dst_offset_y = cy * step;
9235 g_em->src_offset_x = cx * step;
9236 g_em->src_offset_y = cy * step;
9240 g_em->width = TILEX - cx * step;
9241 g_em->height = TILEY - cy * step;
9244 // create unique graphic identifier to decide if tile must be redrawn
9245 /* bit 31 - 16 (16 bit): EM style graphic
9246 bit 15 - 12 ( 4 bit): EM style frame
9247 bit 11 - 6 ( 6 bit): graphic width
9248 bit 5 - 0 ( 6 bit): graphic height */
9249 g_em->unique_identifier =
9250 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9254 for (i = 0; i < GAME_TILE_MAX; i++)
9256 for (j = 0; j < 8; j++)
9258 int element = object_mapping[i].element_rnd;
9259 int action = object_mapping[i].action;
9260 int direction = object_mapping[i].direction;
9261 boolean is_backside = object_mapping[i].is_backside;
9262 int graphic_action = el_act_dir2img(element, action, direction);
9263 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9265 if ((action == ACTION_SMASHED_BY_ROCK ||
9266 action == ACTION_SMASHED_BY_SPRING ||
9267 action == ACTION_EATING) &&
9268 graphic_action == graphic_default)
9270 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9271 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9272 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9273 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9276 // no separate animation for "smashed by rock" -- use rock instead
9277 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9278 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9280 g_em->bitmap = g_xx->bitmap;
9281 g_em->src_x = g_xx->src_x;
9282 g_em->src_y = g_xx->src_y;
9283 g_em->src_offset_x = g_xx->src_offset_x;
9284 g_em->src_offset_y = g_xx->src_offset_y;
9285 g_em->dst_offset_x = g_xx->dst_offset_x;
9286 g_em->dst_offset_y = g_xx->dst_offset_y;
9287 g_em->width = g_xx->width;
9288 g_em->height = g_xx->height;
9289 g_em->unique_identifier = g_xx->unique_identifier;
9292 g_em->preserve_background = TRUE;
9297 for (p = 0; p < MAX_PLAYERS; p++)
9299 for (i = 0; i < PLY_MAX; i++)
9301 int element = player_mapping[p][i].element_rnd;
9302 int action = player_mapping[p][i].action;
9303 int direction = player_mapping[p][i].direction;
9305 for (j = 0; j < 8; j++)
9307 int effective_element = element;
9308 int effective_action = action;
9309 int graphic = (direction == MV_NONE ?
9310 el_act2img(effective_element, effective_action) :
9311 el_act_dir2img(effective_element, effective_action,
9313 struct GraphicInfo *g = &graphic_info[graphic];
9314 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9318 int frame = getAnimationFrame(g->anim_frames,
9321 g->anim_start_frame,
9324 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9326 g_em->bitmap = src_bitmap;
9327 g_em->src_x = src_x;
9328 g_em->src_y = src_y;
9329 g_em->src_offset_x = 0;
9330 g_em->src_offset_y = 0;
9331 g_em->dst_offset_x = 0;
9332 g_em->dst_offset_y = 0;
9333 g_em->width = TILEX;
9334 g_em->height = TILEY;
9340 static void CheckSaveEngineSnapshot_EM(int frame,
9341 boolean any_player_moving,
9342 boolean any_player_snapping,
9343 boolean any_player_dropping)
9345 if (frame == 7 && !any_player_dropping)
9347 if (!local_player->was_waiting)
9349 if (!CheckSaveEngineSnapshotToList())
9352 local_player->was_waiting = TRUE;
9355 else if (any_player_moving || any_player_snapping || any_player_dropping)
9357 local_player->was_waiting = FALSE;
9361 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9362 boolean murphy_is_dropping)
9364 if (murphy_is_waiting)
9366 if (!local_player->was_waiting)
9368 if (!CheckSaveEngineSnapshotToList())
9371 local_player->was_waiting = TRUE;
9376 local_player->was_waiting = FALSE;
9380 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9381 boolean button_released)
9383 if (button_released)
9385 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9386 CheckSaveEngineSnapshotToList();
9388 else if (element_clicked)
9390 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9391 CheckSaveEngineSnapshotToList();
9393 game.snapshot.changed_action = TRUE;
9397 boolean CheckSingleStepMode_EM(int frame,
9398 boolean any_player_moving,
9399 boolean any_player_snapping,
9400 boolean any_player_dropping)
9402 if (tape.single_step && tape.recording && !tape.pausing)
9403 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9404 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9406 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9407 any_player_snapping, any_player_dropping);
9409 return tape.pausing;
9412 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9413 boolean murphy_is_dropping)
9415 boolean murphy_starts_dropping = FALSE;
9418 for (i = 0; i < MAX_PLAYERS; i++)
9419 if (stored_player[i].force_dropping)
9420 murphy_starts_dropping = TRUE;
9422 if (tape.single_step && tape.recording && !tape.pausing)
9423 if (murphy_is_waiting && !murphy_starts_dropping)
9424 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9426 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9429 void CheckSingleStepMode_MM(boolean element_clicked,
9430 boolean button_released)
9432 if (tape.single_step && tape.recording && !tape.pausing)
9433 if (button_released)
9434 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9436 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9439 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9440 int graphic, int sync_frame)
9442 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9444 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9447 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9449 return (IS_NEXT_FRAME(sync_frame, graphic));
9452 int getGraphicInfo_Delay(int graphic)
9454 return graphic_info[graphic].anim_delay;
9457 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9459 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9462 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9468 void PlayMenuSoundExt(int sound)
9470 if (sound == SND_UNDEFINED)
9473 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9474 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9477 if (IS_LOOP_SOUND(sound))
9478 PlaySoundLoop(sound);
9483 void PlayMenuSound(void)
9485 PlayMenuSoundExt(menu.sound[game_status]);
9488 void PlayMenuSoundStereo(int sound, int stereo_position)
9490 if (sound == SND_UNDEFINED)
9493 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9494 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9497 if (IS_LOOP_SOUND(sound))
9498 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9500 PlaySoundStereo(sound, stereo_position);
9503 void PlayMenuSoundIfLoopExt(int sound)
9505 if (sound == SND_UNDEFINED)
9508 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9509 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9512 if (IS_LOOP_SOUND(sound))
9513 PlaySoundLoop(sound);
9516 void PlayMenuSoundIfLoop(void)
9518 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9521 void PlayMenuMusicExt(int music)
9523 if (music == MUS_UNDEFINED)
9526 if (!setup.sound_music)
9529 if (IS_LOOP_MUSIC(music))
9530 PlayMusicLoop(music);
9535 void PlayMenuMusic(void)
9537 char *curr_music = getCurrentlyPlayingMusicFilename();
9538 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9540 if (!strEqual(curr_music, next_music))
9541 PlayMenuMusicExt(menu.music[game_status]);
9544 void PlayMenuSoundsAndMusic(void)
9550 static void FadeMenuSounds(void)
9555 static void FadeMenuMusic(void)
9557 char *curr_music = getCurrentlyPlayingMusicFilename();
9558 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9560 if (!strEqual(curr_music, next_music))
9564 void FadeMenuSoundsAndMusic(void)
9570 void PlaySoundActivating(void)
9573 PlaySound(SND_MENU_ITEM_ACTIVATING);
9577 void PlaySoundSelecting(void)
9580 PlaySound(SND_MENU_ITEM_SELECTING);
9584 void ToggleFullscreenIfNeeded(void)
9586 // if setup and video fullscreen state are already matching, nothing do do
9587 if (setup.fullscreen == video.fullscreen_enabled ||
9588 !video.fullscreen_available)
9591 SDLSetWindowFullscreen(setup.fullscreen);
9593 // set setup value according to successfully changed fullscreen mode
9594 setup.fullscreen = video.fullscreen_enabled;
9597 void ChangeWindowScalingIfNeeded(void)
9599 // if setup and video window scaling are already matching, nothing do do
9600 if (setup.window_scaling_percent == video.window_scaling_percent ||
9601 video.fullscreen_enabled)
9604 SDLSetWindowScaling(setup.window_scaling_percent);
9606 // set setup value according to successfully changed window scaling
9607 setup.window_scaling_percent = video.window_scaling_percent;
9610 void ChangeVsyncModeIfNeeded(void)
9612 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9613 int video_vsync_mode = video.vsync_mode;
9615 // if setup and video vsync mode are already matching, nothing do do
9616 if (setup_vsync_mode == video_vsync_mode)
9619 // if renderer is using OpenGL, vsync mode can directly be changed
9620 SDLSetScreenVsyncMode(setup.vsync_mode);
9622 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9623 if (video.vsync_mode == video_vsync_mode)
9625 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9627 // save backbuffer content which gets lost when re-creating screen
9628 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9630 // force re-creating screen and renderer to set new vsync mode
9631 video.fullscreen_enabled = !setup.fullscreen;
9633 // when creating new renderer, destroy textures linked to old renderer
9634 FreeAllImageTextures(); // needs old renderer to free the textures
9636 // re-create screen and renderer (including change of vsync mode)
9637 ChangeVideoModeIfNeeded(setup.fullscreen);
9639 // set setup value according to successfully changed fullscreen mode
9640 setup.fullscreen = video.fullscreen_enabled;
9642 // restore backbuffer content from temporary backbuffer backup bitmap
9643 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9644 FreeBitmap(tmp_backbuffer);
9646 // update visible window/screen
9647 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9649 // when changing vsync mode, re-create textures for new renderer
9650 InitImageTextures();
9653 // set setup value according to successfully changed vsync mode
9654 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9657 static void JoinRectangles(int *x, int *y, int *width, int *height,
9658 int x2, int y2, int width2, int height2)
9660 // do not join with "off-screen" rectangle
9661 if (x2 == -1 || y2 == -1)
9666 *width = MAX(*width, width2);
9667 *height = MAX(*height, height2);
9670 void SetAnimStatus(int anim_status_new)
9672 if (anim_status_new == GAME_MODE_MAIN)
9673 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9674 else if (anim_status_new == GAME_MODE_NAMES)
9675 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9676 else if (anim_status_new == GAME_MODE_SCORES)
9677 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9679 global.anim_status_next = anim_status_new;
9681 // directly set screen modes that are entered without fading
9682 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9683 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9684 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9685 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9686 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9687 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9688 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9689 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9690 global.anim_status = global.anim_status_next;
9693 void SetGameStatus(int game_status_new)
9695 if (game_status_new != game_status)
9696 game_status_last_screen = game_status;
9698 game_status = game_status_new;
9700 SetAnimStatus(game_status_new);
9703 void SetFontStatus(int game_status_new)
9705 static int last_game_status = -1;
9707 if (game_status_new != -1)
9709 // set game status for font use after storing last game status
9710 last_game_status = game_status;
9711 game_status = game_status_new;
9715 // reset game status after font use from last stored game status
9716 game_status = last_game_status;
9720 void ResetFontStatus(void)
9725 void SetLevelSetInfo(char *identifier, int level_nr)
9727 setString(&levelset.identifier, identifier);
9729 levelset.level_nr = level_nr;
9732 boolean CheckIfAllViewportsHaveChanged(void)
9734 // if game status has not changed, viewports have not changed either
9735 if (game_status == game_status_last)
9738 // check if all viewports have changed with current game status
9740 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9741 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9742 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9743 int new_real_sx = vp_playfield->x;
9744 int new_real_sy = vp_playfield->y;
9745 int new_full_sxsize = vp_playfield->width;
9746 int new_full_sysize = vp_playfield->height;
9747 int new_dx = vp_door_1->x;
9748 int new_dy = vp_door_1->y;
9749 int new_dxsize = vp_door_1->width;
9750 int new_dysize = vp_door_1->height;
9751 int new_vx = vp_door_2->x;
9752 int new_vy = vp_door_2->y;
9753 int new_vxsize = vp_door_2->width;
9754 int new_vysize = vp_door_2->height;
9756 boolean playfield_viewport_has_changed =
9757 (new_real_sx != REAL_SX ||
9758 new_real_sy != REAL_SY ||
9759 new_full_sxsize != FULL_SXSIZE ||
9760 new_full_sysize != FULL_SYSIZE);
9762 boolean door_1_viewport_has_changed =
9765 new_dxsize != DXSIZE ||
9766 new_dysize != DYSIZE);
9768 boolean door_2_viewport_has_changed =
9771 new_vxsize != VXSIZE ||
9772 new_vysize != VYSIZE ||
9773 game_status_last == GAME_MODE_EDITOR);
9775 return (playfield_viewport_has_changed &&
9776 door_1_viewport_has_changed &&
9777 door_2_viewport_has_changed);
9780 boolean CheckFadeAll(void)
9782 return (CheckIfGlobalBorderHasChanged() ||
9783 CheckIfAllViewportsHaveChanged());
9786 void ChangeViewportPropertiesIfNeeded(void)
9788 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9789 FALSE : setup.small_game_graphics);
9790 int gfx_game_mode = getGlobalGameStatus(game_status);
9791 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9793 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9794 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9795 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9796 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9797 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9798 int new_win_xsize = vp_window->width;
9799 int new_win_ysize = vp_window->height;
9800 int border_left = vp_playfield->border_left;
9801 int border_right = vp_playfield->border_right;
9802 int border_top = vp_playfield->border_top;
9803 int border_bottom = vp_playfield->border_bottom;
9804 int new_sx = vp_playfield->x + border_left;
9805 int new_sy = vp_playfield->y + border_top;
9806 int new_sxsize = vp_playfield->width - border_left - border_right;
9807 int new_sysize = vp_playfield->height - border_top - border_bottom;
9808 int new_real_sx = vp_playfield->x;
9809 int new_real_sy = vp_playfield->y;
9810 int new_full_sxsize = vp_playfield->width;
9811 int new_full_sysize = vp_playfield->height;
9812 int new_dx = vp_door_1->x;
9813 int new_dy = vp_door_1->y;
9814 int new_dxsize = vp_door_1->width;
9815 int new_dysize = vp_door_1->height;
9816 int new_vx = vp_door_2->x;
9817 int new_vy = vp_door_2->y;
9818 int new_vxsize = vp_door_2->width;
9819 int new_vysize = vp_door_2->height;
9820 int new_ex = vp_door_3->x;
9821 int new_ey = vp_door_3->y;
9822 int new_exsize = vp_door_3->width;
9823 int new_eysize = vp_door_3->height;
9824 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9825 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9826 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9827 int new_scr_fieldx = new_sxsize / tilesize;
9828 int new_scr_fieldy = new_sysize / tilesize;
9829 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9830 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9831 boolean init_gfx_buffers = FALSE;
9832 boolean init_video_buffer = FALSE;
9833 boolean init_gadgets_and_anims = FALSE;
9834 boolean init_em_graphics = FALSE;
9836 if (new_win_xsize != WIN_XSIZE ||
9837 new_win_ysize != WIN_YSIZE)
9839 WIN_XSIZE = new_win_xsize;
9840 WIN_YSIZE = new_win_ysize;
9842 init_video_buffer = TRUE;
9843 init_gfx_buffers = TRUE;
9844 init_gadgets_and_anims = TRUE;
9846 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9849 if (new_scr_fieldx != SCR_FIELDX ||
9850 new_scr_fieldy != SCR_FIELDY)
9852 // this always toggles between MAIN and GAME when using small tile size
9854 SCR_FIELDX = new_scr_fieldx;
9855 SCR_FIELDY = new_scr_fieldy;
9857 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9868 new_sxsize != SXSIZE ||
9869 new_sysize != SYSIZE ||
9870 new_dxsize != DXSIZE ||
9871 new_dysize != DYSIZE ||
9872 new_vxsize != VXSIZE ||
9873 new_vysize != VYSIZE ||
9874 new_exsize != EXSIZE ||
9875 new_eysize != EYSIZE ||
9876 new_real_sx != REAL_SX ||
9877 new_real_sy != REAL_SY ||
9878 new_full_sxsize != FULL_SXSIZE ||
9879 new_full_sysize != FULL_SYSIZE ||
9880 new_tilesize_var != TILESIZE_VAR
9883 // ------------------------------------------------------------------------
9884 // determine next fading area for changed viewport definitions
9885 // ------------------------------------------------------------------------
9887 // start with current playfield area (default fading area)
9890 FADE_SXSIZE = FULL_SXSIZE;
9891 FADE_SYSIZE = FULL_SYSIZE;
9893 // add new playfield area if position or size has changed
9894 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9895 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9897 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9898 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9901 // add current and new door 1 area if position or size has changed
9902 if (new_dx != DX || new_dy != DY ||
9903 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9905 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9906 DX, DY, DXSIZE, DYSIZE);
9907 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9908 new_dx, new_dy, new_dxsize, new_dysize);
9911 // add current and new door 2 area if position or size has changed
9912 if (new_vx != VX || new_vy != VY ||
9913 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9915 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9916 VX, VY, VXSIZE, VYSIZE);
9917 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9918 new_vx, new_vy, new_vxsize, new_vysize);
9921 // ------------------------------------------------------------------------
9922 // handle changed tile size
9923 // ------------------------------------------------------------------------
9925 if (new_tilesize_var != TILESIZE_VAR)
9927 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9929 // changing tile size invalidates scroll values of engine snapshots
9930 FreeEngineSnapshotSingle();
9932 // changing tile size requires update of graphic mapping for EM engine
9933 init_em_graphics = TRUE;
9944 SXSIZE = new_sxsize;
9945 SYSIZE = new_sysize;
9946 DXSIZE = new_dxsize;
9947 DYSIZE = new_dysize;
9948 VXSIZE = new_vxsize;
9949 VYSIZE = new_vysize;
9950 EXSIZE = new_exsize;
9951 EYSIZE = new_eysize;
9952 REAL_SX = new_real_sx;
9953 REAL_SY = new_real_sy;
9954 FULL_SXSIZE = new_full_sxsize;
9955 FULL_SYSIZE = new_full_sysize;
9956 TILESIZE_VAR = new_tilesize_var;
9958 init_gfx_buffers = TRUE;
9959 init_gadgets_and_anims = TRUE;
9961 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9962 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9965 if (init_gfx_buffers)
9967 // Debug("tools:viewport", "init_gfx_buffers");
9969 SCR_FIELDX = new_scr_fieldx_buffers;
9970 SCR_FIELDY = new_scr_fieldy_buffers;
9974 SCR_FIELDX = new_scr_fieldx;
9975 SCR_FIELDY = new_scr_fieldy;
9977 SetDrawDeactivationMask(REDRAW_NONE);
9978 SetDrawBackgroundMask(REDRAW_FIELD);
9981 if (init_video_buffer)
9983 // Debug("tools:viewport", "init_video_buffer");
9985 FreeAllImageTextures(); // needs old renderer to free the textures
9987 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9988 InitImageTextures();
9991 if (init_gadgets_and_anims)
9993 // Debug("tools:viewport", "init_gadgets_and_anims");
9996 InitGlobalAnimations();
9999 if (init_em_graphics)
10001 InitGraphicInfo_EM();
10005 void OpenURL(char *url)
10007 #if SDL_VERSION_ATLEAST(2,0,14)
10010 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
10011 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
10012 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
10016 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
10018 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
10022 // ============================================================================
10024 // ============================================================================
10026 #if defined(PLATFORM_WINDOWS)
10027 /* FILETIME of Jan 1 1970 00:00:00. */
10028 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
10031 * timezone information is stored outside the kernel so tzp isn't used anymore.
10033 * Note: this function is not for Win32 high precision timing purpose. See
10036 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
10038 FILETIME file_time;
10039 SYSTEMTIME system_time;
10040 ULARGE_INTEGER ularge;
10042 GetSystemTime(&system_time);
10043 SystemTimeToFileTime(&system_time, &file_time);
10044 ularge.LowPart = file_time.dwLowDateTime;
10045 ularge.HighPart = file_time.dwHighDateTime;
10047 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10048 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10054 static char *test_init_uuid_random_function_simple(void)
10056 static char seed_text[100];
10057 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10059 sprintf(seed_text, "%d", seed);
10064 static char *test_init_uuid_random_function_better(void)
10066 static char seed_text[100];
10067 struct timeval current_time;
10069 gettimeofday(¤t_time, NULL);
10071 prng_seed_bytes(¤t_time, sizeof(current_time));
10073 sprintf(seed_text, "%ld.%ld",
10074 (long)current_time.tv_sec,
10075 (long)current_time.tv_usec);
10080 #if defined(PLATFORM_WINDOWS)
10081 static char *test_init_uuid_random_function_better_windows(void)
10083 static char seed_text[100];
10084 struct timeval current_time;
10086 gettimeofday_windows(¤t_time, NULL);
10088 prng_seed_bytes(¤t_time, sizeof(current_time));
10090 sprintf(seed_text, "%ld.%ld",
10091 (long)current_time.tv_sec,
10092 (long)current_time.tv_usec);
10098 static unsigned int test_uuid_random_function_simple(int max)
10100 return GetSimpleRandom(max);
10103 static unsigned int test_uuid_random_function_better(int max)
10105 return (max > 0 ? prng_get_uint() % max : 0);
10108 #if defined(PLATFORM_WINDOWS)
10109 #define NUM_UUID_TESTS 3
10111 #define NUM_UUID_TESTS 2
10114 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10116 struct hashtable *hash_seeds =
10117 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10118 struct hashtable *hash_uuids =
10119 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10120 static char message[100];
10123 char *random_name = (nr == 0 ? "simple" : "better");
10124 char *random_type = (always_seed ? "always" : "only once");
10125 char *(*init_random_function)(void) =
10127 test_init_uuid_random_function_simple :
10128 test_init_uuid_random_function_better);
10129 unsigned int (*random_function)(int) =
10131 test_uuid_random_function_simple :
10132 test_uuid_random_function_better);
10135 #if defined(PLATFORM_WINDOWS)
10138 random_name = "windows";
10139 init_random_function = test_init_uuid_random_function_better_windows;
10145 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10146 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10148 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10149 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10150 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10152 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10156 // always initialize random number generator at least once
10157 init_random_function();
10159 unsigned int time_start = SDL_GetTicks();
10161 for (i = 0; i < num_uuids; i++)
10165 char *seed = getStringCopy(init_random_function());
10167 hashtable_remove(hash_seeds, seed);
10168 hashtable_insert(hash_seeds, seed, "1");
10171 char *uuid = getStringCopy(getUUIDExt(random_function));
10173 hashtable_remove(hash_uuids, uuid);
10174 hashtable_insert(hash_uuids, uuid, "1");
10177 int num_unique_seeds = hashtable_count(hash_seeds);
10178 int num_unique_uuids = hashtable_count(hash_uuids);
10180 unsigned int time_needed = SDL_GetTicks() - time_start;
10182 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10184 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10187 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10189 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10190 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10192 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10194 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10196 Request(message, REQ_CONFIRM);
10198 hashtable_destroy(hash_seeds, 0);
10199 hashtable_destroy(hash_uuids, 0);
10202 void TestGeneratingUUIDs(void)
10204 int num_uuids = 1000000;
10207 for (i = 0; i < NUM_UUID_TESTS; i++)
10208 for (j = 0; j < 2; j++)
10209 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10211 CloseAllAndExit(0);