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);
3016 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
3017 int xsize, int ysize)
3019 if (!global.use_envelope_request ||
3020 request.sort_priority <= 0)
3023 if (request.bitmap == NULL ||
3024 xsize > request.xsize ||
3025 ysize > request.ysize)
3027 if (request.bitmap != NULL)
3028 FreeBitmap(request.bitmap);
3030 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
3032 SDL_Surface *surface = request.bitmap->surface;
3034 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
3035 Fail("SDLGetNativeSurface() failed");
3038 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3040 SDLFreeBitmapTextures(request.bitmap);
3041 SDLCreateBitmapTextures(request.bitmap);
3043 // set envelope request run-time values
3046 request.xsize = xsize;
3047 request.ysize = ysize;
3050 void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage)
3052 if (global.use_envelope_request &&
3053 game.request_active_or_moving &&
3054 request.sort_priority > 0 &&
3055 drawing_target == DRAW_TO_SCREEN &&
3056 drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
3058 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3059 request.sx, request.sy);
3063 static void setRequestBasePosition(int *x, int *y)
3065 int sx_base, sy_base;
3067 if (request.x != -1)
3068 sx_base = request.x;
3069 else if (request.align == ALIGN_LEFT)
3071 else if (request.align == ALIGN_RIGHT)
3072 sx_base = SX + SXSIZE;
3074 sx_base = SX + SXSIZE / 2;
3076 if (request.y != -1)
3077 sy_base = request.y;
3078 else if (request.valign == VALIGN_TOP)
3080 else if (request.valign == VALIGN_BOTTOM)
3081 sy_base = SY + SYSIZE;
3083 sy_base = SY + SYSIZE / 2;
3089 static void setRequestPositionExt(int *x, int *y, int width, int height,
3090 boolean add_border_size)
3092 int border_size = request.border_size;
3093 int sx_base, sy_base;
3096 setRequestBasePosition(&sx_base, &sy_base);
3098 if (request.align == ALIGN_LEFT)
3100 else if (request.align == ALIGN_RIGHT)
3101 sx = sx_base - width;
3103 sx = sx_base - width / 2;
3105 if (request.valign == VALIGN_TOP)
3107 else if (request.valign == VALIGN_BOTTOM)
3108 sy = sy_base - height;
3110 sy = sy_base - height / 2;
3112 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3113 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3115 if (add_border_size)
3125 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3127 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3130 static void DrawEnvelopeRequest(char *text)
3132 char *text_final = text;
3133 char *text_door_style = NULL;
3134 int graphic = IMG_BACKGROUND_REQUEST;
3135 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3136 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3137 int font_nr = FONT_REQUEST;
3138 int font_width = getFontWidth(font_nr);
3139 int font_height = getFontHeight(font_nr);
3140 int border_size = request.border_size;
3141 int line_spacing = request.line_spacing;
3142 int line_height = font_height + line_spacing;
3143 int max_text_width = request.width - 2 * border_size;
3144 int max_text_height = request.height - 2 * border_size;
3145 int line_length = max_text_width / font_width;
3146 int max_lines = max_text_height / line_height;
3147 int text_width = line_length * font_width;
3148 int width = request.width;
3149 int height = request.height;
3150 int tile_size = MAX(request.step_offset, 1);
3151 int x_steps = width / tile_size;
3152 int y_steps = height / tile_size;
3153 int sx_offset = border_size;
3154 int sy_offset = border_size;
3158 if (request.centered)
3159 sx_offset = (request.width - text_width) / 2;
3161 if (request.wrap_single_words && !request.autowrap)
3163 char *src_text_ptr, *dst_text_ptr;
3165 text_door_style = checked_malloc(2 * strlen(text) + 1);
3167 src_text_ptr = text;
3168 dst_text_ptr = text_door_style;
3170 while (*src_text_ptr)
3172 if (*src_text_ptr == ' ' ||
3173 *src_text_ptr == '?' ||
3174 *src_text_ptr == '!')
3175 *dst_text_ptr++ = '\n';
3177 if (*src_text_ptr != ' ')
3178 *dst_text_ptr++ = *src_text_ptr;
3183 *dst_text_ptr = '\0';
3185 text_final = text_door_style;
3188 setRequestPosition(&sx, &sy, FALSE);
3190 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3192 for (y = 0; y < y_steps; y++)
3193 for (x = 0; x < x_steps; x++)
3194 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3195 x, y, x_steps, y_steps,
3196 tile_size, tile_size);
3198 // force DOOR font inside door area
3199 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3201 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3202 line_length, -1, max_lines, line_spacing, mask_mode,
3203 request.autowrap, request.centered, FALSE);
3207 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3208 RedrawGadget(tool_gadget[i]);
3210 // store readily prepared envelope request for later use when animating
3211 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3213 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3215 if (text_door_style)
3216 free(text_door_style);
3219 static void AnimateEnvelopeRequest(int anim_mode, int action)
3221 int graphic = IMG_BACKGROUND_REQUEST;
3222 boolean draw_masked = graphic_info[graphic].draw_masked;
3223 int delay_value_normal = request.step_delay;
3224 int delay_value_fast = delay_value_normal / 2;
3225 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3226 boolean no_delay = (tape.warp_forward);
3227 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3228 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3229 DelayCounter anim_delay = { anim_delay_value };
3231 int tile_size = MAX(request.step_offset, 1);
3232 int max_xsize = request.width / tile_size;
3233 int max_ysize = request.height / tile_size;
3234 int max_xsize_inner = max_xsize - 2;
3235 int max_ysize_inner = max_ysize - 2;
3237 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3238 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3239 int xend = max_xsize_inner;
3240 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3241 int xstep = (xstart < xend ? 1 : 0);
3242 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3244 int end = MAX(xend - xstart, yend - ystart);
3247 if (setup.quick_doors)
3254 for (i = start; i <= end; i++)
3256 int last_frame = end; // last frame of this "for" loop
3257 int x = xstart + i * xstep;
3258 int y = ystart + i * ystep;
3259 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3260 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3261 int xsize_size_left = (xsize - 1) * tile_size;
3262 int ysize_size_top = (ysize - 1) * tile_size;
3263 int max_xsize_pos = (max_xsize - 1) * tile_size;
3264 int max_ysize_pos = (max_ysize - 1) * tile_size;
3265 int width = xsize * tile_size;
3266 int height = ysize * tile_size;
3271 setRequestPosition(&src_x, &src_y, FALSE);
3272 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3274 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3276 for (yy = 0; yy < 2; yy++)
3278 for (xx = 0; xx < 2; xx++)
3280 int src_xx = src_x + xx * max_xsize_pos;
3281 int src_yy = src_y + yy * max_ysize_pos;
3282 int dst_xx = dst_x + xx * xsize_size_left;
3283 int dst_yy = dst_y + yy * ysize_size_top;
3284 int xx_size = (xx ? tile_size : xsize_size_left);
3285 int yy_size = (yy ? tile_size : ysize_size_top);
3288 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3289 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3291 BlitBitmap(bitmap_db_store_2, backbuffer,
3292 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3296 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3298 redraw_mask |= REDRAW_FIELD;
3302 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3305 ClearAutoRepeatKeyEvents();
3308 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3310 int graphic = IMG_BACKGROUND_REQUEST;
3311 int sound_opening = SND_REQUEST_OPENING;
3312 int sound_closing = SND_REQUEST_CLOSING;
3313 int anim_mode_1 = request.anim_mode; // (higher priority)
3314 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3315 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3316 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3317 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3319 if (game_status == GAME_MODE_PLAYING)
3320 BlitScreenToBitmap(backbuffer);
3322 SetDrawtoField(DRAW_TO_BACKBUFFER);
3324 // SetDrawBackgroundMask(REDRAW_NONE);
3326 if (action == ACTION_OPENING)
3328 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3330 if (req_state & REQ_ASK)
3332 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3333 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3334 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3335 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3337 else if (req_state & REQ_CONFIRM)
3339 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3340 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3342 else if (req_state & REQ_PLAYER)
3344 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3345 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3346 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3347 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3350 DrawEnvelopeRequest(text);
3353 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3355 if (action == ACTION_OPENING)
3357 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3359 if (anim_mode == ANIM_DEFAULT)
3360 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3362 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3366 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3368 if (anim_mode != ANIM_NONE)
3369 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3371 if (anim_mode == ANIM_DEFAULT)
3372 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3375 game.envelope_active = FALSE;
3377 if (action == ACTION_CLOSING)
3378 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3380 // SetDrawBackgroundMask(last_draw_background_mask);
3382 redraw_mask |= REDRAW_FIELD;
3386 if (action == ACTION_CLOSING &&
3387 game_status == GAME_MODE_PLAYING &&
3388 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3389 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3392 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3394 if (IS_MM_WALL(element))
3396 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3402 int graphic = el2preimg(element);
3404 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3405 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3410 void DrawLevel(int draw_background_mask)
3414 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3415 SetDrawBackgroundMask(draw_background_mask);
3419 for (x = BX1; x <= BX2; x++)
3420 for (y = BY1; y <= BY2; y++)
3421 DrawScreenField(x, y);
3423 redraw_mask |= REDRAW_FIELD;
3426 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3431 for (x = 0; x < size_x; x++)
3432 for (y = 0; y < size_y; y++)
3433 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3435 redraw_mask |= REDRAW_FIELD;
3438 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3442 for (x = 0; x < size_x; x++)
3443 for (y = 0; y < size_y; y++)
3444 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3446 redraw_mask |= REDRAW_FIELD;
3449 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3451 boolean show_level_border = (BorderElement != EL_EMPTY);
3452 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3453 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3454 int tile_size = preview.tile_size;
3455 int preview_width = preview.xsize * tile_size;
3456 int preview_height = preview.ysize * tile_size;
3457 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3458 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3459 int real_preview_width = real_preview_xsize * tile_size;
3460 int real_preview_height = real_preview_ysize * tile_size;
3461 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3462 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3465 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3468 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3470 dst_x += (preview_width - real_preview_width) / 2;
3471 dst_y += (preview_height - real_preview_height) / 2;
3473 for (x = 0; x < real_preview_xsize; x++)
3475 for (y = 0; y < real_preview_ysize; y++)
3477 int lx = from_x + x + (show_level_border ? -1 : 0);
3478 int ly = from_y + y + (show_level_border ? -1 : 0);
3479 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3480 getBorderElement(lx, ly));
3482 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3483 element, tile_size);
3487 redraw_mask |= REDRAW_FIELD;
3490 #define MICROLABEL_EMPTY 0
3491 #define MICROLABEL_LEVEL_NAME 1
3492 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3493 #define MICROLABEL_LEVEL_AUTHOR 3
3494 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3495 #define MICROLABEL_IMPORTED_FROM 5
3496 #define MICROLABEL_IMPORTED_BY_HEAD 6
3497 #define MICROLABEL_IMPORTED_BY 7
3499 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3501 int max_text_width = SXSIZE;
3502 int font_width = getFontWidth(font_nr);
3504 if (pos->align == ALIGN_CENTER)
3505 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3506 else if (pos->align == ALIGN_RIGHT)
3507 max_text_width = pos->x;
3509 max_text_width = SXSIZE - pos->x;
3511 return max_text_width / font_width;
3514 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3516 char label_text[MAX_OUTPUT_LINESIZE + 1];
3517 int max_len_label_text;
3518 int font_nr = pos->font;
3521 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3524 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3525 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3526 mode == MICROLABEL_IMPORTED_BY_HEAD)
3527 font_nr = pos->font_alt;
3529 max_len_label_text = getMaxTextLength(pos, font_nr);
3531 if (pos->size != -1)
3532 max_len_label_text = pos->size;
3534 for (i = 0; i < max_len_label_text; i++)
3535 label_text[i] = ' ';
3536 label_text[max_len_label_text] = '\0';
3538 if (strlen(label_text) > 0)
3539 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3542 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3543 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3544 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3545 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3546 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3547 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3548 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3549 max_len_label_text);
3550 label_text[max_len_label_text] = '\0';
3552 if (strlen(label_text) > 0)
3553 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3555 redraw_mask |= REDRAW_FIELD;
3558 static void DrawPreviewLevelLabel(int mode)
3560 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3563 static void DrawPreviewLevelInfo(int mode)
3565 if (mode == MICROLABEL_LEVEL_NAME)
3566 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3567 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3568 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3571 static void DrawPreviewLevelExt(boolean restart)
3573 static DelayCounter scroll_delay = { 0 };
3574 static DelayCounter label_delay = { 0 };
3575 static int from_x, from_y, scroll_direction;
3576 static int label_state, label_counter;
3577 boolean show_level_border = (BorderElement != EL_EMPTY);
3578 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3579 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3581 scroll_delay.value = preview.step_delay;
3582 label_delay.value = MICROLEVEL_LABEL_DELAY;
3589 if (preview.anim_mode == ANIM_CENTERED)
3591 if (level_xsize > preview.xsize)
3592 from_x = (level_xsize - preview.xsize) / 2;
3593 if (level_ysize > preview.ysize)
3594 from_y = (level_ysize - preview.ysize) / 2;
3597 from_x += preview.xoffset;
3598 from_y += preview.yoffset;
3600 scroll_direction = MV_RIGHT;
3604 DrawPreviewLevelPlayfield(from_x, from_y);
3605 DrawPreviewLevelLabel(label_state);
3607 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3608 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3610 // initialize delay counters
3611 ResetDelayCounter(&scroll_delay);
3612 ResetDelayCounter(&label_delay);
3614 if (leveldir_current->name)
3616 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3617 char label_text[MAX_OUTPUT_LINESIZE + 1];
3618 int font_nr = pos->font;
3619 int max_len_label_text = getMaxTextLength(pos, font_nr);
3621 if (pos->size != -1)
3622 max_len_label_text = pos->size;
3624 strncpy(label_text, leveldir_current->name, max_len_label_text);
3625 label_text[max_len_label_text] = '\0';
3627 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3628 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3634 // scroll preview level, if needed
3635 if (preview.anim_mode != ANIM_NONE &&
3636 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3637 DelayReached(&scroll_delay))
3639 switch (scroll_direction)
3644 from_x -= preview.step_offset;
3645 from_x = (from_x < 0 ? 0 : from_x);
3648 scroll_direction = MV_UP;
3652 if (from_x < level_xsize - preview.xsize)
3654 from_x += preview.step_offset;
3655 from_x = (from_x > level_xsize - preview.xsize ?
3656 level_xsize - preview.xsize : from_x);
3659 scroll_direction = MV_DOWN;
3665 from_y -= preview.step_offset;
3666 from_y = (from_y < 0 ? 0 : from_y);
3669 scroll_direction = MV_RIGHT;
3673 if (from_y < level_ysize - preview.ysize)
3675 from_y += preview.step_offset;
3676 from_y = (from_y > level_ysize - preview.ysize ?
3677 level_ysize - preview.ysize : from_y);
3680 scroll_direction = MV_LEFT;
3687 DrawPreviewLevelPlayfield(from_x, from_y);
3690 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3691 // redraw micro level label, if needed
3692 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3693 !strEqual(level.author, ANONYMOUS_NAME) &&
3694 !strEqual(level.author, leveldir_current->name) &&
3695 DelayReached(&label_delay))
3697 int max_label_counter = 23;
3699 if (leveldir_current->imported_from != NULL &&
3700 strlen(leveldir_current->imported_from) > 0)
3701 max_label_counter += 14;
3702 if (leveldir_current->imported_by != NULL &&
3703 strlen(leveldir_current->imported_by) > 0)
3704 max_label_counter += 14;
3706 label_counter = (label_counter + 1) % max_label_counter;
3707 label_state = (label_counter >= 0 && label_counter <= 7 ?
3708 MICROLABEL_LEVEL_NAME :
3709 label_counter >= 9 && label_counter <= 12 ?
3710 MICROLABEL_LEVEL_AUTHOR_HEAD :
3711 label_counter >= 14 && label_counter <= 21 ?
3712 MICROLABEL_LEVEL_AUTHOR :
3713 label_counter >= 23 && label_counter <= 26 ?
3714 MICROLABEL_IMPORTED_FROM_HEAD :
3715 label_counter >= 28 && label_counter <= 35 ?
3716 MICROLABEL_IMPORTED_FROM :
3717 label_counter >= 37 && label_counter <= 40 ?
3718 MICROLABEL_IMPORTED_BY_HEAD :
3719 label_counter >= 42 && label_counter <= 49 ?
3720 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3722 if (leveldir_current->imported_from == NULL &&
3723 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3724 label_state == MICROLABEL_IMPORTED_FROM))
3725 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3726 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3728 DrawPreviewLevelLabel(label_state);
3732 void DrawPreviewPlayers(void)
3734 if (game_status != GAME_MODE_MAIN)
3737 // do not draw preview players if level preview redefined, but players aren't
3738 if (preview.redefined && !menu.main.preview_players.redefined)
3741 boolean player_found[MAX_PLAYERS];
3742 int num_players = 0;
3745 for (i = 0; i < MAX_PLAYERS; i++)
3746 player_found[i] = FALSE;
3748 // check which players can be found in the level (simple approach)
3749 for (x = 0; x < lev_fieldx; x++)
3751 for (y = 0; y < lev_fieldy; y++)
3753 int element = level.field[x][y];
3755 if (IS_PLAYER_ELEMENT(element))
3757 int player_nr = GET_PLAYER_NR(element);
3759 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3761 if (!player_found[player_nr])
3764 player_found[player_nr] = TRUE;
3769 struct TextPosInfo *pos = &menu.main.preview_players;
3770 int tile_size = pos->tile_size;
3771 int border_size = pos->border_size;
3772 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3773 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3774 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3775 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3776 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3777 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3778 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3779 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3780 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3781 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3782 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3783 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3785 // clear area in which the players will be drawn
3786 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3787 max_players_width, max_players_height);
3789 if (!network.enabled && !setup.team_mode)
3792 // only draw players if level is suited for team mode
3793 if (num_players < 2)
3796 // draw all players that were found in the level
3797 for (i = 0; i < MAX_PLAYERS; i++)
3799 if (player_found[i])
3801 int graphic = el2img(EL_PLAYER_1 + i);
3803 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3805 xpos += player_xoffset;
3806 ypos += player_yoffset;
3811 void DrawPreviewLevelInitial(void)
3813 DrawPreviewLevelExt(TRUE);
3814 DrawPreviewPlayers();
3817 void DrawPreviewLevelAnimation(void)
3819 DrawPreviewLevelExt(FALSE);
3822 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3823 int border_size, int font_nr)
3825 int graphic = el2img(EL_PLAYER_1 + player_nr);
3826 int font_height = getFontHeight(font_nr);
3827 int player_height = MAX(tile_size, font_height);
3828 int xoffset_text = tile_size + border_size;
3829 int yoffset_text = (player_height - font_height) / 2;
3830 int yoffset_graphic = (player_height - tile_size) / 2;
3831 char *player_name = getNetworkPlayerName(player_nr + 1);
3833 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3835 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3838 static void DrawNetworkPlayersExt(boolean force)
3840 if (game_status != GAME_MODE_MAIN)
3843 if (!network.connected && !force)
3846 // do not draw network players if level preview redefined, but players aren't
3847 if (preview.redefined && !menu.main.network_players.redefined)
3850 int num_players = 0;
3853 for (i = 0; i < MAX_PLAYERS; i++)
3854 if (stored_player[i].connected_network)
3857 struct TextPosInfo *pos = &menu.main.network_players;
3858 int tile_size = pos->tile_size;
3859 int border_size = pos->border_size;
3860 int xoffset_text = tile_size + border_size;
3861 int font_nr = pos->font;
3862 int font_width = getFontWidth(font_nr);
3863 int font_height = getFontHeight(font_nr);
3864 int player_height = MAX(tile_size, font_height);
3865 int player_yoffset = player_height + border_size;
3866 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3867 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3868 int all_players_height = num_players * player_yoffset - border_size;
3869 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3870 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3871 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3873 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3874 max_players_width, max_players_height);
3876 // first draw local network player ...
3877 for (i = 0; i < MAX_PLAYERS; i++)
3879 if (stored_player[i].connected_network &&
3880 stored_player[i].connected_locally)
3882 char *player_name = getNetworkPlayerName(i + 1);
3883 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3884 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3886 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3888 ypos += player_yoffset;
3892 // ... then draw all other network players
3893 for (i = 0; i < MAX_PLAYERS; i++)
3895 if (stored_player[i].connected_network &&
3896 !stored_player[i].connected_locally)
3898 char *player_name = getNetworkPlayerName(i + 1);
3899 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3900 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3902 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3904 ypos += player_yoffset;
3909 void DrawNetworkPlayers(void)
3911 DrawNetworkPlayersExt(FALSE);
3914 void ClearNetworkPlayers(void)
3916 DrawNetworkPlayersExt(TRUE);
3919 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3920 int graphic, int lx, int ly,
3923 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3925 if (mask_mode == USE_MASKING)
3926 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3928 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3931 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3932 int graphic, int sync_frame, int mask_mode)
3934 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3936 if (mask_mode == USE_MASKING)
3937 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3939 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3942 static void DrawGraphicAnimation(int x, int y, int graphic)
3944 int lx = LEVELX(x), ly = LEVELY(y);
3945 int mask_mode = NO_MASKING;
3947 if (!IN_SCR_FIELD(x, y))
3950 if (game.use_masked_elements)
3952 if (Tile[lx][ly] != EL_EMPTY)
3954 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3956 mask_mode = USE_MASKING;
3960 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3961 graphic, lx, ly, mask_mode);
3963 MarkTileDirty(x, y);
3966 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3968 int lx = LEVELX(x), ly = LEVELY(y);
3969 int mask_mode = NO_MASKING;
3971 if (!IN_SCR_FIELD(x, y))
3974 if (game.use_masked_elements)
3976 if (Tile[lx][ly] != EL_EMPTY)
3978 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3980 mask_mode = USE_MASKING;
3984 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3985 graphic, lx, ly, mask_mode);
3987 MarkTileDirty(x, y);
3990 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3992 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3995 void DrawLevelElementAnimation(int x, int y, int element)
3997 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3999 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4002 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
4004 int sx = SCREENX(x), sy = SCREENY(y);
4006 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4009 if (Tile[x][y] == EL_EMPTY)
4010 graphic = el2img(GfxElementEmpty[x][y]);
4012 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4015 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4018 DrawGraphicAnimation(sx, sy, graphic);
4021 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4022 DrawLevelFieldCrumbled(x, y);
4024 if (GFX_CRUMBLED(Tile[x][y]))
4025 DrawLevelFieldCrumbled(x, y);
4029 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4031 int sx = SCREENX(x), sy = SCREENY(y);
4034 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4037 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4039 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4042 DrawGraphicAnimation(sx, sy, graphic);
4044 if (GFX_CRUMBLED(element))
4045 DrawLevelFieldCrumbled(x, y);
4048 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4050 if (player->use_murphy)
4052 // this works only because currently only one player can be "murphy" ...
4053 static int last_horizontal_dir = MV_LEFT;
4054 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4056 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4057 last_horizontal_dir = move_dir;
4059 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4061 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4063 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4069 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
4072 static boolean equalGraphics(int graphic1, int graphic2)
4074 struct GraphicInfo *g1 = &graphic_info[graphic1];
4075 struct GraphicInfo *g2 = &graphic_info[graphic2];
4077 return (g1->bitmap == g2->bitmap &&
4078 g1->src_x == g2->src_x &&
4079 g1->src_y == g2->src_y &&
4080 g1->anim_frames == g2->anim_frames &&
4081 g1->anim_delay == g2->anim_delay &&
4082 g1->anim_mode == g2->anim_mode);
4085 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4089 DRAW_PLAYER_STAGE_INIT = 0,
4090 DRAW_PLAYER_STAGE_LAST_FIELD,
4091 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4092 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4093 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4094 DRAW_PLAYER_STAGE_PLAYER,
4096 DRAW_PLAYER_STAGE_PLAYER,
4097 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4099 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4100 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4102 NUM_DRAW_PLAYER_STAGES
4105 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4107 static int static_last_player_graphic[MAX_PLAYERS];
4108 static int static_last_player_frame[MAX_PLAYERS];
4109 static boolean static_player_is_opaque[MAX_PLAYERS];
4110 static boolean draw_player[MAX_PLAYERS];
4111 int pnr = player->index_nr;
4113 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4115 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4116 static_last_player_frame[pnr] = player->Frame;
4117 static_player_is_opaque[pnr] = FALSE;
4119 draw_player[pnr] = TRUE;
4122 if (!draw_player[pnr])
4126 if (!IN_LEV_FIELD(player->jx, player->jy))
4128 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4129 Debug("draw:DrawPlayerExt", "This should never happen!");
4131 draw_player[pnr] = FALSE;
4137 int last_player_graphic = static_last_player_graphic[pnr];
4138 int last_player_frame = static_last_player_frame[pnr];
4139 boolean player_is_opaque = static_player_is_opaque[pnr];
4141 int jx = player->jx;
4142 int jy = player->jy;
4143 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4144 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4145 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4146 int last_jx = (player->is_moving ? jx - dx : jx);
4147 int last_jy = (player->is_moving ? jy - dy : jy);
4148 int next_jx = jx + dx;
4149 int next_jy = jy + dy;
4150 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4151 int sx = SCREENX(jx);
4152 int sy = SCREENY(jy);
4153 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4154 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4155 int element = Tile[jx][jy];
4156 int last_element = Tile[last_jx][last_jy];
4157 int action = (player->is_pushing ? ACTION_PUSHING :
4158 player->is_digging ? ACTION_DIGGING :
4159 player->is_collecting ? ACTION_COLLECTING :
4160 player->is_moving ? ACTION_MOVING :
4161 player->is_snapping ? ACTION_SNAPPING :
4162 player->is_dropping ? ACTION_DROPPING :
4163 player->is_waiting ? player->action_waiting :
4166 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4168 // ------------------------------------------------------------------------
4169 // initialize drawing the player
4170 // ------------------------------------------------------------------------
4172 draw_player[pnr] = FALSE;
4174 // GfxElement[][] is set to the element the player is digging or collecting;
4175 // remove also for off-screen player if the player is not moving anymore
4176 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4177 GfxElement[jx][jy] = EL_UNDEFINED;
4179 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4182 if (element == EL_EXPLOSION)
4185 InitPlayerGfxAnimation(player, action, move_dir);
4187 draw_player[pnr] = TRUE;
4189 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4191 // ------------------------------------------------------------------------
4192 // draw things in the field the player is leaving, if needed
4193 // ------------------------------------------------------------------------
4195 if (!IN_SCR_FIELD(sx, sy))
4196 draw_player[pnr] = FALSE;
4198 if (!player->is_moving)
4201 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4203 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4205 if (last_element == EL_DYNAMITE_ACTIVE ||
4206 last_element == EL_EM_DYNAMITE_ACTIVE ||
4207 last_element == EL_SP_DISK_RED_ACTIVE)
4208 DrawDynamite(last_jx, last_jy);
4210 DrawLevelFieldThruMask(last_jx, last_jy);
4212 else if (last_element == EL_DYNAMITE_ACTIVE ||
4213 last_element == EL_EM_DYNAMITE_ACTIVE ||
4214 last_element == EL_SP_DISK_RED_ACTIVE)
4215 DrawDynamite(last_jx, last_jy);
4217 DrawLevelField(last_jx, last_jy);
4219 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4221 // ------------------------------------------------------------------------
4222 // draw things behind the player, if needed
4223 // ------------------------------------------------------------------------
4227 DrawLevelElement(jx, jy, Back[jx][jy]);
4232 if (IS_ACTIVE_BOMB(element))
4234 DrawLevelElement(jx, jy, EL_EMPTY);
4239 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4241 int old_element = GfxElement[jx][jy];
4242 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4243 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4245 if (GFX_CRUMBLED(old_element))
4246 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4248 DrawScreenGraphic(sx, sy, old_graphic, frame);
4250 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4251 static_player_is_opaque[pnr] = TRUE;
4255 GfxElement[jx][jy] = EL_UNDEFINED;
4257 // make sure that pushed elements are drawn with correct frame rate
4258 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4260 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4261 GfxFrame[jx][jy] = player->StepFrame;
4263 DrawLevelField(jx, jy);
4266 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4268 // ------------------------------------------------------------------------
4269 // draw things the player is pushing, if needed
4270 // ------------------------------------------------------------------------
4272 if (!player->is_pushing || !player->is_moving)
4275 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4278 int gfx_frame = GfxFrame[jx][jy];
4280 if (!IS_MOVING(jx, jy)) // push movement already finished
4282 element = Tile[next_jx][next_jy];
4283 gfx_frame = GfxFrame[next_jx][next_jy];
4286 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4287 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4288 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4290 // draw background element under pushed element (like the Sokoban field)
4291 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4293 // this allows transparent pushing animation over non-black background
4296 DrawLevelElement(jx, jy, Back[jx][jy]);
4298 DrawLevelElement(jx, jy, EL_EMPTY);
4301 if (Back[next_jx][next_jy])
4302 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4304 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4306 int px = SCREENX(jx), py = SCREENY(jy);
4307 int pxx = (TILEX - ABS(sxx)) * dx;
4308 int pyy = (TILEY - ABS(syy)) * dy;
4311 // do not draw (EM style) pushing animation when pushing is finished
4312 // (two-tile animations usually do not contain start and end frame)
4313 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4314 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4316 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4318 // masked drawing is needed for EMC style (double) movement graphics
4319 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4320 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4323 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4325 // ------------------------------------------------------------------------
4326 // draw player himself
4327 // ------------------------------------------------------------------------
4329 int graphic = getPlayerGraphic(player, move_dir);
4331 // in the case of changed player action or direction, prevent the current
4332 // animation frame from being restarted for identical animations
4333 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4334 player->Frame = last_player_frame;
4336 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4338 if (player_is_opaque)
4339 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4341 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4343 if (SHIELD_ON(player))
4345 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4346 IMG_SHIELD_NORMAL_ACTIVE);
4347 frame = getGraphicAnimationFrame(graphic, -1);
4349 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4352 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4354 // ------------------------------------------------------------------------
4355 // draw things in front of player (active dynamite or dynabombs)
4356 // ------------------------------------------------------------------------
4358 if (IS_ACTIVE_BOMB(element))
4360 int graphic = el2img(element);
4361 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4363 if (game.emulation == EMU_SUPAPLEX)
4364 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4366 DrawGraphicThruMask(sx, sy, graphic, frame);
4369 if (player_is_moving && last_element == EL_EXPLOSION)
4371 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4372 GfxElement[last_jx][last_jy] : EL_EMPTY);
4373 int graphic = el_act2img(element, ACTION_EXPLODING);
4374 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4375 int phase = ExplodePhase[last_jx][last_jy] - 1;
4376 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4379 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4382 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4384 // ------------------------------------------------------------------------
4385 // draw elements the player is just walking/passing through/under
4386 // ------------------------------------------------------------------------
4388 if (player_is_moving)
4390 // handle the field the player is leaving ...
4391 if (IS_ACCESSIBLE_INSIDE(last_element))
4392 DrawLevelField(last_jx, last_jy);
4393 else if (IS_ACCESSIBLE_UNDER(last_element))
4394 DrawLevelFieldThruMask(last_jx, last_jy);
4397 // do not redraw accessible elements if the player is just pushing them
4398 if (!player_is_moving || !player->is_pushing)
4400 // ... and the field the player is entering
4401 if (IS_ACCESSIBLE_INSIDE(element))
4402 DrawLevelField(jx, jy);
4403 else if (IS_ACCESSIBLE_UNDER(element))
4404 DrawLevelFieldThruMask(jx, jy);
4407 MarkTileDirty(sx, sy);
4411 void DrawPlayer(struct PlayerInfo *player)
4415 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4416 DrawPlayerExt(player, i);
4419 void DrawAllPlayers(void)
4423 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4424 for (j = 0; j < MAX_PLAYERS; j++)
4425 if (stored_player[j].active)
4426 DrawPlayerExt(&stored_player[j], i);
4429 void DrawPlayerField(int x, int y)
4431 if (!IS_PLAYER(x, y))
4434 DrawPlayer(PLAYERINFO(x, y));
4437 // ----------------------------------------------------------------------------
4439 void WaitForEventToContinue(void)
4441 boolean first_wait = TRUE;
4442 boolean still_wait = TRUE;
4444 if (program.headless)
4447 // simulate releasing mouse button over last gadget, if still pressed
4449 HandleGadgets(-1, -1, 0);
4451 button_status = MB_RELEASED;
4454 ClearPlayerAction();
4460 if (NextValidEvent(&event))
4464 case EVENT_BUTTONPRESS:
4465 case EVENT_FINGERPRESS:
4469 case EVENT_BUTTONRELEASE:
4470 case EVENT_FINGERRELEASE:
4471 still_wait = first_wait;
4474 case EVENT_KEYPRESS:
4475 case SDL_CONTROLLERBUTTONDOWN:
4476 case SDL_JOYBUTTONDOWN:
4481 HandleOtherEvents(&event);
4485 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4490 if (!PendingEvent())
4495 #define MAX_REQUEST_LINES 13
4496 #define MAX_REQUEST_LINE_FONT1_LEN 7
4497 #define MAX_REQUEST_LINE_FONT2_LEN 10
4499 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4501 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4503 int draw_buffer_last = GetDrawtoField();
4504 int width = request.width;
4505 int height = request.height;
4509 // when showing request dialog after game ended, deactivate game panel
4510 if (game_just_ended)
4511 game.panel.active = FALSE;
4513 game.request_active = TRUE;
4515 setRequestPosition(&sx, &sy, FALSE);
4517 button_status = MB_RELEASED;
4519 request_gadget_id = -1;
4524 boolean event_handled = FALSE;
4526 if (game_just_ended)
4528 SetDrawtoField(draw_buffer_game);
4530 HandleGameActions();
4532 SetDrawtoField(DRAW_TO_BACKBUFFER);
4534 if (global.use_envelope_request)
4536 // copy current state of request area to middle of playfield area
4537 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4545 while (NextValidEvent(&event))
4547 event_handled = TRUE;
4551 case EVENT_BUTTONPRESS:
4552 case EVENT_BUTTONRELEASE:
4553 case EVENT_MOTIONNOTIFY:
4557 if (event.type == EVENT_MOTIONNOTIFY)
4562 motion_status = TRUE;
4563 mx = ((MotionEvent *) &event)->x;
4564 my = ((MotionEvent *) &event)->y;
4568 motion_status = FALSE;
4569 mx = ((ButtonEvent *) &event)->x;
4570 my = ((ButtonEvent *) &event)->y;
4571 if (event.type == EVENT_BUTTONPRESS)
4572 button_status = ((ButtonEvent *) &event)->button;
4574 button_status = MB_RELEASED;
4577 // this sets 'request_gadget_id'
4578 HandleGadgets(mx, my, button_status);
4580 switch (request_gadget_id)
4582 case TOOL_CTRL_ID_YES:
4583 case TOOL_CTRL_ID_TOUCH_YES:
4586 case TOOL_CTRL_ID_NO:
4587 case TOOL_CTRL_ID_TOUCH_NO:
4590 case TOOL_CTRL_ID_CONFIRM:
4591 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4592 result = TRUE | FALSE;
4595 case TOOL_CTRL_ID_PLAYER_1:
4598 case TOOL_CTRL_ID_PLAYER_2:
4601 case TOOL_CTRL_ID_PLAYER_3:
4604 case TOOL_CTRL_ID_PLAYER_4:
4609 // only check clickable animations if no request gadget clicked
4610 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4617 case SDL_WINDOWEVENT:
4618 HandleWindowEvent((WindowEvent *) &event);
4621 case SDL_APP_WILLENTERBACKGROUND:
4622 case SDL_APP_DIDENTERBACKGROUND:
4623 case SDL_APP_WILLENTERFOREGROUND:
4624 case SDL_APP_DIDENTERFOREGROUND:
4625 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4628 case EVENT_KEYPRESS:
4630 Key key = GetEventKey((KeyEvent *)&event);
4635 if (req_state & REQ_CONFIRM)
4644 #if defined(KSYM_Rewind)
4645 case KSYM_Rewind: // for Amazon Fire TV remote
4654 #if defined(KSYM_FastForward)
4655 case KSYM_FastForward: // for Amazon Fire TV remote
4661 HandleKeysDebug(key, KEY_PRESSED);
4665 if (req_state & REQ_PLAYER)
4667 int old_player_nr = setup.network_player_nr;
4670 result = old_player_nr + 1;
4675 result = old_player_nr + 1;
4706 case EVENT_FINGERRELEASE:
4707 case EVENT_KEYRELEASE:
4708 ClearPlayerAction();
4711 case SDL_CONTROLLERBUTTONDOWN:
4712 switch (event.cbutton.button)
4714 case SDL_CONTROLLER_BUTTON_A:
4715 case SDL_CONTROLLER_BUTTON_X:
4716 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4717 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4721 case SDL_CONTROLLER_BUTTON_B:
4722 case SDL_CONTROLLER_BUTTON_Y:
4723 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4724 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4725 case SDL_CONTROLLER_BUTTON_BACK:
4730 if (req_state & REQ_PLAYER)
4732 int old_player_nr = setup.network_player_nr;
4735 result = old_player_nr + 1;
4737 switch (event.cbutton.button)
4739 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4740 case SDL_CONTROLLER_BUTTON_Y:
4744 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4745 case SDL_CONTROLLER_BUTTON_B:
4749 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4750 case SDL_CONTROLLER_BUTTON_A:
4754 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4755 case SDL_CONTROLLER_BUTTON_X:
4766 case SDL_CONTROLLERBUTTONUP:
4767 HandleJoystickEvent(&event);
4768 ClearPlayerAction();
4772 HandleOtherEvents(&event);
4777 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4779 int joy = AnyJoystick();
4781 if (joy & JOY_BUTTON_1)
4783 else if (joy & JOY_BUTTON_2)
4786 else if (AnyJoystick())
4788 int joy = AnyJoystick();
4790 if (req_state & REQ_PLAYER)
4794 else if (joy & JOY_RIGHT)
4796 else if (joy & JOY_DOWN)
4798 else if (joy & JOY_LEFT)
4805 if (game_just_ended)
4807 if (global.use_envelope_request)
4809 // copy back current state of pressed buttons inside request area
4810 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4814 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4820 SetDrawtoField(draw_buffer_last);
4822 game.request_active = FALSE;
4827 static boolean RequestDoor(char *text, unsigned int req_state)
4829 int draw_buffer_last = GetDrawtoField();
4830 unsigned int old_door_state;
4831 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4832 int font_nr = FONT_TEXT_2;
4837 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4839 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4840 font_nr = FONT_TEXT_1;
4843 if (game_status == GAME_MODE_PLAYING)
4844 BlitScreenToBitmap(backbuffer);
4846 // disable deactivated drawing when quick-loading level tape recording
4847 if (tape.playing && tape.deactivate_display)
4848 TapeDeactivateDisplayOff(TRUE);
4850 SetMouseCursor(CURSOR_DEFAULT);
4852 // pause network game while waiting for request to answer
4853 if (network.enabled &&
4854 game_status == GAME_MODE_PLAYING &&
4855 !game.all_players_gone &&
4856 req_state & REQUEST_WAIT_FOR_INPUT)
4857 SendToServer_PausePlaying();
4859 old_door_state = GetDoorState();
4861 // simulate releasing mouse button over last gadget, if still pressed
4863 HandleGadgets(-1, -1, 0);
4867 // draw released gadget before proceeding
4870 if (old_door_state & DOOR_OPEN_1)
4872 CloseDoor(DOOR_CLOSE_1);
4874 // save old door content
4875 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4876 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4879 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4880 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4882 // clear door drawing field
4883 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4885 // force DOOR font inside door area
4886 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4888 // write text for request
4889 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4891 char text_line[max_request_line_len + 1];
4897 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4899 tc = *(text_ptr + tx);
4900 // if (!tc || tc == ' ')
4901 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4905 if ((tc == '?' || tc == '!') && tl == 0)
4915 strncpy(text_line, text_ptr, tl);
4918 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4919 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4920 text_line, font_nr);
4922 text_ptr += tl + (tc == ' ' ? 1 : 0);
4923 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4928 if (req_state & REQ_ASK)
4930 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4931 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4932 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4933 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4935 else if (req_state & REQ_CONFIRM)
4937 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4938 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4940 else if (req_state & REQ_PLAYER)
4942 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4943 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4944 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4945 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4948 // copy request gadgets to door backbuffer
4949 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4951 OpenDoor(DOOR_OPEN_1);
4953 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4955 if (game_status == GAME_MODE_PLAYING)
4957 SetPanelBackground();
4958 SetDrawBackgroundMask(REDRAW_DOOR_1);
4962 SetDrawBackgroundMask(REDRAW_FIELD);
4968 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4970 // ---------- handle request buttons ----------
4971 result = RequestHandleEvents(req_state, draw_buffer_last);
4975 if (!(req_state & REQ_STAY_OPEN))
4977 CloseDoor(DOOR_CLOSE_1);
4979 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4980 (req_state & REQ_REOPEN))
4981 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4986 if (game_status == GAME_MODE_PLAYING)
4988 SetPanelBackground();
4989 SetDrawBackgroundMask(REDRAW_DOOR_1);
4993 SetDrawBackgroundMask(REDRAW_FIELD);
4996 // continue network game after request
4997 if (network.enabled &&
4998 game_status == GAME_MODE_PLAYING &&
4999 !game.all_players_gone &&
5000 req_state & REQUEST_WAIT_FOR_INPUT)
5001 SendToServer_ContinuePlaying();
5003 // restore deactivated drawing when quick-loading level tape recording
5004 if (tape.playing && tape.deactivate_display)
5005 TapeDeactivateDisplayOn();
5010 static boolean RequestEnvelope(char *text, unsigned int req_state)
5012 int draw_buffer_last = GetDrawtoField();
5015 if (game_status == GAME_MODE_PLAYING)
5016 BlitScreenToBitmap(backbuffer);
5018 // disable deactivated drawing when quick-loading level tape recording
5019 if (tape.playing && tape.deactivate_display)
5020 TapeDeactivateDisplayOff(TRUE);
5022 SetMouseCursor(CURSOR_DEFAULT);
5024 // pause network game while waiting for request to answer
5025 if (network.enabled &&
5026 game_status == GAME_MODE_PLAYING &&
5027 !game.all_players_gone &&
5028 req_state & REQUEST_WAIT_FOR_INPUT)
5029 SendToServer_PausePlaying();
5031 // simulate releasing mouse button over last gadget, if still pressed
5033 HandleGadgets(-1, -1, 0);
5037 // (replace with setting corresponding request background)
5038 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5039 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5041 // clear door drawing field
5042 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5044 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5046 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5048 if (game_status == GAME_MODE_PLAYING)
5050 SetPanelBackground();
5051 SetDrawBackgroundMask(REDRAW_DOOR_1);
5055 SetDrawBackgroundMask(REDRAW_FIELD);
5061 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5063 // ---------- handle request buttons ----------
5064 result = RequestHandleEvents(req_state, draw_buffer_last);
5068 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5072 if (game_status == GAME_MODE_PLAYING)
5074 SetPanelBackground();
5075 SetDrawBackgroundMask(REDRAW_DOOR_1);
5079 SetDrawBackgroundMask(REDRAW_FIELD);
5082 // continue network game after request
5083 if (network.enabled &&
5084 game_status == GAME_MODE_PLAYING &&
5085 !game.all_players_gone &&
5086 req_state & REQUEST_WAIT_FOR_INPUT)
5087 SendToServer_ContinuePlaying();
5089 // restore deactivated drawing when quick-loading level tape recording
5090 if (tape.playing && tape.deactivate_display)
5091 TapeDeactivateDisplayOn();
5096 boolean Request(char *text, unsigned int req_state)
5098 boolean overlay_enabled = GetOverlayEnabled();
5101 game.request_active_or_moving = TRUE;
5103 SetOverlayEnabled(FALSE);
5105 if (global.use_envelope_request)
5106 result = RequestEnvelope(text, req_state);
5108 result = RequestDoor(text, req_state);
5110 SetOverlayEnabled(overlay_enabled);
5112 game.request_active_or_moving = FALSE;
5117 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5119 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5120 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5123 if (dpo1->sort_priority != dpo2->sort_priority)
5124 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5126 compare_result = dpo1->nr - dpo2->nr;
5128 return compare_result;
5131 void InitGraphicCompatibilityInfo_Doors(void)
5137 struct DoorInfo *door;
5141 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5142 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5144 { -1, -1, -1, NULL }
5146 struct Rect door_rect_list[] =
5148 { DX, DY, DXSIZE, DYSIZE },
5149 { VX, VY, VXSIZE, VYSIZE }
5153 for (i = 0; doors[i].door_token != -1; i++)
5155 int door_token = doors[i].door_token;
5156 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5157 int part_1 = doors[i].part_1;
5158 int part_8 = doors[i].part_8;
5159 int part_2 = part_1 + 1;
5160 int part_3 = part_1 + 2;
5161 struct DoorInfo *door = doors[i].door;
5162 struct Rect *door_rect = &door_rect_list[door_index];
5163 boolean door_gfx_redefined = FALSE;
5165 // check if any door part graphic definitions have been redefined
5167 for (j = 0; door_part_controls[j].door_token != -1; j++)
5169 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5170 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5172 if (dpc->door_token == door_token && fi->redefined)
5173 door_gfx_redefined = TRUE;
5176 // check for old-style door graphic/animation modifications
5178 if (!door_gfx_redefined)
5180 if (door->anim_mode & ANIM_STATIC_PANEL)
5182 door->panel.step_xoffset = 0;
5183 door->panel.step_yoffset = 0;
5186 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5188 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5189 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5190 int num_door_steps, num_panel_steps;
5192 // remove door part graphics other than the two default wings
5194 for (j = 0; door_part_controls[j].door_token != -1; j++)
5196 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5197 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5199 if (dpc->graphic >= part_3 &&
5200 dpc->graphic <= part_8)
5204 // set graphics and screen positions of the default wings
5206 g_part_1->width = door_rect->width;
5207 g_part_1->height = door_rect->height;
5208 g_part_2->width = door_rect->width;
5209 g_part_2->height = door_rect->height;
5210 g_part_2->src_x = door_rect->width;
5211 g_part_2->src_y = g_part_1->src_y;
5213 door->part_2.x = door->part_1.x;
5214 door->part_2.y = door->part_1.y;
5216 if (door->width != -1)
5218 g_part_1->width = door->width;
5219 g_part_2->width = door->width;
5221 // special treatment for graphics and screen position of right wing
5222 g_part_2->src_x += door_rect->width - door->width;
5223 door->part_2.x += door_rect->width - door->width;
5226 if (door->height != -1)
5228 g_part_1->height = door->height;
5229 g_part_2->height = door->height;
5231 // special treatment for graphics and screen position of bottom wing
5232 g_part_2->src_y += door_rect->height - door->height;
5233 door->part_2.y += door_rect->height - door->height;
5236 // set animation delays for the default wings and panels
5238 door->part_1.step_delay = door->step_delay;
5239 door->part_2.step_delay = door->step_delay;
5240 door->panel.step_delay = door->step_delay;
5242 // set animation draw order for the default wings
5244 door->part_1.sort_priority = 2; // draw left wing over ...
5245 door->part_2.sort_priority = 1; // ... right wing
5247 // set animation draw offset for the default wings
5249 if (door->anim_mode & ANIM_HORIZONTAL)
5251 door->part_1.step_xoffset = door->step_offset;
5252 door->part_1.step_yoffset = 0;
5253 door->part_2.step_xoffset = door->step_offset * -1;
5254 door->part_2.step_yoffset = 0;
5256 num_door_steps = g_part_1->width / door->step_offset;
5258 else // ANIM_VERTICAL
5260 door->part_1.step_xoffset = 0;
5261 door->part_1.step_yoffset = door->step_offset;
5262 door->part_2.step_xoffset = 0;
5263 door->part_2.step_yoffset = door->step_offset * -1;
5265 num_door_steps = g_part_1->height / door->step_offset;
5268 // set animation draw offset for the default panels
5270 if (door->step_offset > 1)
5272 num_panel_steps = 2 * door_rect->height / door->step_offset;
5273 door->panel.start_step = num_panel_steps - num_door_steps;
5274 door->panel.start_step_closing = door->panel.start_step;
5278 num_panel_steps = door_rect->height / door->step_offset;
5279 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5280 door->panel.start_step_closing = door->panel.start_step;
5281 door->panel.step_delay *= 2;
5288 void InitDoors(void)
5292 for (i = 0; door_part_controls[i].door_token != -1; i++)
5294 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5295 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5297 // initialize "start_step_opening" and "start_step_closing", if needed
5298 if (dpc->pos->start_step_opening == 0 &&
5299 dpc->pos->start_step_closing == 0)
5301 // dpc->pos->start_step_opening = dpc->pos->start_step;
5302 dpc->pos->start_step_closing = dpc->pos->start_step;
5305 // fill structure for door part draw order (sorted below)
5307 dpo->sort_priority = dpc->pos->sort_priority;
5310 // sort door part controls according to sort_priority and graphic number
5311 qsort(door_part_order, MAX_DOOR_PARTS,
5312 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5315 unsigned int OpenDoor(unsigned int door_state)
5317 if (door_state & DOOR_COPY_BACK)
5319 if (door_state & DOOR_OPEN_1)
5320 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5321 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5323 if (door_state & DOOR_OPEN_2)
5324 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5325 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5327 door_state &= ~DOOR_COPY_BACK;
5330 return MoveDoor(door_state);
5333 unsigned int CloseDoor(unsigned int door_state)
5335 unsigned int old_door_state = GetDoorState();
5337 if (!(door_state & DOOR_NO_COPY_BACK))
5339 if (old_door_state & DOOR_OPEN_1)
5340 BlitBitmap(backbuffer, bitmap_db_door_1,
5341 DX, DY, DXSIZE, DYSIZE, 0, 0);
5343 if (old_door_state & DOOR_OPEN_2)
5344 BlitBitmap(backbuffer, bitmap_db_door_2,
5345 VX, VY, VXSIZE, VYSIZE, 0, 0);
5347 door_state &= ~DOOR_NO_COPY_BACK;
5350 return MoveDoor(door_state);
5353 unsigned int GetDoorState(void)
5355 return MoveDoor(DOOR_GET_STATE);
5358 unsigned int SetDoorState(unsigned int door_state)
5360 return MoveDoor(door_state | DOOR_SET_STATE);
5363 static int euclid(int a, int b)
5365 return (b ? euclid(b, a % b) : a);
5368 unsigned int MoveDoor(unsigned int door_state)
5370 struct Rect door_rect_list[] =
5372 { DX, DY, DXSIZE, DYSIZE },
5373 { VX, VY, VXSIZE, VYSIZE }
5375 static int door1 = DOOR_CLOSE_1;
5376 static int door2 = DOOR_CLOSE_2;
5377 DelayCounter door_delay = { 0 };
5380 if (door_state == DOOR_GET_STATE)
5381 return (door1 | door2);
5383 if (door_state & DOOR_SET_STATE)
5385 if (door_state & DOOR_ACTION_1)
5386 door1 = door_state & DOOR_ACTION_1;
5387 if (door_state & DOOR_ACTION_2)
5388 door2 = door_state & DOOR_ACTION_2;
5390 return (door1 | door2);
5393 if (!(door_state & DOOR_FORCE_REDRAW))
5395 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5396 door_state &= ~DOOR_OPEN_1;
5397 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5398 door_state &= ~DOOR_CLOSE_1;
5399 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5400 door_state &= ~DOOR_OPEN_2;
5401 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5402 door_state &= ~DOOR_CLOSE_2;
5405 if (global.autoplay_leveldir)
5407 door_state |= DOOR_NO_DELAY;
5408 door_state &= ~DOOR_CLOSE_ALL;
5411 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5412 door_state |= DOOR_NO_DELAY;
5414 if (door_state & DOOR_ACTION)
5416 boolean door_panel_drawn[NUM_DOORS];
5417 boolean panel_has_doors[NUM_DOORS];
5418 boolean door_part_skip[MAX_DOOR_PARTS];
5419 boolean door_part_done[MAX_DOOR_PARTS];
5420 boolean door_part_done_all;
5421 int num_steps[MAX_DOOR_PARTS];
5422 int max_move_delay = 0; // delay for complete animations of all doors
5423 int max_step_delay = 0; // delay (ms) between two animation frames
5424 int num_move_steps = 0; // number of animation steps for all doors
5425 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5426 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5430 for (i = 0; i < NUM_DOORS; i++)
5431 panel_has_doors[i] = FALSE;
5433 for (i = 0; i < MAX_DOOR_PARTS; i++)
5435 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5436 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5437 int door_token = dpc->door_token;
5439 door_part_done[i] = FALSE;
5440 door_part_skip[i] = (!(door_state & door_token) ||
5444 for (i = 0; i < MAX_DOOR_PARTS; i++)
5446 int nr = door_part_order[i].nr;
5447 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5448 struct DoorPartPosInfo *pos = dpc->pos;
5449 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5450 int door_token = dpc->door_token;
5451 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5452 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5453 int step_xoffset = ABS(pos->step_xoffset);
5454 int step_yoffset = ABS(pos->step_yoffset);
5455 int step_delay = pos->step_delay;
5456 int current_door_state = door_state & door_token;
5457 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5458 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5459 boolean part_opening = (is_panel ? door_closing : door_opening);
5460 int start_step = (part_opening ? pos->start_step_opening :
5461 pos->start_step_closing);
5462 float move_xsize = (step_xoffset ? g->width : 0);
5463 float move_ysize = (step_yoffset ? g->height : 0);
5464 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5465 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5466 int move_steps = (move_xsteps && move_ysteps ?
5467 MIN(move_xsteps, move_ysteps) :
5468 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5469 int move_delay = move_steps * step_delay;
5471 if (door_part_skip[nr])
5474 max_move_delay = MAX(max_move_delay, move_delay);
5475 max_step_delay = (max_step_delay == 0 ? step_delay :
5476 euclid(max_step_delay, step_delay));
5477 num_steps[nr] = move_steps;
5481 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5483 panel_has_doors[door_index] = TRUE;
5487 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5489 num_move_steps = max_move_delay / max_step_delay;
5490 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5492 door_delay.value = max_step_delay;
5494 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5496 start = num_move_steps - 1;
5500 // opening door sound has priority over simultaneously closing door
5501 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5503 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5505 if (door_state & DOOR_OPEN_1)
5506 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5507 if (door_state & DOOR_OPEN_2)
5508 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5510 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5512 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5514 if (door_state & DOOR_CLOSE_1)
5515 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5516 if (door_state & DOOR_CLOSE_2)
5517 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5521 for (k = start; k < num_move_steps; k++)
5523 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5525 door_part_done_all = TRUE;
5527 for (i = 0; i < NUM_DOORS; i++)
5528 door_panel_drawn[i] = FALSE;
5530 for (i = 0; i < MAX_DOOR_PARTS; i++)
5532 int nr = door_part_order[i].nr;
5533 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5534 struct DoorPartPosInfo *pos = dpc->pos;
5535 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5536 int door_token = dpc->door_token;
5537 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5538 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5539 boolean is_panel_and_door_has_closed = FALSE;
5540 struct Rect *door_rect = &door_rect_list[door_index];
5541 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5543 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5544 int current_door_state = door_state & door_token;
5545 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5546 boolean door_closing = !door_opening;
5547 boolean part_opening = (is_panel ? door_closing : door_opening);
5548 boolean part_closing = !part_opening;
5549 int start_step = (part_opening ? pos->start_step_opening :
5550 pos->start_step_closing);
5551 int step_delay = pos->step_delay;
5552 int step_factor = step_delay / max_step_delay;
5553 int k1 = (step_factor ? k / step_factor + 1 : k);
5554 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5555 int kk = MAX(0, k2);
5558 int src_x, src_y, src_xx, src_yy;
5559 int dst_x, dst_y, dst_xx, dst_yy;
5562 if (door_part_skip[nr])
5565 if (!(door_state & door_token))
5573 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5574 int kk_door = MAX(0, k2_door);
5575 int sync_frame = kk_door * door_delay.value;
5576 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5578 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5579 &g_src_x, &g_src_y);
5584 if (!door_panel_drawn[door_index])
5586 ClearRectangle(drawto, door_rect->x, door_rect->y,
5587 door_rect->width, door_rect->height);
5589 door_panel_drawn[door_index] = TRUE;
5592 // draw opening or closing door parts
5594 if (pos->step_xoffset < 0) // door part on right side
5597 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5600 if (dst_xx + width > door_rect->width)
5601 width = door_rect->width - dst_xx;
5603 else // door part on left side
5606 dst_xx = pos->x - kk * pos->step_xoffset;
5610 src_xx = ABS(dst_xx);
5614 width = g->width - src_xx;
5616 if (width > door_rect->width)
5617 width = door_rect->width;
5619 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5622 if (pos->step_yoffset < 0) // door part on bottom side
5625 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5628 if (dst_yy + height > door_rect->height)
5629 height = door_rect->height - dst_yy;
5631 else // door part on top side
5634 dst_yy = pos->y - kk * pos->step_yoffset;
5638 src_yy = ABS(dst_yy);
5642 height = g->height - src_yy;
5645 src_x = g_src_x + src_xx;
5646 src_y = g_src_y + src_yy;
5648 dst_x = door_rect->x + dst_xx;
5649 dst_y = door_rect->y + dst_yy;
5651 is_panel_and_door_has_closed =
5654 panel_has_doors[door_index] &&
5655 k >= num_move_steps_doors_only - 1);
5657 if (width >= 0 && width <= g->width &&
5658 height >= 0 && height <= g->height &&
5659 !is_panel_and_door_has_closed)
5661 if (is_panel || !pos->draw_masked)
5662 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5665 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5669 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5671 if ((part_opening && (width < 0 || height < 0)) ||
5672 (part_closing && (width >= g->width && height >= g->height)))
5673 door_part_done[nr] = TRUE;
5675 // continue door part animations, but not panel after door has closed
5676 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5677 door_part_done_all = FALSE;
5680 if (!(door_state & DOOR_NO_DELAY))
5684 SkipUntilDelayReached(&door_delay, &k, last_frame);
5686 // prevent OS (Windows) from complaining about program not responding
5690 if (door_part_done_all)
5694 if (!(door_state & DOOR_NO_DELAY))
5696 // wait for specified door action post delay
5697 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5698 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5699 else if (door_state & DOOR_ACTION_1)
5700 door_delay.value = door_1.post_delay;
5701 else if (door_state & DOOR_ACTION_2)
5702 door_delay.value = door_2.post_delay;
5704 while (!DelayReached(&door_delay))
5709 if (door_state & DOOR_ACTION_1)
5710 door1 = door_state & DOOR_ACTION_1;
5711 if (door_state & DOOR_ACTION_2)
5712 door2 = door_state & DOOR_ACTION_2;
5714 // draw masked border over door area
5715 DrawMaskedBorder(REDRAW_DOOR_1);
5716 DrawMaskedBorder(REDRAW_DOOR_2);
5718 ClearAutoRepeatKeyEvents();
5720 return (door1 | door2);
5723 static boolean useSpecialEditorDoor(void)
5725 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5726 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5728 // do not draw special editor door if editor border defined or redefined
5729 if (graphic_info[graphic].bitmap != NULL || redefined)
5732 // do not draw special editor door if global border defined to be empty
5733 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5736 // do not draw special editor door if viewport definitions do not match
5740 EY + EYSIZE != VY + VYSIZE)
5746 void DrawSpecialEditorDoor(void)
5748 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5749 int top_border_width = gfx1->width;
5750 int top_border_height = gfx1->height;
5751 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5752 int ex = EX - outer_border;
5753 int ey = EY - outer_border;
5754 int vy = VY - outer_border;
5755 int exsize = EXSIZE + 2 * outer_border;
5757 if (!useSpecialEditorDoor())
5760 // draw bigger level editor toolbox window
5761 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5762 top_border_width, top_border_height, ex, ey - top_border_height);
5763 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5764 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5766 redraw_mask |= REDRAW_ALL;
5769 void UndrawSpecialEditorDoor(void)
5771 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5772 int top_border_width = gfx1->width;
5773 int top_border_height = gfx1->height;
5774 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5775 int ex = EX - outer_border;
5776 int ey = EY - outer_border;
5777 int ey_top = ey - top_border_height;
5778 int exsize = EXSIZE + 2 * outer_border;
5779 int eysize = EYSIZE + 2 * outer_border;
5781 if (!useSpecialEditorDoor())
5784 // draw normal tape recorder window
5785 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5787 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5788 ex, ey_top, top_border_width, top_border_height,
5790 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5791 ex, ey, exsize, eysize, ex, ey);
5795 // if screen background is set to "[NONE]", clear editor toolbox window
5796 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5797 ClearRectangle(drawto, ex, ey, exsize, eysize);
5800 redraw_mask |= REDRAW_ALL;
5804 // ---------- new tool button stuff -------------------------------------------
5809 struct TextPosInfo *pos;
5811 boolean is_touch_button;
5813 } toolbutton_info[NUM_TOOL_BUTTONS] =
5816 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5817 TOOL_CTRL_ID_YES, FALSE, "yes"
5820 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5821 TOOL_CTRL_ID_NO, FALSE, "no"
5824 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5825 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5828 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5829 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5832 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5833 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5836 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5837 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5840 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5841 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5844 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5845 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5848 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5849 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5852 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5853 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5857 void CreateToolButtons(void)
5861 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5863 int graphic = toolbutton_info[i].graphic;
5864 struct GraphicInfo *gfx = &graphic_info[graphic];
5865 struct TextPosInfo *pos = toolbutton_info[i].pos;
5866 struct GadgetInfo *gi;
5867 Bitmap *deco_bitmap = None;
5868 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5869 unsigned int event_mask = GD_EVENT_RELEASED;
5870 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5871 int base_x = (is_touch_button ? 0 : DX);
5872 int base_y = (is_touch_button ? 0 : DY);
5873 int gd_x = gfx->src_x;
5874 int gd_y = gfx->src_y;
5875 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5876 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5881 // do not use touch buttons if overlay touch buttons are disabled
5882 if (is_touch_button && !setup.touch.overlay_buttons)
5885 if (global.use_envelope_request && !is_touch_button)
5887 setRequestPosition(&base_x, &base_y, TRUE);
5889 // check if request buttons are outside of envelope and fix, if needed
5890 if (x < 0 || x + gfx->width > request.width ||
5891 y < 0 || y + gfx->height > request.height)
5893 if (id == TOOL_CTRL_ID_YES)
5896 y = request.height - 2 * request.border_size - gfx->height;
5898 else if (id == TOOL_CTRL_ID_NO)
5900 x = request.width - 2 * request.border_size - gfx->width;
5901 y = request.height - 2 * request.border_size - gfx->height;
5903 else if (id == TOOL_CTRL_ID_CONFIRM)
5905 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5906 y = request.height - 2 * request.border_size - gfx->height;
5908 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5910 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5912 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5913 y = request.height - 2 * request.border_size - gfx->height * 2;
5915 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5916 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5921 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5924 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5926 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5927 pos->size, &deco_bitmap, &deco_x, &deco_y);
5928 deco_xpos = (gfx->width - pos->size) / 2;
5929 deco_ypos = (gfx->height - pos->size) / 2;
5932 gi = CreateGadget(GDI_CUSTOM_ID, id,
5933 GDI_IMAGE_ID, graphic,
5934 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5937 GDI_WIDTH, gfx->width,
5938 GDI_HEIGHT, gfx->height,
5939 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5940 GDI_STATE, GD_BUTTON_UNPRESSED,
5941 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5942 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5943 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5944 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5945 GDI_DECORATION_SIZE, pos->size, pos->size,
5946 GDI_DECORATION_SHIFTING, 1, 1,
5947 GDI_DIRECT_DRAW, FALSE,
5948 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5949 GDI_EVENT_MASK, event_mask,
5950 GDI_CALLBACK_ACTION, HandleToolButtons,
5954 Fail("cannot create gadget");
5956 tool_gadget[id] = gi;
5960 void FreeToolButtons(void)
5964 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5965 FreeGadget(tool_gadget[i]);
5968 static void UnmapToolButtons(void)
5972 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5973 UnmapGadget(tool_gadget[i]);
5976 static void HandleToolButtons(struct GadgetInfo *gi)
5978 request_gadget_id = gi->custom_id;
5981 static struct Mapping_EM_to_RND_object
5984 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5985 boolean is_backside; // backside of moving element
5991 em_object_mapping_list[GAME_TILE_MAX + 1] =
5994 Zborder, FALSE, FALSE,
5998 Zplayer, FALSE, FALSE,
6007 Ztank, FALSE, FALSE,
6011 Zeater, FALSE, FALSE,
6015 Zdynamite, FALSE, FALSE,
6019 Zboom, FALSE, FALSE,
6024 Xchain, FALSE, FALSE,
6025 EL_DEFAULT, ACTION_EXPLODING, -1
6028 Xboom_bug, FALSE, FALSE,
6029 EL_BUG, ACTION_EXPLODING, -1
6032 Xboom_tank, FALSE, FALSE,
6033 EL_SPACESHIP, ACTION_EXPLODING, -1
6036 Xboom_android, FALSE, FALSE,
6037 EL_EMC_ANDROID, ACTION_OTHER, -1
6040 Xboom_1, FALSE, FALSE,
6041 EL_DEFAULT, ACTION_EXPLODING, -1
6044 Xboom_2, FALSE, FALSE,
6045 EL_DEFAULT, ACTION_EXPLODING, -1
6049 Xblank, TRUE, FALSE,
6054 Xsplash_e, FALSE, FALSE,
6055 EL_ACID_SPLASH_RIGHT, -1, -1
6058 Xsplash_w, FALSE, FALSE,
6059 EL_ACID_SPLASH_LEFT, -1, -1
6063 Xplant, TRUE, FALSE,
6064 EL_EMC_PLANT, -1, -1
6067 Yplant, FALSE, FALSE,
6068 EL_EMC_PLANT, -1, -1
6072 Xacid_1, TRUE, FALSE,
6076 Xacid_2, FALSE, FALSE,
6080 Xacid_3, FALSE, FALSE,
6084 Xacid_4, FALSE, FALSE,
6088 Xacid_5, FALSE, FALSE,
6092 Xacid_6, FALSE, FALSE,
6096 Xacid_7, FALSE, FALSE,
6100 Xacid_8, FALSE, FALSE,
6105 Xfake_acid_1, TRUE, FALSE,
6106 EL_EMC_FAKE_ACID, -1, -1
6109 Xfake_acid_2, FALSE, FALSE,
6110 EL_EMC_FAKE_ACID, -1, -1
6113 Xfake_acid_3, FALSE, FALSE,
6114 EL_EMC_FAKE_ACID, -1, -1
6117 Xfake_acid_4, FALSE, FALSE,
6118 EL_EMC_FAKE_ACID, -1, -1
6121 Xfake_acid_5, FALSE, FALSE,
6122 EL_EMC_FAKE_ACID, -1, -1
6125 Xfake_acid_6, FALSE, FALSE,
6126 EL_EMC_FAKE_ACID, -1, -1
6129 Xfake_acid_7, FALSE, FALSE,
6130 EL_EMC_FAKE_ACID, -1, -1
6133 Xfake_acid_8, FALSE, FALSE,
6134 EL_EMC_FAKE_ACID, -1, -1
6138 Xfake_acid_1_player, FALSE, FALSE,
6139 EL_EMC_FAKE_ACID, -1, -1
6142 Xfake_acid_2_player, FALSE, FALSE,
6143 EL_EMC_FAKE_ACID, -1, -1
6146 Xfake_acid_3_player, FALSE, FALSE,
6147 EL_EMC_FAKE_ACID, -1, -1
6150 Xfake_acid_4_player, FALSE, FALSE,
6151 EL_EMC_FAKE_ACID, -1, -1
6154 Xfake_acid_5_player, FALSE, FALSE,
6155 EL_EMC_FAKE_ACID, -1, -1
6158 Xfake_acid_6_player, FALSE, FALSE,
6159 EL_EMC_FAKE_ACID, -1, -1
6162 Xfake_acid_7_player, FALSE, FALSE,
6163 EL_EMC_FAKE_ACID, -1, -1
6166 Xfake_acid_8_player, FALSE, FALSE,
6167 EL_EMC_FAKE_ACID, -1, -1
6171 Xgrass, TRUE, FALSE,
6172 EL_EMC_GRASS, -1, -1
6175 Ygrass_nB, FALSE, FALSE,
6176 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6179 Ygrass_eB, FALSE, FALSE,
6180 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6183 Ygrass_sB, FALSE, FALSE,
6184 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6187 Ygrass_wB, FALSE, FALSE,
6188 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6196 Ydirt_nB, FALSE, FALSE,
6197 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6200 Ydirt_eB, FALSE, FALSE,
6201 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6204 Ydirt_sB, FALSE, FALSE,
6205 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6208 Ydirt_wB, FALSE, FALSE,
6209 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6213 Xandroid, TRUE, FALSE,
6214 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6217 Xandroid_1_n, FALSE, FALSE,
6218 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6221 Xandroid_2_n, FALSE, FALSE,
6222 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6225 Xandroid_1_e, FALSE, FALSE,
6226 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6229 Xandroid_2_e, FALSE, FALSE,
6230 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6233 Xandroid_1_w, FALSE, FALSE,
6234 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6237 Xandroid_2_w, FALSE, FALSE,
6238 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6241 Xandroid_1_s, FALSE, FALSE,
6242 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6245 Xandroid_2_s, FALSE, FALSE,
6246 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6249 Yandroid_n, FALSE, FALSE,
6250 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6253 Yandroid_nB, FALSE, TRUE,
6254 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6257 Yandroid_ne, FALSE, FALSE,
6258 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6261 Yandroid_neB, FALSE, TRUE,
6262 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6265 Yandroid_e, FALSE, FALSE,
6266 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6269 Yandroid_eB, FALSE, TRUE,
6270 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6273 Yandroid_se, FALSE, FALSE,
6274 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6277 Yandroid_seB, FALSE, TRUE,
6278 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6281 Yandroid_s, FALSE, FALSE,
6282 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6285 Yandroid_sB, FALSE, TRUE,
6286 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6289 Yandroid_sw, FALSE, FALSE,
6290 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6293 Yandroid_swB, FALSE, TRUE,
6294 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6297 Yandroid_w, FALSE, FALSE,
6298 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6301 Yandroid_wB, FALSE, TRUE,
6302 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6305 Yandroid_nw, FALSE, FALSE,
6306 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6309 Yandroid_nwB, FALSE, TRUE,
6310 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6314 Xeater_n, TRUE, FALSE,
6315 EL_YAMYAM_UP, -1, -1
6318 Xeater_e, TRUE, FALSE,
6319 EL_YAMYAM_RIGHT, -1, -1
6322 Xeater_w, TRUE, FALSE,
6323 EL_YAMYAM_LEFT, -1, -1
6326 Xeater_s, TRUE, FALSE,
6327 EL_YAMYAM_DOWN, -1, -1
6330 Yeater_n, FALSE, FALSE,
6331 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6334 Yeater_nB, FALSE, TRUE,
6335 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6338 Yeater_e, FALSE, FALSE,
6339 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6342 Yeater_eB, FALSE, TRUE,
6343 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6346 Yeater_s, FALSE, FALSE,
6347 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6350 Yeater_sB, FALSE, TRUE,
6351 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6354 Yeater_w, FALSE, FALSE,
6355 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6358 Yeater_wB, FALSE, TRUE,
6359 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6362 Yeater_stone, FALSE, FALSE,
6363 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6366 Yeater_spring, FALSE, FALSE,
6367 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6371 Xalien, TRUE, FALSE,
6375 Xalien_pause, FALSE, FALSE,
6379 Yalien_n, FALSE, FALSE,
6380 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6383 Yalien_nB, FALSE, TRUE,
6384 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6387 Yalien_e, FALSE, FALSE,
6388 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6391 Yalien_eB, FALSE, TRUE,
6392 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6395 Yalien_s, FALSE, FALSE,
6396 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6399 Yalien_sB, FALSE, TRUE,
6400 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6403 Yalien_w, FALSE, FALSE,
6404 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6407 Yalien_wB, FALSE, TRUE,
6408 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6411 Yalien_stone, FALSE, FALSE,
6412 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6415 Yalien_spring, FALSE, FALSE,
6416 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6420 Xbug_1_n, TRUE, FALSE,
6424 Xbug_1_e, TRUE, FALSE,
6425 EL_BUG_RIGHT, -1, -1
6428 Xbug_1_s, TRUE, FALSE,
6432 Xbug_1_w, TRUE, FALSE,
6436 Xbug_2_n, FALSE, FALSE,
6440 Xbug_2_e, FALSE, FALSE,
6441 EL_BUG_RIGHT, -1, -1
6444 Xbug_2_s, FALSE, FALSE,
6448 Xbug_2_w, FALSE, FALSE,
6452 Ybug_n, FALSE, FALSE,
6453 EL_BUG, ACTION_MOVING, MV_BIT_UP
6456 Ybug_nB, FALSE, TRUE,
6457 EL_BUG, ACTION_MOVING, MV_BIT_UP
6460 Ybug_e, FALSE, FALSE,
6461 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6464 Ybug_eB, FALSE, TRUE,
6465 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6468 Ybug_s, FALSE, FALSE,
6469 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6472 Ybug_sB, FALSE, TRUE,
6473 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6476 Ybug_w, FALSE, FALSE,
6477 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6480 Ybug_wB, FALSE, TRUE,
6481 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6484 Ybug_w_n, FALSE, FALSE,
6485 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6488 Ybug_n_e, FALSE, FALSE,
6489 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6492 Ybug_e_s, FALSE, FALSE,
6493 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6496 Ybug_s_w, FALSE, FALSE,
6497 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6500 Ybug_e_n, FALSE, FALSE,
6501 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6504 Ybug_s_e, FALSE, FALSE,
6505 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6508 Ybug_w_s, FALSE, FALSE,
6509 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6512 Ybug_n_w, FALSE, FALSE,
6513 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6516 Ybug_stone, FALSE, FALSE,
6517 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6520 Ybug_spring, FALSE, FALSE,
6521 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6525 Xtank_1_n, TRUE, FALSE,
6526 EL_SPACESHIP_UP, -1, -1
6529 Xtank_1_e, TRUE, FALSE,
6530 EL_SPACESHIP_RIGHT, -1, -1
6533 Xtank_1_s, TRUE, FALSE,
6534 EL_SPACESHIP_DOWN, -1, -1
6537 Xtank_1_w, TRUE, FALSE,
6538 EL_SPACESHIP_LEFT, -1, -1
6541 Xtank_2_n, FALSE, FALSE,
6542 EL_SPACESHIP_UP, -1, -1
6545 Xtank_2_e, FALSE, FALSE,
6546 EL_SPACESHIP_RIGHT, -1, -1
6549 Xtank_2_s, FALSE, FALSE,
6550 EL_SPACESHIP_DOWN, -1, -1
6553 Xtank_2_w, FALSE, FALSE,
6554 EL_SPACESHIP_LEFT, -1, -1
6557 Ytank_n, FALSE, FALSE,
6558 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6561 Ytank_nB, FALSE, TRUE,
6562 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6565 Ytank_e, FALSE, FALSE,
6566 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6569 Ytank_eB, FALSE, TRUE,
6570 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6573 Ytank_s, FALSE, FALSE,
6574 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6577 Ytank_sB, FALSE, TRUE,
6578 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6581 Ytank_w, FALSE, FALSE,
6582 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6585 Ytank_wB, FALSE, TRUE,
6586 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6589 Ytank_w_n, FALSE, FALSE,
6590 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6593 Ytank_n_e, FALSE, FALSE,
6594 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6597 Ytank_e_s, FALSE, FALSE,
6598 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6601 Ytank_s_w, FALSE, FALSE,
6602 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6605 Ytank_e_n, FALSE, FALSE,
6606 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6609 Ytank_s_e, FALSE, FALSE,
6610 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6613 Ytank_w_s, FALSE, FALSE,
6614 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6617 Ytank_n_w, FALSE, FALSE,
6618 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6621 Ytank_stone, FALSE, FALSE,
6622 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6625 Ytank_spring, FALSE, FALSE,
6626 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6630 Xemerald, TRUE, FALSE,
6634 Xemerald_pause, FALSE, FALSE,
6638 Xemerald_fall, FALSE, FALSE,
6642 Xemerald_shine, FALSE, FALSE,
6643 EL_EMERALD, ACTION_TWINKLING, -1
6646 Yemerald_s, FALSE, FALSE,
6647 EL_EMERALD, ACTION_FALLING, -1
6650 Yemerald_sB, FALSE, TRUE,
6651 EL_EMERALD, ACTION_FALLING, -1
6654 Yemerald_e, FALSE, FALSE,
6655 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6658 Yemerald_eB, FALSE, TRUE,
6659 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6662 Yemerald_w, FALSE, FALSE,
6663 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6666 Yemerald_wB, FALSE, TRUE,
6667 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6670 Yemerald_blank, FALSE, FALSE,
6671 EL_EMERALD, ACTION_COLLECTING, -1
6675 Xdiamond, TRUE, FALSE,
6679 Xdiamond_pause, FALSE, FALSE,
6683 Xdiamond_fall, FALSE, FALSE,
6687 Xdiamond_shine, FALSE, FALSE,
6688 EL_DIAMOND, ACTION_TWINKLING, -1
6691 Ydiamond_s, FALSE, FALSE,
6692 EL_DIAMOND, ACTION_FALLING, -1
6695 Ydiamond_sB, FALSE, TRUE,
6696 EL_DIAMOND, ACTION_FALLING, -1
6699 Ydiamond_e, FALSE, FALSE,
6700 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6703 Ydiamond_eB, FALSE, TRUE,
6704 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6707 Ydiamond_w, FALSE, FALSE,
6708 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6711 Ydiamond_wB, FALSE, TRUE,
6712 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6715 Ydiamond_blank, FALSE, FALSE,
6716 EL_DIAMOND, ACTION_COLLECTING, -1
6719 Ydiamond_stone, FALSE, FALSE,
6720 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6724 Xstone, TRUE, FALSE,
6728 Xstone_pause, FALSE, FALSE,
6732 Xstone_fall, FALSE, FALSE,
6736 Ystone_s, FALSE, FALSE,
6737 EL_ROCK, ACTION_FALLING, -1
6740 Ystone_sB, FALSE, TRUE,
6741 EL_ROCK, ACTION_FALLING, -1
6744 Ystone_e, FALSE, FALSE,
6745 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6748 Ystone_eB, FALSE, TRUE,
6749 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6752 Ystone_w, FALSE, FALSE,
6753 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6756 Ystone_wB, FALSE, TRUE,
6757 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6765 Xbomb_pause, FALSE, FALSE,
6769 Xbomb_fall, FALSE, FALSE,
6773 Ybomb_s, FALSE, FALSE,
6774 EL_BOMB, ACTION_FALLING, -1
6777 Ybomb_sB, FALSE, TRUE,
6778 EL_BOMB, ACTION_FALLING, -1
6781 Ybomb_e, FALSE, FALSE,
6782 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6785 Ybomb_eB, FALSE, TRUE,
6786 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6789 Ybomb_w, FALSE, FALSE,
6790 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6793 Ybomb_wB, FALSE, TRUE,
6794 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6797 Ybomb_blank, FALSE, FALSE,
6798 EL_BOMB, ACTION_ACTIVATING, -1
6806 Xnut_pause, FALSE, FALSE,
6810 Xnut_fall, FALSE, FALSE,
6814 Ynut_s, FALSE, FALSE,
6815 EL_NUT, ACTION_FALLING, -1
6818 Ynut_sB, FALSE, TRUE,
6819 EL_NUT, ACTION_FALLING, -1
6822 Ynut_e, FALSE, FALSE,
6823 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6826 Ynut_eB, FALSE, TRUE,
6827 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6830 Ynut_w, FALSE, FALSE,
6831 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6834 Ynut_wB, FALSE, TRUE,
6835 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6838 Ynut_stone, FALSE, FALSE,
6839 EL_NUT, ACTION_BREAKING, -1
6843 Xspring, TRUE, FALSE,
6847 Xspring_pause, FALSE, FALSE,
6851 Xspring_e, TRUE, FALSE,
6852 EL_SPRING_RIGHT, -1, -1
6855 Xspring_w, TRUE, FALSE,
6856 EL_SPRING_LEFT, -1, -1
6859 Xspring_fall, FALSE, FALSE,
6863 Yspring_s, FALSE, FALSE,
6864 EL_SPRING, ACTION_FALLING, -1
6867 Yspring_sB, FALSE, TRUE,
6868 EL_SPRING, ACTION_FALLING, -1
6871 Yspring_e, FALSE, FALSE,
6872 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6875 Yspring_eB, FALSE, TRUE,
6876 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6879 Yspring_w, FALSE, FALSE,
6880 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6883 Yspring_wB, FALSE, TRUE,
6884 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6887 Yspring_alien_e, FALSE, FALSE,
6888 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6891 Yspring_alien_eB, FALSE, TRUE,
6892 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6895 Yspring_alien_w, FALSE, FALSE,
6896 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6899 Yspring_alien_wB, FALSE, TRUE,
6900 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6904 Xpush_emerald_e, FALSE, FALSE,
6905 EL_EMERALD, -1, MV_BIT_RIGHT
6908 Xpush_emerald_w, FALSE, FALSE,
6909 EL_EMERALD, -1, MV_BIT_LEFT
6912 Xpush_diamond_e, FALSE, FALSE,
6913 EL_DIAMOND, -1, MV_BIT_RIGHT
6916 Xpush_diamond_w, FALSE, FALSE,
6917 EL_DIAMOND, -1, MV_BIT_LEFT
6920 Xpush_stone_e, FALSE, FALSE,
6921 EL_ROCK, -1, MV_BIT_RIGHT
6924 Xpush_stone_w, FALSE, FALSE,
6925 EL_ROCK, -1, MV_BIT_LEFT
6928 Xpush_bomb_e, FALSE, FALSE,
6929 EL_BOMB, -1, MV_BIT_RIGHT
6932 Xpush_bomb_w, FALSE, FALSE,
6933 EL_BOMB, -1, MV_BIT_LEFT
6936 Xpush_nut_e, FALSE, FALSE,
6937 EL_NUT, -1, MV_BIT_RIGHT
6940 Xpush_nut_w, FALSE, FALSE,
6941 EL_NUT, -1, MV_BIT_LEFT
6944 Xpush_spring_e, FALSE, FALSE,
6945 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6948 Xpush_spring_w, FALSE, FALSE,
6949 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6953 Xdynamite, TRUE, FALSE,
6954 EL_EM_DYNAMITE, -1, -1
6957 Ydynamite_blank, FALSE, FALSE,
6958 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6961 Xdynamite_1, TRUE, FALSE,
6962 EL_EM_DYNAMITE_ACTIVE, -1, -1
6965 Xdynamite_2, FALSE, FALSE,
6966 EL_EM_DYNAMITE_ACTIVE, -1, -1
6969 Xdynamite_3, FALSE, FALSE,
6970 EL_EM_DYNAMITE_ACTIVE, -1, -1
6973 Xdynamite_4, FALSE, FALSE,
6974 EL_EM_DYNAMITE_ACTIVE, -1, -1
6978 Xkey_1, TRUE, FALSE,
6982 Xkey_2, TRUE, FALSE,
6986 Xkey_3, TRUE, FALSE,
6990 Xkey_4, TRUE, FALSE,
6994 Xkey_5, TRUE, FALSE,
6995 EL_EMC_KEY_5, -1, -1
6998 Xkey_6, TRUE, FALSE,
6999 EL_EMC_KEY_6, -1, -1
7002 Xkey_7, TRUE, FALSE,
7003 EL_EMC_KEY_7, -1, -1
7006 Xkey_8, TRUE, FALSE,
7007 EL_EMC_KEY_8, -1, -1
7011 Xdoor_1, TRUE, FALSE,
7012 EL_EM_GATE_1, -1, -1
7015 Xdoor_2, TRUE, FALSE,
7016 EL_EM_GATE_2, -1, -1
7019 Xdoor_3, TRUE, FALSE,
7020 EL_EM_GATE_3, -1, -1
7023 Xdoor_4, TRUE, FALSE,
7024 EL_EM_GATE_4, -1, -1
7027 Xdoor_5, TRUE, FALSE,
7028 EL_EMC_GATE_5, -1, -1
7031 Xdoor_6, TRUE, FALSE,
7032 EL_EMC_GATE_6, -1, -1
7035 Xdoor_7, TRUE, FALSE,
7036 EL_EMC_GATE_7, -1, -1
7039 Xdoor_8, TRUE, FALSE,
7040 EL_EMC_GATE_8, -1, -1
7044 Xfake_door_1, TRUE, FALSE,
7045 EL_EM_GATE_1_GRAY, -1, -1
7048 Xfake_door_2, TRUE, FALSE,
7049 EL_EM_GATE_2_GRAY, -1, -1
7052 Xfake_door_3, TRUE, FALSE,
7053 EL_EM_GATE_3_GRAY, -1, -1
7056 Xfake_door_4, TRUE, FALSE,
7057 EL_EM_GATE_4_GRAY, -1, -1
7060 Xfake_door_5, TRUE, FALSE,
7061 EL_EMC_GATE_5_GRAY, -1, -1
7064 Xfake_door_6, TRUE, FALSE,
7065 EL_EMC_GATE_6_GRAY, -1, -1
7068 Xfake_door_7, TRUE, FALSE,
7069 EL_EMC_GATE_7_GRAY, -1, -1
7072 Xfake_door_8, TRUE, FALSE,
7073 EL_EMC_GATE_8_GRAY, -1, -1
7077 Xballoon, TRUE, FALSE,
7081 Yballoon_n, FALSE, FALSE,
7082 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7085 Yballoon_nB, FALSE, TRUE,
7086 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7089 Yballoon_e, FALSE, FALSE,
7090 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7093 Yballoon_eB, FALSE, TRUE,
7094 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7097 Yballoon_s, FALSE, FALSE,
7098 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7101 Yballoon_sB, FALSE, TRUE,
7102 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7105 Yballoon_w, FALSE, FALSE,
7106 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7109 Yballoon_wB, FALSE, TRUE,
7110 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7114 Xball_1, TRUE, FALSE,
7115 EL_EMC_MAGIC_BALL, -1, -1
7118 Yball_1, FALSE, FALSE,
7119 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7122 Xball_2, FALSE, FALSE,
7123 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7126 Yball_2, FALSE, FALSE,
7127 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7130 Yball_blank, FALSE, FALSE,
7131 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7135 Xamoeba_1, TRUE, FALSE,
7136 EL_AMOEBA_DRY, ACTION_OTHER, -1
7139 Xamoeba_2, FALSE, FALSE,
7140 EL_AMOEBA_DRY, ACTION_OTHER, -1
7143 Xamoeba_3, FALSE, FALSE,
7144 EL_AMOEBA_DRY, ACTION_OTHER, -1
7147 Xamoeba_4, FALSE, FALSE,
7148 EL_AMOEBA_DRY, ACTION_OTHER, -1
7151 Xamoeba_5, TRUE, FALSE,
7152 EL_AMOEBA_WET, ACTION_OTHER, -1
7155 Xamoeba_6, FALSE, FALSE,
7156 EL_AMOEBA_WET, ACTION_OTHER, -1
7159 Xamoeba_7, FALSE, FALSE,
7160 EL_AMOEBA_WET, ACTION_OTHER, -1
7163 Xamoeba_8, FALSE, FALSE,
7164 EL_AMOEBA_WET, ACTION_OTHER, -1
7169 EL_AMOEBA_DROP, ACTION_GROWING, -1
7172 Xdrip_fall, FALSE, FALSE,
7173 EL_AMOEBA_DROP, -1, -1
7176 Xdrip_stretch, FALSE, FALSE,
7177 EL_AMOEBA_DROP, ACTION_FALLING, -1
7180 Xdrip_stretchB, FALSE, TRUE,
7181 EL_AMOEBA_DROP, ACTION_FALLING, -1
7184 Ydrip_1_s, FALSE, FALSE,
7185 EL_AMOEBA_DROP, ACTION_FALLING, -1
7188 Ydrip_1_sB, FALSE, TRUE,
7189 EL_AMOEBA_DROP, ACTION_FALLING, -1
7192 Ydrip_2_s, FALSE, FALSE,
7193 EL_AMOEBA_DROP, ACTION_FALLING, -1
7196 Ydrip_2_sB, FALSE, TRUE,
7197 EL_AMOEBA_DROP, ACTION_FALLING, -1
7201 Xwonderwall, TRUE, FALSE,
7202 EL_MAGIC_WALL, -1, -1
7205 Ywonderwall, FALSE, FALSE,
7206 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7210 Xwheel, TRUE, FALSE,
7211 EL_ROBOT_WHEEL, -1, -1
7214 Ywheel, FALSE, FALSE,
7215 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7219 Xswitch, TRUE, FALSE,
7220 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7223 Yswitch, FALSE, FALSE,
7224 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7228 Xbumper, TRUE, FALSE,
7229 EL_EMC_SPRING_BUMPER, -1, -1
7232 Ybumper, FALSE, FALSE,
7233 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7237 Xacid_nw, TRUE, FALSE,
7238 EL_ACID_POOL_TOPLEFT, -1, -1
7241 Xacid_ne, TRUE, FALSE,
7242 EL_ACID_POOL_TOPRIGHT, -1, -1
7245 Xacid_sw, TRUE, FALSE,
7246 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7249 Xacid_s, TRUE, FALSE,
7250 EL_ACID_POOL_BOTTOM, -1, -1
7253 Xacid_se, TRUE, FALSE,
7254 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7258 Xfake_blank, TRUE, FALSE,
7259 EL_INVISIBLE_WALL, -1, -1
7262 Yfake_blank, FALSE, FALSE,
7263 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7267 Xfake_grass, TRUE, FALSE,
7268 EL_EMC_FAKE_GRASS, -1, -1
7271 Yfake_grass, FALSE, FALSE,
7272 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7276 Xfake_amoeba, TRUE, FALSE,
7277 EL_EMC_DRIPPER, -1, -1
7280 Yfake_amoeba, FALSE, FALSE,
7281 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7285 Xlenses, TRUE, FALSE,
7286 EL_EMC_LENSES, -1, -1
7290 Xmagnify, TRUE, FALSE,
7291 EL_EMC_MAGNIFIER, -1, -1
7296 EL_QUICKSAND_EMPTY, -1, -1
7299 Xsand_stone, TRUE, FALSE,
7300 EL_QUICKSAND_FULL, -1, -1
7303 Xsand_stonein_1, FALSE, TRUE,
7304 EL_ROCK, ACTION_FILLING, -1
7307 Xsand_stonein_2, FALSE, TRUE,
7308 EL_ROCK, ACTION_FILLING, -1
7311 Xsand_stonein_3, FALSE, TRUE,
7312 EL_ROCK, ACTION_FILLING, -1
7315 Xsand_stonein_4, FALSE, TRUE,
7316 EL_ROCK, ACTION_FILLING, -1
7319 Xsand_sandstone_1, FALSE, FALSE,
7320 EL_QUICKSAND_FILLING, -1, -1
7323 Xsand_sandstone_2, FALSE, FALSE,
7324 EL_QUICKSAND_FILLING, -1, -1
7327 Xsand_sandstone_3, FALSE, FALSE,
7328 EL_QUICKSAND_FILLING, -1, -1
7331 Xsand_sandstone_4, FALSE, FALSE,
7332 EL_QUICKSAND_FILLING, -1, -1
7335 Xsand_stonesand_1, FALSE, FALSE,
7336 EL_QUICKSAND_EMPTYING, -1, -1
7339 Xsand_stonesand_2, FALSE, FALSE,
7340 EL_QUICKSAND_EMPTYING, -1, -1
7343 Xsand_stonesand_3, FALSE, FALSE,
7344 EL_QUICKSAND_EMPTYING, -1, -1
7347 Xsand_stonesand_4, FALSE, FALSE,
7348 EL_QUICKSAND_EMPTYING, -1, -1
7351 Xsand_stoneout_1, FALSE, FALSE,
7352 EL_ROCK, ACTION_EMPTYING, -1
7355 Xsand_stoneout_2, FALSE, FALSE,
7356 EL_ROCK, ACTION_EMPTYING, -1
7359 Xsand_stonesand_quickout_1, FALSE, FALSE,
7360 EL_QUICKSAND_EMPTYING, -1, -1
7363 Xsand_stonesand_quickout_2, FALSE, FALSE,
7364 EL_QUICKSAND_EMPTYING, -1, -1
7368 Xslide_ns, TRUE, FALSE,
7369 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7372 Yslide_ns_blank, FALSE, FALSE,
7373 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7376 Xslide_ew, TRUE, FALSE,
7377 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7380 Yslide_ew_blank, FALSE, FALSE,
7381 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7385 Xwind_n, TRUE, FALSE,
7386 EL_BALLOON_SWITCH_UP, -1, -1
7389 Xwind_e, TRUE, FALSE,
7390 EL_BALLOON_SWITCH_RIGHT, -1, -1
7393 Xwind_s, TRUE, FALSE,
7394 EL_BALLOON_SWITCH_DOWN, -1, -1
7397 Xwind_w, TRUE, FALSE,
7398 EL_BALLOON_SWITCH_LEFT, -1, -1
7401 Xwind_any, TRUE, FALSE,
7402 EL_BALLOON_SWITCH_ANY, -1, -1
7405 Xwind_stop, TRUE, FALSE,
7406 EL_BALLOON_SWITCH_NONE, -1, -1
7411 EL_EM_EXIT_CLOSED, -1, -1
7414 Xexit_1, TRUE, FALSE,
7415 EL_EM_EXIT_OPEN, -1, -1
7418 Xexit_2, FALSE, FALSE,
7419 EL_EM_EXIT_OPEN, -1, -1
7422 Xexit_3, FALSE, FALSE,
7423 EL_EM_EXIT_OPEN, -1, -1
7427 Xpause, FALSE, FALSE,
7432 Xwall_1, TRUE, FALSE,
7436 Xwall_2, TRUE, FALSE,
7437 EL_EMC_WALL_14, -1, -1
7440 Xwall_3, TRUE, FALSE,
7441 EL_EMC_WALL_15, -1, -1
7444 Xwall_4, TRUE, FALSE,
7445 EL_EMC_WALL_16, -1, -1
7449 Xroundwall_1, TRUE, FALSE,
7450 EL_WALL_SLIPPERY, -1, -1
7453 Xroundwall_2, TRUE, FALSE,
7454 EL_EMC_WALL_SLIPPERY_2, -1, -1
7457 Xroundwall_3, TRUE, FALSE,
7458 EL_EMC_WALL_SLIPPERY_3, -1, -1
7461 Xroundwall_4, TRUE, FALSE,
7462 EL_EMC_WALL_SLIPPERY_4, -1, -1
7466 Xsteel_1, TRUE, FALSE,
7467 EL_STEELWALL, -1, -1
7470 Xsteel_2, TRUE, FALSE,
7471 EL_EMC_STEELWALL_2, -1, -1
7474 Xsteel_3, TRUE, FALSE,
7475 EL_EMC_STEELWALL_3, -1, -1
7478 Xsteel_4, TRUE, FALSE,
7479 EL_EMC_STEELWALL_4, -1, -1
7483 Xdecor_1, TRUE, FALSE,
7484 EL_EMC_WALL_8, -1, -1
7487 Xdecor_2, TRUE, FALSE,
7488 EL_EMC_WALL_6, -1, -1
7491 Xdecor_3, TRUE, FALSE,
7492 EL_EMC_WALL_4, -1, -1
7495 Xdecor_4, TRUE, FALSE,
7496 EL_EMC_WALL_7, -1, -1
7499 Xdecor_5, TRUE, FALSE,
7500 EL_EMC_WALL_5, -1, -1
7503 Xdecor_6, TRUE, FALSE,
7504 EL_EMC_WALL_9, -1, -1
7507 Xdecor_7, TRUE, FALSE,
7508 EL_EMC_WALL_10, -1, -1
7511 Xdecor_8, TRUE, FALSE,
7512 EL_EMC_WALL_1, -1, -1
7515 Xdecor_9, TRUE, FALSE,
7516 EL_EMC_WALL_2, -1, -1
7519 Xdecor_10, TRUE, FALSE,
7520 EL_EMC_WALL_3, -1, -1
7523 Xdecor_11, TRUE, FALSE,
7524 EL_EMC_WALL_11, -1, -1
7527 Xdecor_12, TRUE, FALSE,
7528 EL_EMC_WALL_12, -1, -1
7532 Xalpha_0, TRUE, FALSE,
7533 EL_CHAR('0'), -1, -1
7536 Xalpha_1, TRUE, FALSE,
7537 EL_CHAR('1'), -1, -1
7540 Xalpha_2, TRUE, FALSE,
7541 EL_CHAR('2'), -1, -1
7544 Xalpha_3, TRUE, FALSE,
7545 EL_CHAR('3'), -1, -1
7548 Xalpha_4, TRUE, FALSE,
7549 EL_CHAR('4'), -1, -1
7552 Xalpha_5, TRUE, FALSE,
7553 EL_CHAR('5'), -1, -1
7556 Xalpha_6, TRUE, FALSE,
7557 EL_CHAR('6'), -1, -1
7560 Xalpha_7, TRUE, FALSE,
7561 EL_CHAR('7'), -1, -1
7564 Xalpha_8, TRUE, FALSE,
7565 EL_CHAR('8'), -1, -1
7568 Xalpha_9, TRUE, FALSE,
7569 EL_CHAR('9'), -1, -1
7572 Xalpha_excla, TRUE, FALSE,
7573 EL_CHAR('!'), -1, -1
7576 Xalpha_apost, TRUE, FALSE,
7577 EL_CHAR('\''), -1, -1
7580 Xalpha_comma, TRUE, FALSE,
7581 EL_CHAR(','), -1, -1
7584 Xalpha_minus, TRUE, FALSE,
7585 EL_CHAR('-'), -1, -1
7588 Xalpha_perio, TRUE, FALSE,
7589 EL_CHAR('.'), -1, -1
7592 Xalpha_colon, TRUE, FALSE,
7593 EL_CHAR(':'), -1, -1
7596 Xalpha_quest, TRUE, FALSE,
7597 EL_CHAR('?'), -1, -1
7600 Xalpha_a, TRUE, FALSE,
7601 EL_CHAR('A'), -1, -1
7604 Xalpha_b, TRUE, FALSE,
7605 EL_CHAR('B'), -1, -1
7608 Xalpha_c, TRUE, FALSE,
7609 EL_CHAR('C'), -1, -1
7612 Xalpha_d, TRUE, FALSE,
7613 EL_CHAR('D'), -1, -1
7616 Xalpha_e, TRUE, FALSE,
7617 EL_CHAR('E'), -1, -1
7620 Xalpha_f, TRUE, FALSE,
7621 EL_CHAR('F'), -1, -1
7624 Xalpha_g, TRUE, FALSE,
7625 EL_CHAR('G'), -1, -1
7628 Xalpha_h, TRUE, FALSE,
7629 EL_CHAR('H'), -1, -1
7632 Xalpha_i, TRUE, FALSE,
7633 EL_CHAR('I'), -1, -1
7636 Xalpha_j, TRUE, FALSE,
7637 EL_CHAR('J'), -1, -1
7640 Xalpha_k, TRUE, FALSE,
7641 EL_CHAR('K'), -1, -1
7644 Xalpha_l, TRUE, FALSE,
7645 EL_CHAR('L'), -1, -1
7648 Xalpha_m, TRUE, FALSE,
7649 EL_CHAR('M'), -1, -1
7652 Xalpha_n, TRUE, FALSE,
7653 EL_CHAR('N'), -1, -1
7656 Xalpha_o, TRUE, FALSE,
7657 EL_CHAR('O'), -1, -1
7660 Xalpha_p, TRUE, FALSE,
7661 EL_CHAR('P'), -1, -1
7664 Xalpha_q, TRUE, FALSE,
7665 EL_CHAR('Q'), -1, -1
7668 Xalpha_r, TRUE, FALSE,
7669 EL_CHAR('R'), -1, -1
7672 Xalpha_s, TRUE, FALSE,
7673 EL_CHAR('S'), -1, -1
7676 Xalpha_t, TRUE, FALSE,
7677 EL_CHAR('T'), -1, -1
7680 Xalpha_u, TRUE, FALSE,
7681 EL_CHAR('U'), -1, -1
7684 Xalpha_v, TRUE, FALSE,
7685 EL_CHAR('V'), -1, -1
7688 Xalpha_w, TRUE, FALSE,
7689 EL_CHAR('W'), -1, -1
7692 Xalpha_x, TRUE, FALSE,
7693 EL_CHAR('X'), -1, -1
7696 Xalpha_y, TRUE, FALSE,
7697 EL_CHAR('Y'), -1, -1
7700 Xalpha_z, TRUE, FALSE,
7701 EL_CHAR('Z'), -1, -1
7704 Xalpha_arrow_e, TRUE, FALSE,
7705 EL_CHAR('>'), -1, -1
7708 Xalpha_arrow_w, TRUE, FALSE,
7709 EL_CHAR('<'), -1, -1
7712 Xalpha_copyr, TRUE, FALSE,
7713 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7717 Ykey_1_blank, FALSE, FALSE,
7718 EL_EM_KEY_1, ACTION_COLLECTING, -1
7721 Ykey_2_blank, FALSE, FALSE,
7722 EL_EM_KEY_2, ACTION_COLLECTING, -1
7725 Ykey_3_blank, FALSE, FALSE,
7726 EL_EM_KEY_3, ACTION_COLLECTING, -1
7729 Ykey_4_blank, FALSE, FALSE,
7730 EL_EM_KEY_4, ACTION_COLLECTING, -1
7733 Ykey_5_blank, FALSE, FALSE,
7734 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7737 Ykey_6_blank, FALSE, FALSE,
7738 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7741 Ykey_7_blank, FALSE, FALSE,
7742 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7745 Ykey_8_blank, FALSE, FALSE,
7746 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7749 Ylenses_blank, FALSE, FALSE,
7750 EL_EMC_LENSES, ACTION_COLLECTING, -1
7753 Ymagnify_blank, FALSE, FALSE,
7754 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7757 Ygrass_blank, FALSE, FALSE,
7758 EL_EMC_GRASS, ACTION_SNAPPING, -1
7761 Ydirt_blank, FALSE, FALSE,
7762 EL_SAND, ACTION_SNAPPING, -1
7771 static struct Mapping_EM_to_RND_player
7780 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7784 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7788 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7792 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7796 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7800 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7804 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7808 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7812 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7816 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7820 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7824 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7828 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7832 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7836 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7840 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7844 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7848 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7852 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7856 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7860 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7864 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7868 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7872 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7876 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7880 EL_PLAYER_1, ACTION_DEFAULT, -1,
7884 EL_PLAYER_2, ACTION_DEFAULT, -1,
7888 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7892 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7896 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7900 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7904 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7908 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7912 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7916 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7920 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7924 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7928 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7932 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7936 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7940 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7944 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7948 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7952 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7956 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7960 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7964 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7968 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7972 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7976 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7980 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7984 EL_PLAYER_3, ACTION_DEFAULT, -1,
7988 EL_PLAYER_4, ACTION_DEFAULT, -1,
7997 int map_element_RND_to_EM_cave(int element_rnd)
7999 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
8000 static boolean mapping_initialized = FALSE;
8002 if (!mapping_initialized)
8006 // return "Xalpha_quest" for all undefined elements in mapping array
8007 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8008 mapping_RND_to_EM[i] = Xalpha_quest;
8010 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8011 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
8012 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
8013 em_object_mapping_list[i].element_em;
8015 mapping_initialized = TRUE;
8018 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
8020 Warn("invalid RND level element %d", element_rnd);
8025 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
8028 int map_element_EM_to_RND_cave(int element_em_cave)
8030 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8031 static boolean mapping_initialized = FALSE;
8033 if (!mapping_initialized)
8037 // return "EL_UNKNOWN" for all undefined elements in mapping array
8038 for (i = 0; i < GAME_TILE_MAX; i++)
8039 mapping_EM_to_RND[i] = EL_UNKNOWN;
8041 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8042 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8043 em_object_mapping_list[i].element_rnd;
8045 mapping_initialized = TRUE;
8048 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8050 Warn("invalid EM cave element %d", element_em_cave);
8055 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8058 int map_element_EM_to_RND_game(int element_em_game)
8060 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8061 static boolean mapping_initialized = FALSE;
8063 if (!mapping_initialized)
8067 // return "EL_UNKNOWN" for all undefined elements in mapping array
8068 for (i = 0; i < GAME_TILE_MAX; i++)
8069 mapping_EM_to_RND[i] = EL_UNKNOWN;
8071 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8072 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8073 em_object_mapping_list[i].element_rnd;
8075 mapping_initialized = TRUE;
8078 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8080 Warn("invalid EM game element %d", element_em_game);
8085 return mapping_EM_to_RND[element_em_game];
8088 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8090 struct LevelInfo_EM *level_em = level->native_em_level;
8091 struct CAVE *cav = level_em->cav;
8094 for (i = 0; i < GAME_TILE_MAX; i++)
8095 cav->android_array[i] = Cblank;
8097 for (i = 0; i < level->num_android_clone_elements; i++)
8099 int element_rnd = level->android_clone_element[i];
8100 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8102 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8103 if (em_object_mapping_list[j].element_rnd == element_rnd)
8104 cav->android_array[em_object_mapping_list[j].element_em] =
8109 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8111 struct LevelInfo_EM *level_em = level->native_em_level;
8112 struct CAVE *cav = level_em->cav;
8115 level->num_android_clone_elements = 0;
8117 for (i = 0; i < GAME_TILE_MAX; i++)
8119 int element_em_cave = cav->android_array[i];
8121 boolean element_found = FALSE;
8123 if (element_em_cave == Cblank)
8126 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8128 for (j = 0; j < level->num_android_clone_elements; j++)
8129 if (level->android_clone_element[j] == element_rnd)
8130 element_found = TRUE;
8134 level->android_clone_element[level->num_android_clone_elements++] =
8137 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8142 if (level->num_android_clone_elements == 0)
8144 level->num_android_clone_elements = 1;
8145 level->android_clone_element[0] = EL_EMPTY;
8149 int map_direction_RND_to_EM(int direction)
8151 return (direction == MV_UP ? 0 :
8152 direction == MV_RIGHT ? 1 :
8153 direction == MV_DOWN ? 2 :
8154 direction == MV_LEFT ? 3 :
8158 int map_direction_EM_to_RND(int direction)
8160 return (direction == 0 ? MV_UP :
8161 direction == 1 ? MV_RIGHT :
8162 direction == 2 ? MV_DOWN :
8163 direction == 3 ? MV_LEFT :
8167 int map_element_RND_to_SP(int element_rnd)
8169 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8171 if (element_rnd >= EL_SP_START &&
8172 element_rnd <= EL_SP_END)
8173 element_sp = element_rnd - EL_SP_START;
8174 else if (element_rnd == EL_EMPTY_SPACE)
8176 else if (element_rnd == EL_INVISIBLE_WALL)
8182 int map_element_SP_to_RND(int element_sp)
8184 int element_rnd = EL_UNKNOWN;
8186 if (element_sp >= 0x00 &&
8188 element_rnd = EL_SP_START + element_sp;
8189 else if (element_sp == 0x28)
8190 element_rnd = EL_INVISIBLE_WALL;
8195 int map_action_SP_to_RND(int action_sp)
8199 case actActive: return ACTION_ACTIVE;
8200 case actImpact: return ACTION_IMPACT;
8201 case actExploding: return ACTION_EXPLODING;
8202 case actDigging: return ACTION_DIGGING;
8203 case actSnapping: return ACTION_SNAPPING;
8204 case actCollecting: return ACTION_COLLECTING;
8205 case actPassing: return ACTION_PASSING;
8206 case actPushing: return ACTION_PUSHING;
8207 case actDropping: return ACTION_DROPPING;
8209 default: return ACTION_DEFAULT;
8213 int map_element_RND_to_MM(int element_rnd)
8215 return (element_rnd >= EL_MM_START_1 &&
8216 element_rnd <= EL_MM_END_1 ?
8217 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8219 element_rnd >= EL_MM_START_2 &&
8220 element_rnd <= EL_MM_END_2 ?
8221 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8223 element_rnd >= EL_CHAR_START &&
8224 element_rnd <= EL_CHAR_END ?
8225 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8227 element_rnd >= EL_MM_RUNTIME_START &&
8228 element_rnd <= EL_MM_RUNTIME_END ?
8229 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8231 EL_MM_EMPTY_NATIVE);
8234 int map_element_MM_to_RND(int element_mm)
8236 return (element_mm == EL_MM_EMPTY_NATIVE ||
8237 element_mm == EL_DF_EMPTY_NATIVE ?
8240 element_mm >= EL_MM_START_1_NATIVE &&
8241 element_mm <= EL_MM_END_1_NATIVE ?
8242 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8244 element_mm >= EL_MM_START_2_NATIVE &&
8245 element_mm <= EL_MM_END_2_NATIVE ?
8246 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8248 element_mm >= EL_MM_CHAR_START_NATIVE &&
8249 element_mm <= EL_MM_CHAR_END_NATIVE ?
8250 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8252 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8253 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8254 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8259 int map_action_MM_to_RND(int action_mm)
8261 // all MM actions are defined to exactly match their RND counterparts
8265 int map_sound_MM_to_RND(int sound_mm)
8269 case SND_MM_GAME_LEVELTIME_CHARGING:
8270 return SND_GAME_LEVELTIME_CHARGING;
8272 case SND_MM_GAME_HEALTH_CHARGING:
8273 return SND_GAME_HEALTH_CHARGING;
8276 return SND_UNDEFINED;
8280 int map_mm_wall_element(int element)
8282 return (element >= EL_MM_STEEL_WALL_START &&
8283 element <= EL_MM_STEEL_WALL_END ?
8286 element >= EL_MM_WOODEN_WALL_START &&
8287 element <= EL_MM_WOODEN_WALL_END ?
8290 element >= EL_MM_ICE_WALL_START &&
8291 element <= EL_MM_ICE_WALL_END ?
8294 element >= EL_MM_AMOEBA_WALL_START &&
8295 element <= EL_MM_AMOEBA_WALL_END ?
8298 element >= EL_DF_STEEL_WALL_START &&
8299 element <= EL_DF_STEEL_WALL_END ?
8302 element >= EL_DF_WOODEN_WALL_START &&
8303 element <= EL_DF_WOODEN_WALL_END ?
8309 int map_mm_wall_element_editor(int element)
8313 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8314 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8315 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8316 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8317 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8318 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8320 default: return element;
8324 int get_next_element(int element)
8328 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8329 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8330 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8331 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8332 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8333 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8334 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8335 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8336 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8337 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8338 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8340 default: return element;
8344 int el2img_mm(int element_mm)
8346 return el2img(map_element_MM_to_RND(element_mm));
8349 int el_act2img_mm(int element_mm, int action)
8351 return el_act2img(map_element_MM_to_RND(element_mm), action);
8354 int el_act_dir2img(int element, int action, int direction)
8356 element = GFX_ELEMENT(element);
8357 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8359 // direction_graphic[][] == graphic[] for undefined direction graphics
8360 return element_info[element].direction_graphic[action][direction];
8363 static int el_act_dir2crm(int element, int action, int direction)
8365 element = GFX_ELEMENT(element);
8366 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8368 // direction_graphic[][] == graphic[] for undefined direction graphics
8369 return element_info[element].direction_crumbled[action][direction];
8372 int el_act2img(int element, int action)
8374 element = GFX_ELEMENT(element);
8376 return element_info[element].graphic[action];
8379 int el_act2crm(int element, int action)
8381 element = GFX_ELEMENT(element);
8383 return element_info[element].crumbled[action];
8386 int el_dir2img(int element, int direction)
8388 element = GFX_ELEMENT(element);
8390 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8393 int el2baseimg(int element)
8395 return element_info[element].graphic[ACTION_DEFAULT];
8398 int el2img(int element)
8400 element = GFX_ELEMENT(element);
8402 return element_info[element].graphic[ACTION_DEFAULT];
8405 int el2edimg(int element)
8407 element = GFX_ELEMENT(element);
8409 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8412 int el2preimg(int element)
8414 element = GFX_ELEMENT(element);
8416 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8419 int el2panelimg(int element)
8421 element = GFX_ELEMENT(element);
8423 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8426 int font2baseimg(int font_nr)
8428 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8431 int getBeltNrFromBeltElement(int element)
8433 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8434 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8435 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8438 int getBeltNrFromBeltActiveElement(int element)
8440 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8441 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8442 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8445 int getBeltNrFromBeltSwitchElement(int element)
8447 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8448 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8449 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8452 int getBeltDirNrFromBeltElement(int element)
8454 static int belt_base_element[4] =
8456 EL_CONVEYOR_BELT_1_LEFT,
8457 EL_CONVEYOR_BELT_2_LEFT,
8458 EL_CONVEYOR_BELT_3_LEFT,
8459 EL_CONVEYOR_BELT_4_LEFT
8462 int belt_nr = getBeltNrFromBeltElement(element);
8463 int belt_dir_nr = element - belt_base_element[belt_nr];
8465 return (belt_dir_nr % 3);
8468 int getBeltDirNrFromBeltSwitchElement(int element)
8470 static int belt_base_element[4] =
8472 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8473 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8474 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8475 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8478 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8479 int belt_dir_nr = element - belt_base_element[belt_nr];
8481 return (belt_dir_nr % 3);
8484 int getBeltDirFromBeltElement(int element)
8486 static int belt_move_dir[3] =
8493 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8495 return belt_move_dir[belt_dir_nr];
8498 int getBeltDirFromBeltSwitchElement(int element)
8500 static int belt_move_dir[3] =
8507 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8509 return belt_move_dir[belt_dir_nr];
8512 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8514 static int belt_base_element[4] =
8516 EL_CONVEYOR_BELT_1_LEFT,
8517 EL_CONVEYOR_BELT_2_LEFT,
8518 EL_CONVEYOR_BELT_3_LEFT,
8519 EL_CONVEYOR_BELT_4_LEFT
8522 return belt_base_element[belt_nr] + belt_dir_nr;
8525 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8527 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8529 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8532 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8534 static int belt_base_element[4] =
8536 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8537 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8538 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8539 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8542 return belt_base_element[belt_nr] + belt_dir_nr;
8545 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8547 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8549 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8552 boolean swapTiles_EM(boolean is_pre_emc_cave)
8554 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8557 boolean getTeamMode_EM(void)
8559 return game.team_mode || network_playing;
8562 boolean isActivePlayer_EM(int player_nr)
8564 return stored_player[player_nr].active;
8567 unsigned int InitRND(int seed)
8569 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8570 return InitEngineRandom_EM(seed);
8571 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8572 return InitEngineRandom_SP(seed);
8573 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8574 return InitEngineRandom_MM(seed);
8576 return InitEngineRandom_RND(seed);
8579 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8580 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8582 static int get_effective_element_EM(int tile, int frame_em)
8584 int element = object_mapping[tile].element_rnd;
8585 int action = object_mapping[tile].action;
8586 boolean is_backside = object_mapping[tile].is_backside;
8587 boolean action_removing = (action == ACTION_DIGGING ||
8588 action == ACTION_SNAPPING ||
8589 action == ACTION_COLLECTING);
8597 return (frame_em > 5 ? EL_EMPTY : element);
8603 else // frame_em == 7
8614 case Ydiamond_stone:
8618 case Xdrip_stretchB:
8634 case Ymagnify_blank:
8637 case Xsand_stonein_1:
8638 case Xsand_stonein_2:
8639 case Xsand_stonein_3:
8640 case Xsand_stonein_4:
8644 return (is_backside || action_removing ? EL_EMPTY : element);
8649 static boolean check_linear_animation_EM(int tile)
8653 case Xsand_stonesand_1:
8654 case Xsand_stonesand_quickout_1:
8655 case Xsand_sandstone_1:
8656 case Xsand_stonein_1:
8657 case Xsand_stoneout_1:
8685 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8686 boolean has_crumbled_graphics,
8687 int crumbled, int sync_frame)
8689 // if element can be crumbled, but certain action graphics are just empty
8690 // space (like instantly snapping sand to empty space in 1 frame), do not
8691 // treat these empty space graphics as crumbled graphics in EMC engine
8692 if (crumbled == IMG_EMPTY_SPACE)
8693 has_crumbled_graphics = FALSE;
8695 if (has_crumbled_graphics)
8697 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8698 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8699 g_crumbled->anim_delay,
8700 g_crumbled->anim_mode,
8701 g_crumbled->anim_start_frame,
8704 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8705 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8707 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8708 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8710 g_em->has_crumbled_graphics = TRUE;
8714 g_em->crumbled_bitmap = NULL;
8715 g_em->crumbled_src_x = 0;
8716 g_em->crumbled_src_y = 0;
8717 g_em->crumbled_border_size = 0;
8718 g_em->crumbled_tile_size = 0;
8720 g_em->has_crumbled_graphics = FALSE;
8725 void ResetGfxAnimation_EM(int x, int y, int tile)
8731 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8732 int tile, int frame_em, int x, int y)
8734 int action = object_mapping[tile].action;
8735 int direction = object_mapping[tile].direction;
8736 int effective_element = get_effective_element_EM(tile, frame_em);
8737 int graphic = (direction == MV_NONE ?
8738 el_act2img(effective_element, action) :
8739 el_act_dir2img(effective_element, action, direction));
8740 struct GraphicInfo *g = &graphic_info[graphic];
8742 boolean action_removing = (action == ACTION_DIGGING ||
8743 action == ACTION_SNAPPING ||
8744 action == ACTION_COLLECTING);
8745 boolean action_moving = (action == ACTION_FALLING ||
8746 action == ACTION_MOVING ||
8747 action == ACTION_PUSHING ||
8748 action == ACTION_EATING ||
8749 action == ACTION_FILLING ||
8750 action == ACTION_EMPTYING);
8751 boolean action_falling = (action == ACTION_FALLING ||
8752 action == ACTION_FILLING ||
8753 action == ACTION_EMPTYING);
8755 // special case: graphic uses "2nd movement tile" and has defined
8756 // 7 frames for movement animation (or less) => use default graphic
8757 // for last (8th) frame which ends the movement animation
8758 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8760 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8761 graphic = (direction == MV_NONE ?
8762 el_act2img(effective_element, action) :
8763 el_act_dir2img(effective_element, action, direction));
8765 g = &graphic_info[graphic];
8768 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8772 else if (action_moving)
8774 boolean is_backside = object_mapping[tile].is_backside;
8778 int direction = object_mapping[tile].direction;
8779 int move_dir = (action_falling ? MV_DOWN : direction);
8784 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8785 if (g->double_movement && frame_em == 0)
8789 if (move_dir == MV_LEFT)
8790 GfxFrame[x - 1][y] = GfxFrame[x][y];
8791 else if (move_dir == MV_RIGHT)
8792 GfxFrame[x + 1][y] = GfxFrame[x][y];
8793 else if (move_dir == MV_UP)
8794 GfxFrame[x][y - 1] = GfxFrame[x][y];
8795 else if (move_dir == MV_DOWN)
8796 GfxFrame[x][y + 1] = GfxFrame[x][y];
8803 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8804 if (tile == Xsand_stonesand_quickout_1 ||
8805 tile == Xsand_stonesand_quickout_2)
8809 if (graphic_info[graphic].anim_global_sync)
8810 sync_frame = FrameCounter;
8811 else if (graphic_info[graphic].anim_global_anim_sync)
8812 sync_frame = getGlobalAnimSyncFrame();
8813 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8814 sync_frame = GfxFrame[x][y];
8816 sync_frame = 0; // playfield border (pseudo steel)
8818 SetRandomAnimationValue(x, y);
8820 int frame = getAnimationFrame(g->anim_frames,
8823 g->anim_start_frame,
8826 g_em->unique_identifier =
8827 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8830 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8831 int tile, int frame_em, int x, int y)
8833 int action = object_mapping[tile].action;
8834 int direction = object_mapping[tile].direction;
8835 boolean is_backside = object_mapping[tile].is_backside;
8836 int effective_element = get_effective_element_EM(tile, frame_em);
8837 int effective_action = action;
8838 int graphic = (direction == MV_NONE ?
8839 el_act2img(effective_element, effective_action) :
8840 el_act_dir2img(effective_element, effective_action,
8842 int crumbled = (direction == MV_NONE ?
8843 el_act2crm(effective_element, effective_action) :
8844 el_act_dir2crm(effective_element, effective_action,
8846 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8847 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8848 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8849 struct GraphicInfo *g = &graphic_info[graphic];
8852 // special case: graphic uses "2nd movement tile" and has defined
8853 // 7 frames for movement animation (or less) => use default graphic
8854 // for last (8th) frame which ends the movement animation
8855 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8857 effective_action = ACTION_DEFAULT;
8858 graphic = (direction == MV_NONE ?
8859 el_act2img(effective_element, effective_action) :
8860 el_act_dir2img(effective_element, effective_action,
8862 crumbled = (direction == MV_NONE ?
8863 el_act2crm(effective_element, effective_action) :
8864 el_act_dir2crm(effective_element, effective_action,
8867 g = &graphic_info[graphic];
8870 if (graphic_info[graphic].anim_global_sync)
8871 sync_frame = FrameCounter;
8872 else if (graphic_info[graphic].anim_global_anim_sync)
8873 sync_frame = getGlobalAnimSyncFrame();
8874 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8875 sync_frame = GfxFrame[x][y];
8877 sync_frame = 0; // playfield border (pseudo steel)
8879 SetRandomAnimationValue(x, y);
8881 int frame = getAnimationFrame(g->anim_frames,
8884 g->anim_start_frame,
8887 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8888 g->double_movement && is_backside);
8890 // (updating the "crumbled" graphic definitions is probably not really needed,
8891 // as animations for crumbled graphics can't be longer than one EMC cycle)
8892 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8896 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8897 int player_nr, int anim, int frame_em)
8899 int element = player_mapping[player_nr][anim].element_rnd;
8900 int action = player_mapping[player_nr][anim].action;
8901 int direction = player_mapping[player_nr][anim].direction;
8902 int graphic = (direction == MV_NONE ?
8903 el_act2img(element, action) :
8904 el_act_dir2img(element, action, direction));
8905 struct GraphicInfo *g = &graphic_info[graphic];
8908 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8910 stored_player[player_nr].StepFrame = frame_em;
8912 sync_frame = stored_player[player_nr].Frame;
8914 int frame = getAnimationFrame(g->anim_frames,
8917 g->anim_start_frame,
8920 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8921 &g_em->src_x, &g_em->src_y, FALSE);
8924 void InitGraphicInfo_EM(void)
8928 // always start with reliable default values
8929 for (i = 0; i < GAME_TILE_MAX; i++)
8931 object_mapping[i].element_rnd = EL_UNKNOWN;
8932 object_mapping[i].is_backside = FALSE;
8933 object_mapping[i].action = ACTION_DEFAULT;
8934 object_mapping[i].direction = MV_NONE;
8937 // always start with reliable default values
8938 for (p = 0; p < MAX_PLAYERS; p++)
8940 for (i = 0; i < PLY_MAX; i++)
8942 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8943 player_mapping[p][i].action = ACTION_DEFAULT;
8944 player_mapping[p][i].direction = MV_NONE;
8948 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8950 int e = em_object_mapping_list[i].element_em;
8952 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8953 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8955 if (em_object_mapping_list[i].action != -1)
8956 object_mapping[e].action = em_object_mapping_list[i].action;
8958 if (em_object_mapping_list[i].direction != -1)
8959 object_mapping[e].direction =
8960 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8963 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8965 int a = em_player_mapping_list[i].action_em;
8966 int p = em_player_mapping_list[i].player_nr;
8968 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8970 if (em_player_mapping_list[i].action != -1)
8971 player_mapping[p][a].action = em_player_mapping_list[i].action;
8973 if (em_player_mapping_list[i].direction != -1)
8974 player_mapping[p][a].direction =
8975 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8978 for (i = 0; i < GAME_TILE_MAX; i++)
8980 int element = object_mapping[i].element_rnd;
8981 int action = object_mapping[i].action;
8982 int direction = object_mapping[i].direction;
8983 boolean is_backside = object_mapping[i].is_backside;
8984 boolean action_exploding = ((action == ACTION_EXPLODING ||
8985 action == ACTION_SMASHED_BY_ROCK ||
8986 action == ACTION_SMASHED_BY_SPRING) &&
8987 element != EL_DIAMOND);
8988 boolean action_active = (action == ACTION_ACTIVE);
8989 boolean action_other = (action == ACTION_OTHER);
8991 for (j = 0; j < 8; j++)
8993 int effective_element = get_effective_element_EM(i, j);
8994 int effective_action = (j < 7 ? action :
8995 i == Xdrip_stretch ? action :
8996 i == Xdrip_stretchB ? action :
8997 i == Ydrip_1_s ? action :
8998 i == Ydrip_1_sB ? action :
8999 i == Yball_1 ? action :
9000 i == Xball_2 ? action :
9001 i == Yball_2 ? action :
9002 i == Yball_blank ? action :
9003 i == Ykey_1_blank ? action :
9004 i == Ykey_2_blank ? action :
9005 i == Ykey_3_blank ? action :
9006 i == Ykey_4_blank ? action :
9007 i == Ykey_5_blank ? action :
9008 i == Ykey_6_blank ? action :
9009 i == Ykey_7_blank ? action :
9010 i == Ykey_8_blank ? action :
9011 i == Ylenses_blank ? action :
9012 i == Ymagnify_blank ? action :
9013 i == Ygrass_blank ? action :
9014 i == Ydirt_blank ? action :
9015 i == Xsand_stonein_1 ? action :
9016 i == Xsand_stonein_2 ? action :
9017 i == Xsand_stonein_3 ? action :
9018 i == Xsand_stonein_4 ? action :
9019 i == Xsand_stoneout_1 ? action :
9020 i == Xsand_stoneout_2 ? action :
9021 i == Xboom_android ? ACTION_EXPLODING :
9022 action_exploding ? ACTION_EXPLODING :
9023 action_active ? action :
9024 action_other ? action :
9026 int graphic = (el_act_dir2img(effective_element, effective_action,
9028 int crumbled = (el_act_dir2crm(effective_element, effective_action,
9030 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
9031 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
9032 boolean has_action_graphics = (graphic != base_graphic);
9033 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9034 struct GraphicInfo *g = &graphic_info[graphic];
9035 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9038 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9039 boolean special_animation = (action != ACTION_DEFAULT &&
9040 g->anim_frames == 3 &&
9041 g->anim_delay == 2 &&
9042 g->anim_mode & ANIM_LINEAR);
9043 int sync_frame = (i == Xdrip_stretch ? 7 :
9044 i == Xdrip_stretchB ? 7 :
9045 i == Ydrip_2_s ? j + 8 :
9046 i == Ydrip_2_sB ? j + 8 :
9055 i == Xfake_acid_1 ? 0 :
9056 i == Xfake_acid_2 ? 10 :
9057 i == Xfake_acid_3 ? 20 :
9058 i == Xfake_acid_4 ? 30 :
9059 i == Xfake_acid_5 ? 40 :
9060 i == Xfake_acid_6 ? 50 :
9061 i == Xfake_acid_7 ? 60 :
9062 i == Xfake_acid_8 ? 70 :
9063 i == Xfake_acid_1_player ? 0 :
9064 i == Xfake_acid_2_player ? 10 :
9065 i == Xfake_acid_3_player ? 20 :
9066 i == Xfake_acid_4_player ? 30 :
9067 i == Xfake_acid_5_player ? 40 :
9068 i == Xfake_acid_6_player ? 50 :
9069 i == Xfake_acid_7_player ? 60 :
9070 i == Xfake_acid_8_player ? 70 :
9072 i == Yball_2 ? j + 8 :
9073 i == Yball_blank ? j + 1 :
9074 i == Ykey_1_blank ? j + 1 :
9075 i == Ykey_2_blank ? j + 1 :
9076 i == Ykey_3_blank ? j + 1 :
9077 i == Ykey_4_blank ? j + 1 :
9078 i == Ykey_5_blank ? j + 1 :
9079 i == Ykey_6_blank ? j + 1 :
9080 i == Ykey_7_blank ? j + 1 :
9081 i == Ykey_8_blank ? j + 1 :
9082 i == Ylenses_blank ? j + 1 :
9083 i == Ymagnify_blank ? j + 1 :
9084 i == Ygrass_blank ? j + 1 :
9085 i == Ydirt_blank ? j + 1 :
9086 i == Xamoeba_1 ? 0 :
9087 i == Xamoeba_2 ? 1 :
9088 i == Xamoeba_3 ? 2 :
9089 i == Xamoeba_4 ? 3 :
9090 i == Xamoeba_5 ? 0 :
9091 i == Xamoeba_6 ? 1 :
9092 i == Xamoeba_7 ? 2 :
9093 i == Xamoeba_8 ? 3 :
9094 i == Xexit_2 ? j + 8 :
9095 i == Xexit_3 ? j + 16 :
9096 i == Xdynamite_1 ? 0 :
9097 i == Xdynamite_2 ? 8 :
9098 i == Xdynamite_3 ? 16 :
9099 i == Xdynamite_4 ? 24 :
9100 i == Xsand_stonein_1 ? j + 1 :
9101 i == Xsand_stonein_2 ? j + 9 :
9102 i == Xsand_stonein_3 ? j + 17 :
9103 i == Xsand_stonein_4 ? j + 25 :
9104 i == Xsand_stoneout_1 && j == 0 ? 0 :
9105 i == Xsand_stoneout_1 && j == 1 ? 0 :
9106 i == Xsand_stoneout_1 && j == 2 ? 1 :
9107 i == Xsand_stoneout_1 && j == 3 ? 2 :
9108 i == Xsand_stoneout_1 && j == 4 ? 2 :
9109 i == Xsand_stoneout_1 && j == 5 ? 3 :
9110 i == Xsand_stoneout_1 && j == 6 ? 4 :
9111 i == Xsand_stoneout_1 && j == 7 ? 4 :
9112 i == Xsand_stoneout_2 && j == 0 ? 5 :
9113 i == Xsand_stoneout_2 && j == 1 ? 6 :
9114 i == Xsand_stoneout_2 && j == 2 ? 7 :
9115 i == Xsand_stoneout_2 && j == 3 ? 8 :
9116 i == Xsand_stoneout_2 && j == 4 ? 9 :
9117 i == Xsand_stoneout_2 && j == 5 ? 11 :
9118 i == Xsand_stoneout_2 && j == 6 ? 13 :
9119 i == Xsand_stoneout_2 && j == 7 ? 15 :
9120 i == Xboom_bug && j == 1 ? 2 :
9121 i == Xboom_bug && j == 2 ? 2 :
9122 i == Xboom_bug && j == 3 ? 4 :
9123 i == Xboom_bug && j == 4 ? 4 :
9124 i == Xboom_bug && j == 5 ? 2 :
9125 i == Xboom_bug && j == 6 ? 2 :
9126 i == Xboom_bug && j == 7 ? 0 :
9127 i == Xboom_tank && j == 1 ? 2 :
9128 i == Xboom_tank && j == 2 ? 2 :
9129 i == Xboom_tank && j == 3 ? 4 :
9130 i == Xboom_tank && j == 4 ? 4 :
9131 i == Xboom_tank && j == 5 ? 2 :
9132 i == Xboom_tank && j == 6 ? 2 :
9133 i == Xboom_tank && j == 7 ? 0 :
9134 i == Xboom_android && j == 7 ? 6 :
9135 i == Xboom_1 && j == 1 ? 2 :
9136 i == Xboom_1 && j == 2 ? 2 :
9137 i == Xboom_1 && j == 3 ? 4 :
9138 i == Xboom_1 && j == 4 ? 4 :
9139 i == Xboom_1 && j == 5 ? 6 :
9140 i == Xboom_1 && j == 6 ? 6 :
9141 i == Xboom_1 && j == 7 ? 8 :
9142 i == Xboom_2 && j == 0 ? 8 :
9143 i == Xboom_2 && j == 1 ? 8 :
9144 i == Xboom_2 && j == 2 ? 10 :
9145 i == Xboom_2 && j == 3 ? 10 :
9146 i == Xboom_2 && j == 4 ? 10 :
9147 i == Xboom_2 && j == 5 ? 12 :
9148 i == Xboom_2 && j == 6 ? 12 :
9149 i == Xboom_2 && j == 7 ? 12 :
9150 special_animation && j == 4 ? 3 :
9151 effective_action != action ? 0 :
9153 int frame = getAnimationFrame(g->anim_frames,
9156 g->anim_start_frame,
9159 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9160 g->double_movement && is_backside);
9162 g_em->bitmap = src_bitmap;
9163 g_em->src_x = src_x;
9164 g_em->src_y = src_y;
9165 g_em->src_offset_x = 0;
9166 g_em->src_offset_y = 0;
9167 g_em->dst_offset_x = 0;
9168 g_em->dst_offset_y = 0;
9169 g_em->width = TILEX;
9170 g_em->height = TILEY;
9172 g_em->preserve_background = FALSE;
9174 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9177 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9178 effective_action == ACTION_MOVING ||
9179 effective_action == ACTION_PUSHING ||
9180 effective_action == ACTION_EATING)) ||
9181 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9182 effective_action == ACTION_EMPTYING)))
9185 (effective_action == ACTION_FALLING ||
9186 effective_action == ACTION_FILLING ||
9187 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9188 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9189 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9190 int num_steps = (i == Ydrip_1_s ? 16 :
9191 i == Ydrip_1_sB ? 16 :
9192 i == Ydrip_2_s ? 16 :
9193 i == Ydrip_2_sB ? 16 :
9194 i == Xsand_stonein_1 ? 32 :
9195 i == Xsand_stonein_2 ? 32 :
9196 i == Xsand_stonein_3 ? 32 :
9197 i == Xsand_stonein_4 ? 32 :
9198 i == Xsand_stoneout_1 ? 16 :
9199 i == Xsand_stoneout_2 ? 16 : 8);
9200 int cx = ABS(dx) * (TILEX / num_steps);
9201 int cy = ABS(dy) * (TILEY / num_steps);
9202 int step_frame = (i == Ydrip_2_s ? j + 8 :
9203 i == Ydrip_2_sB ? j + 8 :
9204 i == Xsand_stonein_2 ? j + 8 :
9205 i == Xsand_stonein_3 ? j + 16 :
9206 i == Xsand_stonein_4 ? j + 24 :
9207 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9208 int step = (is_backside ? step_frame : num_steps - step_frame);
9210 if (is_backside) // tile where movement starts
9212 if (dx < 0 || dy < 0)
9214 g_em->src_offset_x = cx * step;
9215 g_em->src_offset_y = cy * step;
9219 g_em->dst_offset_x = cx * step;
9220 g_em->dst_offset_y = cy * step;
9223 else // tile where movement ends
9225 if (dx < 0 || dy < 0)
9227 g_em->dst_offset_x = cx * step;
9228 g_em->dst_offset_y = cy * step;
9232 g_em->src_offset_x = cx * step;
9233 g_em->src_offset_y = cy * step;
9237 g_em->width = TILEX - cx * step;
9238 g_em->height = TILEY - cy * step;
9241 // create unique graphic identifier to decide if tile must be redrawn
9242 /* bit 31 - 16 (16 bit): EM style graphic
9243 bit 15 - 12 ( 4 bit): EM style frame
9244 bit 11 - 6 ( 6 bit): graphic width
9245 bit 5 - 0 ( 6 bit): graphic height */
9246 g_em->unique_identifier =
9247 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9251 for (i = 0; i < GAME_TILE_MAX; i++)
9253 for (j = 0; j < 8; j++)
9255 int element = object_mapping[i].element_rnd;
9256 int action = object_mapping[i].action;
9257 int direction = object_mapping[i].direction;
9258 boolean is_backside = object_mapping[i].is_backside;
9259 int graphic_action = el_act_dir2img(element, action, direction);
9260 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9262 if ((action == ACTION_SMASHED_BY_ROCK ||
9263 action == ACTION_SMASHED_BY_SPRING ||
9264 action == ACTION_EATING) &&
9265 graphic_action == graphic_default)
9267 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9268 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9269 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9270 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9273 // no separate animation for "smashed by rock" -- use rock instead
9274 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9275 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9277 g_em->bitmap = g_xx->bitmap;
9278 g_em->src_x = g_xx->src_x;
9279 g_em->src_y = g_xx->src_y;
9280 g_em->src_offset_x = g_xx->src_offset_x;
9281 g_em->src_offset_y = g_xx->src_offset_y;
9282 g_em->dst_offset_x = g_xx->dst_offset_x;
9283 g_em->dst_offset_y = g_xx->dst_offset_y;
9284 g_em->width = g_xx->width;
9285 g_em->height = g_xx->height;
9286 g_em->unique_identifier = g_xx->unique_identifier;
9289 g_em->preserve_background = TRUE;
9294 for (p = 0; p < MAX_PLAYERS; p++)
9296 for (i = 0; i < PLY_MAX; i++)
9298 int element = player_mapping[p][i].element_rnd;
9299 int action = player_mapping[p][i].action;
9300 int direction = player_mapping[p][i].direction;
9302 for (j = 0; j < 8; j++)
9304 int effective_element = element;
9305 int effective_action = action;
9306 int graphic = (direction == MV_NONE ?
9307 el_act2img(effective_element, effective_action) :
9308 el_act_dir2img(effective_element, effective_action,
9310 struct GraphicInfo *g = &graphic_info[graphic];
9311 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9315 int frame = getAnimationFrame(g->anim_frames,
9318 g->anim_start_frame,
9321 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9323 g_em->bitmap = src_bitmap;
9324 g_em->src_x = src_x;
9325 g_em->src_y = src_y;
9326 g_em->src_offset_x = 0;
9327 g_em->src_offset_y = 0;
9328 g_em->dst_offset_x = 0;
9329 g_em->dst_offset_y = 0;
9330 g_em->width = TILEX;
9331 g_em->height = TILEY;
9337 static void CheckSaveEngineSnapshot_EM(int frame,
9338 boolean any_player_moving,
9339 boolean any_player_snapping,
9340 boolean any_player_dropping)
9342 if (frame == 7 && !any_player_dropping)
9344 if (!local_player->was_waiting)
9346 if (!CheckSaveEngineSnapshotToList())
9349 local_player->was_waiting = TRUE;
9352 else if (any_player_moving || any_player_snapping || any_player_dropping)
9354 local_player->was_waiting = FALSE;
9358 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9359 boolean murphy_is_dropping)
9361 if (murphy_is_waiting)
9363 if (!local_player->was_waiting)
9365 if (!CheckSaveEngineSnapshotToList())
9368 local_player->was_waiting = TRUE;
9373 local_player->was_waiting = FALSE;
9377 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9378 boolean button_released)
9380 if (button_released)
9382 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9383 CheckSaveEngineSnapshotToList();
9385 else if (element_clicked)
9387 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9388 CheckSaveEngineSnapshotToList();
9390 game.snapshot.changed_action = TRUE;
9394 boolean CheckSingleStepMode_EM(int frame,
9395 boolean any_player_moving,
9396 boolean any_player_snapping,
9397 boolean any_player_dropping)
9399 if (tape.single_step && tape.recording && !tape.pausing)
9400 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9401 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9403 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9404 any_player_snapping, any_player_dropping);
9406 return tape.pausing;
9409 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9410 boolean murphy_is_dropping)
9412 boolean murphy_starts_dropping = FALSE;
9415 for (i = 0; i < MAX_PLAYERS; i++)
9416 if (stored_player[i].force_dropping)
9417 murphy_starts_dropping = TRUE;
9419 if (tape.single_step && tape.recording && !tape.pausing)
9420 if (murphy_is_waiting && !murphy_starts_dropping)
9421 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9423 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9426 void CheckSingleStepMode_MM(boolean element_clicked,
9427 boolean button_released)
9429 if (tape.single_step && tape.recording && !tape.pausing)
9430 if (button_released)
9431 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9433 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9436 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9437 int graphic, int sync_frame)
9439 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9441 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9444 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9446 return (IS_NEXT_FRAME(sync_frame, graphic));
9449 int getGraphicInfo_Delay(int graphic)
9451 return graphic_info[graphic].anim_delay;
9454 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9456 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9459 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9465 void PlayMenuSoundExt(int sound)
9467 if (sound == SND_UNDEFINED)
9470 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9471 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9474 if (IS_LOOP_SOUND(sound))
9475 PlaySoundLoop(sound);
9480 void PlayMenuSound(void)
9482 PlayMenuSoundExt(menu.sound[game_status]);
9485 void PlayMenuSoundStereo(int sound, int stereo_position)
9487 if (sound == SND_UNDEFINED)
9490 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9491 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9494 if (IS_LOOP_SOUND(sound))
9495 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9497 PlaySoundStereo(sound, stereo_position);
9500 void PlayMenuSoundIfLoopExt(int sound)
9502 if (sound == SND_UNDEFINED)
9505 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9506 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9509 if (IS_LOOP_SOUND(sound))
9510 PlaySoundLoop(sound);
9513 void PlayMenuSoundIfLoop(void)
9515 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9518 void PlayMenuMusicExt(int music)
9520 if (music == MUS_UNDEFINED)
9523 if (!setup.sound_music)
9526 if (IS_LOOP_MUSIC(music))
9527 PlayMusicLoop(music);
9532 void PlayMenuMusic(void)
9534 char *curr_music = getCurrentlyPlayingMusicFilename();
9535 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9537 if (!strEqual(curr_music, next_music))
9538 PlayMenuMusicExt(menu.music[game_status]);
9541 void PlayMenuSoundsAndMusic(void)
9547 static void FadeMenuSounds(void)
9552 static void FadeMenuMusic(void)
9554 char *curr_music = getCurrentlyPlayingMusicFilename();
9555 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9557 if (!strEqual(curr_music, next_music))
9561 void FadeMenuSoundsAndMusic(void)
9567 void PlaySoundActivating(void)
9570 PlaySound(SND_MENU_ITEM_ACTIVATING);
9574 void PlaySoundSelecting(void)
9577 PlaySound(SND_MENU_ITEM_SELECTING);
9581 void ToggleFullscreenIfNeeded(void)
9583 // if setup and video fullscreen state are already matching, nothing do do
9584 if (setup.fullscreen == video.fullscreen_enabled ||
9585 !video.fullscreen_available)
9588 SDLSetWindowFullscreen(setup.fullscreen);
9590 // set setup value according to successfully changed fullscreen mode
9591 setup.fullscreen = video.fullscreen_enabled;
9594 void ChangeWindowScalingIfNeeded(void)
9596 // if setup and video window scaling are already matching, nothing do do
9597 if (setup.window_scaling_percent == video.window_scaling_percent ||
9598 video.fullscreen_enabled)
9601 SDLSetWindowScaling(setup.window_scaling_percent);
9603 // set setup value according to successfully changed window scaling
9604 setup.window_scaling_percent = video.window_scaling_percent;
9607 void ChangeVsyncModeIfNeeded(void)
9609 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9610 int video_vsync_mode = video.vsync_mode;
9612 // if setup and video vsync mode are already matching, nothing do do
9613 if (setup_vsync_mode == video_vsync_mode)
9616 // if renderer is using OpenGL, vsync mode can directly be changed
9617 SDLSetScreenVsyncMode(setup.vsync_mode);
9619 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9620 if (video.vsync_mode == video_vsync_mode)
9622 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9624 // save backbuffer content which gets lost when re-creating screen
9625 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9627 // force re-creating screen and renderer to set new vsync mode
9628 video.fullscreen_enabled = !setup.fullscreen;
9630 // when creating new renderer, destroy textures linked to old renderer
9631 FreeAllImageTextures(); // needs old renderer to free the textures
9633 // re-create screen and renderer (including change of vsync mode)
9634 ChangeVideoModeIfNeeded(setup.fullscreen);
9636 // set setup value according to successfully changed fullscreen mode
9637 setup.fullscreen = video.fullscreen_enabled;
9639 // restore backbuffer content from temporary backbuffer backup bitmap
9640 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9641 FreeBitmap(tmp_backbuffer);
9643 // update visible window/screen
9644 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9646 // when changing vsync mode, re-create textures for new renderer
9647 InitImageTextures();
9650 // set setup value according to successfully changed vsync mode
9651 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9654 static void JoinRectangles(int *x, int *y, int *width, int *height,
9655 int x2, int y2, int width2, int height2)
9657 // do not join with "off-screen" rectangle
9658 if (x2 == -1 || y2 == -1)
9663 *width = MAX(*width, width2);
9664 *height = MAX(*height, height2);
9667 void SetAnimStatus(int anim_status_new)
9669 if (anim_status_new == GAME_MODE_MAIN)
9670 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9671 else if (anim_status_new == GAME_MODE_NAMES)
9672 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9673 else if (anim_status_new == GAME_MODE_SCORES)
9674 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9676 global.anim_status_next = anim_status_new;
9678 // directly set screen modes that are entered without fading
9679 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9680 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9681 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9682 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9683 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9684 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9685 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9686 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9687 global.anim_status = global.anim_status_next;
9690 void SetGameStatus(int game_status_new)
9692 if (game_status_new != game_status)
9693 game_status_last_screen = game_status;
9695 game_status = game_status_new;
9697 SetAnimStatus(game_status_new);
9700 void SetFontStatus(int game_status_new)
9702 static int last_game_status = -1;
9704 if (game_status_new != -1)
9706 // set game status for font use after storing last game status
9707 last_game_status = game_status;
9708 game_status = game_status_new;
9712 // reset game status after font use from last stored game status
9713 game_status = last_game_status;
9717 void ResetFontStatus(void)
9722 void SetLevelSetInfo(char *identifier, int level_nr)
9724 setString(&levelset.identifier, identifier);
9726 levelset.level_nr = level_nr;
9729 boolean CheckIfAllViewportsHaveChanged(void)
9731 // if game status has not changed, viewports have not changed either
9732 if (game_status == game_status_last)
9735 // check if all viewports have changed with current game status
9737 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9738 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9739 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9740 int new_real_sx = vp_playfield->x;
9741 int new_real_sy = vp_playfield->y;
9742 int new_full_sxsize = vp_playfield->width;
9743 int new_full_sysize = vp_playfield->height;
9744 int new_dx = vp_door_1->x;
9745 int new_dy = vp_door_1->y;
9746 int new_dxsize = vp_door_1->width;
9747 int new_dysize = vp_door_1->height;
9748 int new_vx = vp_door_2->x;
9749 int new_vy = vp_door_2->y;
9750 int new_vxsize = vp_door_2->width;
9751 int new_vysize = vp_door_2->height;
9753 boolean playfield_viewport_has_changed =
9754 (new_real_sx != REAL_SX ||
9755 new_real_sy != REAL_SY ||
9756 new_full_sxsize != FULL_SXSIZE ||
9757 new_full_sysize != FULL_SYSIZE);
9759 boolean door_1_viewport_has_changed =
9762 new_dxsize != DXSIZE ||
9763 new_dysize != DYSIZE);
9765 boolean door_2_viewport_has_changed =
9768 new_vxsize != VXSIZE ||
9769 new_vysize != VYSIZE ||
9770 game_status_last == GAME_MODE_EDITOR);
9772 return (playfield_viewport_has_changed &&
9773 door_1_viewport_has_changed &&
9774 door_2_viewport_has_changed);
9777 boolean CheckFadeAll(void)
9779 return (CheckIfGlobalBorderHasChanged() ||
9780 CheckIfAllViewportsHaveChanged());
9783 void ChangeViewportPropertiesIfNeeded(void)
9785 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9786 FALSE : setup.small_game_graphics);
9787 int gfx_game_mode = getGlobalGameStatus(game_status);
9788 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9790 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9791 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9792 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9793 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9794 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9795 int new_win_xsize = vp_window->width;
9796 int new_win_ysize = vp_window->height;
9797 int border_left = vp_playfield->border_left;
9798 int border_right = vp_playfield->border_right;
9799 int border_top = vp_playfield->border_top;
9800 int border_bottom = vp_playfield->border_bottom;
9801 int new_sx = vp_playfield->x + border_left;
9802 int new_sy = vp_playfield->y + border_top;
9803 int new_sxsize = vp_playfield->width - border_left - border_right;
9804 int new_sysize = vp_playfield->height - border_top - border_bottom;
9805 int new_real_sx = vp_playfield->x;
9806 int new_real_sy = vp_playfield->y;
9807 int new_full_sxsize = vp_playfield->width;
9808 int new_full_sysize = vp_playfield->height;
9809 int new_dx = vp_door_1->x;
9810 int new_dy = vp_door_1->y;
9811 int new_dxsize = vp_door_1->width;
9812 int new_dysize = vp_door_1->height;
9813 int new_vx = vp_door_2->x;
9814 int new_vy = vp_door_2->y;
9815 int new_vxsize = vp_door_2->width;
9816 int new_vysize = vp_door_2->height;
9817 int new_ex = vp_door_3->x;
9818 int new_ey = vp_door_3->y;
9819 int new_exsize = vp_door_3->width;
9820 int new_eysize = vp_door_3->height;
9821 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9822 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9823 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9824 int new_scr_fieldx = new_sxsize / tilesize;
9825 int new_scr_fieldy = new_sysize / tilesize;
9826 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9827 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9828 boolean init_gfx_buffers = FALSE;
9829 boolean init_video_buffer = FALSE;
9830 boolean init_gadgets_and_anims = FALSE;
9831 boolean init_em_graphics = FALSE;
9833 if (new_win_xsize != WIN_XSIZE ||
9834 new_win_ysize != WIN_YSIZE)
9836 WIN_XSIZE = new_win_xsize;
9837 WIN_YSIZE = new_win_ysize;
9839 init_video_buffer = TRUE;
9840 init_gfx_buffers = TRUE;
9841 init_gadgets_and_anims = TRUE;
9843 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9846 if (new_scr_fieldx != SCR_FIELDX ||
9847 new_scr_fieldy != SCR_FIELDY)
9849 // this always toggles between MAIN and GAME when using small tile size
9851 SCR_FIELDX = new_scr_fieldx;
9852 SCR_FIELDY = new_scr_fieldy;
9854 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9865 new_sxsize != SXSIZE ||
9866 new_sysize != SYSIZE ||
9867 new_dxsize != DXSIZE ||
9868 new_dysize != DYSIZE ||
9869 new_vxsize != VXSIZE ||
9870 new_vysize != VYSIZE ||
9871 new_exsize != EXSIZE ||
9872 new_eysize != EYSIZE ||
9873 new_real_sx != REAL_SX ||
9874 new_real_sy != REAL_SY ||
9875 new_full_sxsize != FULL_SXSIZE ||
9876 new_full_sysize != FULL_SYSIZE ||
9877 new_tilesize_var != TILESIZE_VAR
9880 // ------------------------------------------------------------------------
9881 // determine next fading area for changed viewport definitions
9882 // ------------------------------------------------------------------------
9884 // start with current playfield area (default fading area)
9887 FADE_SXSIZE = FULL_SXSIZE;
9888 FADE_SYSIZE = FULL_SYSIZE;
9890 // add new playfield area if position or size has changed
9891 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9892 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9894 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9895 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9898 // add current and new door 1 area if position or size has changed
9899 if (new_dx != DX || new_dy != DY ||
9900 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9902 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9903 DX, DY, DXSIZE, DYSIZE);
9904 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9905 new_dx, new_dy, new_dxsize, new_dysize);
9908 // add current and new door 2 area if position or size has changed
9909 if (new_vx != VX || new_vy != VY ||
9910 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9912 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9913 VX, VY, VXSIZE, VYSIZE);
9914 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9915 new_vx, new_vy, new_vxsize, new_vysize);
9918 // ------------------------------------------------------------------------
9919 // handle changed tile size
9920 // ------------------------------------------------------------------------
9922 if (new_tilesize_var != TILESIZE_VAR)
9924 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9926 // changing tile size invalidates scroll values of engine snapshots
9927 FreeEngineSnapshotSingle();
9929 // changing tile size requires update of graphic mapping for EM engine
9930 init_em_graphics = TRUE;
9941 SXSIZE = new_sxsize;
9942 SYSIZE = new_sysize;
9943 DXSIZE = new_dxsize;
9944 DYSIZE = new_dysize;
9945 VXSIZE = new_vxsize;
9946 VYSIZE = new_vysize;
9947 EXSIZE = new_exsize;
9948 EYSIZE = new_eysize;
9949 REAL_SX = new_real_sx;
9950 REAL_SY = new_real_sy;
9951 FULL_SXSIZE = new_full_sxsize;
9952 FULL_SYSIZE = new_full_sysize;
9953 TILESIZE_VAR = new_tilesize_var;
9955 init_gfx_buffers = TRUE;
9956 init_gadgets_and_anims = TRUE;
9958 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9959 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9962 if (init_gfx_buffers)
9964 // Debug("tools:viewport", "init_gfx_buffers");
9966 SCR_FIELDX = new_scr_fieldx_buffers;
9967 SCR_FIELDY = new_scr_fieldy_buffers;
9971 SCR_FIELDX = new_scr_fieldx;
9972 SCR_FIELDY = new_scr_fieldy;
9974 SetDrawDeactivationMask(REDRAW_NONE);
9975 SetDrawBackgroundMask(REDRAW_FIELD);
9978 if (init_video_buffer)
9980 // Debug("tools:viewport", "init_video_buffer");
9982 FreeAllImageTextures(); // needs old renderer to free the textures
9984 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9985 InitImageTextures();
9988 if (init_gadgets_and_anims)
9990 // Debug("tools:viewport", "init_gadgets_and_anims");
9993 InitGlobalAnimations();
9996 if (init_em_graphics)
9998 InitGraphicInfo_EM();
10002 void OpenURL(char *url)
10004 #if SDL_VERSION_ATLEAST(2,0,14)
10007 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
10008 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
10009 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
10013 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
10015 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
10019 // ============================================================================
10021 // ============================================================================
10023 #if defined(PLATFORM_WINDOWS)
10024 /* FILETIME of Jan 1 1970 00:00:00. */
10025 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
10028 * timezone information is stored outside the kernel so tzp isn't used anymore.
10030 * Note: this function is not for Win32 high precision timing purpose. See
10033 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
10035 FILETIME file_time;
10036 SYSTEMTIME system_time;
10037 ULARGE_INTEGER ularge;
10039 GetSystemTime(&system_time);
10040 SystemTimeToFileTime(&system_time, &file_time);
10041 ularge.LowPart = file_time.dwLowDateTime;
10042 ularge.HighPart = file_time.dwHighDateTime;
10044 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10045 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10051 static char *test_init_uuid_random_function_simple(void)
10053 static char seed_text[100];
10054 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10056 sprintf(seed_text, "%d", seed);
10061 static char *test_init_uuid_random_function_better(void)
10063 static char seed_text[100];
10064 struct timeval current_time;
10066 gettimeofday(¤t_time, NULL);
10068 prng_seed_bytes(¤t_time, sizeof(current_time));
10070 sprintf(seed_text, "%ld.%ld",
10071 (long)current_time.tv_sec,
10072 (long)current_time.tv_usec);
10077 #if defined(PLATFORM_WINDOWS)
10078 static char *test_init_uuid_random_function_better_windows(void)
10080 static char seed_text[100];
10081 struct timeval current_time;
10083 gettimeofday_windows(¤t_time, NULL);
10085 prng_seed_bytes(¤t_time, sizeof(current_time));
10087 sprintf(seed_text, "%ld.%ld",
10088 (long)current_time.tv_sec,
10089 (long)current_time.tv_usec);
10095 static unsigned int test_uuid_random_function_simple(int max)
10097 return GetSimpleRandom(max);
10100 static unsigned int test_uuid_random_function_better(int max)
10102 return (max > 0 ? prng_get_uint() % max : 0);
10105 #if defined(PLATFORM_WINDOWS)
10106 #define NUM_UUID_TESTS 3
10108 #define NUM_UUID_TESTS 2
10111 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10113 struct hashtable *hash_seeds =
10114 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10115 struct hashtable *hash_uuids =
10116 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10117 static char message[100];
10120 char *random_name = (nr == 0 ? "simple" : "better");
10121 char *random_type = (always_seed ? "always" : "only once");
10122 char *(*init_random_function)(void) =
10124 test_init_uuid_random_function_simple :
10125 test_init_uuid_random_function_better);
10126 unsigned int (*random_function)(int) =
10128 test_uuid_random_function_simple :
10129 test_uuid_random_function_better);
10132 #if defined(PLATFORM_WINDOWS)
10135 random_name = "windows";
10136 init_random_function = test_init_uuid_random_function_better_windows;
10142 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10143 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10145 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10146 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10147 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10149 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10153 // always initialize random number generator at least once
10154 init_random_function();
10156 unsigned int time_start = SDL_GetTicks();
10158 for (i = 0; i < num_uuids; i++)
10162 char *seed = getStringCopy(init_random_function());
10164 hashtable_remove(hash_seeds, seed);
10165 hashtable_insert(hash_seeds, seed, "1");
10168 char *uuid = getStringCopy(getUUIDExt(random_function));
10170 hashtable_remove(hash_uuids, uuid);
10171 hashtable_insert(hash_uuids, uuid, "1");
10174 int num_unique_seeds = hashtable_count(hash_seeds);
10175 int num_unique_uuids = hashtable_count(hash_uuids);
10177 unsigned int time_needed = SDL_GetTicks() - time_start;
10179 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10181 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10184 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10186 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10187 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10189 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10191 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10193 Request(message, REQ_CONFIRM);
10195 hashtable_destroy(hash_seeds, 0);
10196 hashtable_destroy(hash_uuids, 0);
10199 void TestGeneratingUUIDs(void)
10201 int num_uuids = 1000000;
10204 for (i = 0; i < NUM_UUID_TESTS; i++)
10205 for (j = 0; j < 2; j++)
10206 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10208 CloseAllAndExit(0);