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 // always use global border for PLAYING when restarting the game
638 if (global.border_status == GAME_MODE_PSEUDO_RESTARTING)
639 global.border_status = GAME_MODE_PLAYING;
641 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
643 global.border_status = last_border_status;
644 gfx.masked_border_bitmap_ptr = backbuffer;
648 void DrawTileCursor(int draw_target)
650 DrawTileCursor_MM(draw_target, game_status == GAME_MODE_PLAYING);
653 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
655 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
658 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
660 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
661 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
663 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
666 void BlitScreenToBitmap(Bitmap *target_bitmap)
668 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
669 BlitScreenToBitmap_EM(target_bitmap);
670 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
671 BlitScreenToBitmap_SP(target_bitmap);
672 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
673 BlitScreenToBitmap_MM(target_bitmap);
674 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
675 BlitScreenToBitmap_RND(target_bitmap);
677 redraw_mask |= REDRAW_FIELD;
680 static void DrawFramesPerSecond(void)
683 int font_nr = FONT_TEXT_2;
684 int font_width = getFontWidth(font_nr);
685 int draw_deactivation_mask = GetDrawDeactivationMask();
686 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
688 // draw FPS with leading space (needed if field buffer deactivated)
689 sprintf(text, " %04.1f fps", global.frames_per_second);
691 // override draw deactivation mask (required for invisible warp mode)
692 SetDrawDeactivationMask(REDRAW_NONE);
694 // draw opaque FPS if field buffer deactivated, else draw masked FPS
695 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
696 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
698 // set draw deactivation mask to previous value
699 SetDrawDeactivationMask(draw_deactivation_mask);
701 // force full-screen redraw in this frame
702 redraw_mask = REDRAW_ALL;
706 static void PrintFrameTimeDebugging(void)
708 static unsigned int last_counter = 0;
709 unsigned int counter = Counter();
710 int diff_1 = counter - last_counter;
711 int diff_2 = diff_1 - GAME_FRAME_DELAY;
713 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
714 char diff_bar[2 * diff_2_max + 5];
718 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
720 for (i = 0; i < diff_2_max; i++)
721 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
722 i >= diff_2_max - diff_2_cut ? '-' : ' ');
724 diff_bar[pos++] = '|';
726 for (i = 0; i < diff_2_max; i++)
727 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
729 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
731 diff_bar[pos++] = '\0';
733 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
736 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
739 last_counter = counter;
743 static int unifiedRedrawMask(int mask)
745 if (mask & REDRAW_ALL)
748 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
754 static boolean equalRedrawMasks(int mask_1, int mask_2)
756 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
759 void BackToFront(void)
761 static int last_redraw_mask = REDRAW_NONE;
763 // force screen redraw in every frame to continue drawing global animations
764 // (but always use the last redraw mask to prevent unwanted side effects)
765 if (redraw_mask == REDRAW_NONE)
766 redraw_mask = last_redraw_mask;
768 last_redraw_mask = redraw_mask;
771 // masked border now drawn immediately when blitting backbuffer to window
773 // draw masked border to all viewports, if defined
774 DrawMaskedBorder(redraw_mask);
777 // draw frames per second (only if debug mode is enabled)
778 if (redraw_mask & REDRAW_FPS)
779 DrawFramesPerSecond();
781 // remove playfield redraw before potentially merging with doors redraw
782 if (DrawingDeactivated(REAL_SX, REAL_SY))
783 redraw_mask &= ~REDRAW_FIELD;
785 // redraw complete window if both playfield and (some) doors need redraw
786 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
787 redraw_mask = REDRAW_ALL;
789 /* although redrawing the whole window would be fine for normal gameplay,
790 being able to only redraw the playfield is required for deactivating
791 certain drawing areas (mainly playfield) to work, which is needed for
792 warp-forward to be fast enough (by skipping redraw of most frames) */
794 if (redraw_mask & REDRAW_ALL)
796 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
798 else if (redraw_mask & REDRAW_FIELD)
800 BlitBitmap(backbuffer, window,
801 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
803 else if (redraw_mask & REDRAW_DOORS)
805 // merge door areas to prevent calling screen redraw more than once
811 if (redraw_mask & REDRAW_DOOR_1)
815 x2 = MAX(x2, DX + DXSIZE);
816 y2 = MAX(y2, DY + DYSIZE);
819 if (redraw_mask & REDRAW_DOOR_2)
823 x2 = MAX(x2, VX + VXSIZE);
824 y2 = MAX(y2, VY + VYSIZE);
827 if (redraw_mask & REDRAW_DOOR_3)
831 x2 = MAX(x2, EX + EXSIZE);
832 y2 = MAX(y2, EY + EYSIZE);
835 // make sure that at least one pixel is blitted, and inside the screen
836 // (else nothing is blitted, causing the animations not to be updated)
837 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
838 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
839 x2 = MIN(MAX(1, x2), WIN_XSIZE);
840 y2 = MIN(MAX(1, y2), WIN_YSIZE);
842 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
845 redraw_mask = REDRAW_NONE;
848 PrintFrameTimeDebugging();
852 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
854 unsigned int frame_delay_value_old = GetVideoFrameDelay();
856 SetVideoFrameDelay(frame_delay_value);
860 SetVideoFrameDelay(frame_delay_value_old);
863 static int fade_type_skip = FADE_TYPE_NONE;
865 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
867 void (*draw_border_function)(void) = NULL;
868 int x, y, width, height;
869 int fade_delay, post_delay;
871 if (fade_type == FADE_TYPE_FADE_OUT)
873 if (fade_type_skip != FADE_TYPE_NONE)
875 // skip all fade operations until specified fade operation
876 if (fade_type & fade_type_skip)
877 fade_type_skip = FADE_TYPE_NONE;
882 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
886 redraw_mask |= fade_mask;
888 if (fade_type == FADE_TYPE_SKIP)
890 fade_type_skip = fade_mode;
895 fade_delay = fading.fade_delay;
896 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
898 if (fade_type_skip != FADE_TYPE_NONE)
900 // skip all fade operations until specified fade operation
901 if (fade_type & fade_type_skip)
902 fade_type_skip = FADE_TYPE_NONE;
907 if (global.autoplay_leveldir)
912 if (fade_mask == REDRAW_FIELD)
917 height = FADE_SYSIZE;
919 if (border.draw_masked_when_fading)
920 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
922 DrawMaskedBorder_FIELD(); // draw once
932 // when switching screens without fading, set fade delay to zero
933 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
936 // do not display black frame when fading out without fade delay
937 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
940 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
941 draw_border_function);
943 redraw_mask &= ~fade_mask;
945 ClearAutoRepeatKeyEvents();
948 static void SetScreenStates_BeforeFadingIn(void)
950 // temporarily set screen mode for animations to screen after fading in
951 global.anim_status = global.anim_status_next;
953 // store backbuffer with all animations that will be started after fading in
954 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
956 // set screen mode for animations back to fading
957 global.anim_status = GAME_MODE_PSEUDO_FADING;
960 static void SetScreenStates_AfterFadingIn(void)
962 // store new source screen (to use correct masked border for fading)
963 gfx.fade_border_source_status = global.border_status;
965 global.anim_status = global.anim_status_next;
968 static void SetScreenStates_BeforeFadingOut(void)
970 // store new target screen (to use correct masked border for fading)
971 gfx.fade_border_target_status = game_status;
973 // set screen mode for animations to fading
974 global.anim_status = GAME_MODE_PSEUDO_FADING;
976 // store backbuffer with all animations that will be stopped for fading out
977 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
980 static void SetScreenStates_AfterFadingOut(void)
982 global.border_status = game_status;
984 // always use global border for PLAYING when restarting the game
985 if (global.border_status == GAME_MODE_PSEUDO_RESTARTING)
986 global.border_status = GAME_MODE_PLAYING;
989 void FadeIn(int fade_mask)
991 SetScreenStates_BeforeFadingIn();
994 DrawMaskedBorder(REDRAW_ALL);
997 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
998 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1000 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1004 FADE_SXSIZE = FULL_SXSIZE;
1005 FADE_SYSIZE = FULL_SYSIZE;
1007 // activate virtual buttons depending on upcoming game status
1008 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1009 game_status == GAME_MODE_PLAYING && !tape.playing)
1010 SetOverlayActive(TRUE);
1012 SetScreenStates_AfterFadingIn();
1014 // force update of global animation status in case of rapid screen changes
1015 redraw_mask = REDRAW_ALL;
1019 void FadeOut(int fade_mask)
1021 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1022 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1023 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1026 SetScreenStates_BeforeFadingOut();
1028 SetTileCursorActive(FALSE);
1029 SetOverlayActive(FALSE);
1032 DrawMaskedBorder(REDRAW_ALL);
1035 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1036 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1038 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1040 SetScreenStates_AfterFadingOut();
1043 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1045 static struct TitleFadingInfo fading_leave_stored;
1048 fading_leave_stored = fading_leave;
1050 fading = fading_leave_stored;
1053 void FadeSetEnterMenu(void)
1055 fading = menu.enter_menu;
1057 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1060 void FadeSetLeaveMenu(void)
1062 fading = menu.leave_menu;
1064 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1067 void FadeSetEnterScreen(void)
1069 fading = menu.enter_screen[game_status];
1071 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1074 void FadeSetNextScreen(void)
1076 fading = menu.next_screen[game_status];
1078 // (do not overwrite fade mode set by FadeSetEnterScreen)
1079 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1082 void FadeSetLeaveScreen(void)
1084 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1087 void FadeSetFromType(int type)
1089 if (type & TYPE_ENTER_SCREEN)
1090 FadeSetEnterScreen();
1091 else if (type & TYPE_ENTER)
1093 else if (type & TYPE_LEAVE)
1097 void FadeSetDisabled(void)
1099 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1101 fading = fading_none;
1104 void FadeSkipNextFadeIn(void)
1106 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1109 void FadeSkipNextFadeOut(void)
1111 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1114 static int getGlobalGameStatus(int status)
1116 return (status == GAME_MODE_PSEUDO_TYPENAME ? GAME_MODE_MAIN :
1117 status == GAME_MODE_SCOREINFO ? GAME_MODE_SCORES :
1121 int getImageFromGraphicOrDefault(int graphic, int default_graphic)
1123 if (graphic == IMG_UNDEFINED)
1124 return IMG_UNDEFINED;
1126 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1128 return (graphic_info[graphic].bitmap != NULL || redefined ?
1129 graphic : default_graphic);
1132 static int getBackgroundImage(int graphic)
1134 return getImageFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1137 static int getGlobalBorderImage(int graphic)
1139 return getImageFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1142 Bitmap *getGlobalBorderBitmapFromStatus(int status_raw)
1144 int status = getGlobalGameStatus(status_raw);
1146 (status == GAME_MODE_MAIN ? IMG_GLOBAL_BORDER_MAIN :
1147 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1148 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1149 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1151 int graphic_final = getGlobalBorderImage(graphic);
1153 return graphic_info[graphic_final].bitmap;
1156 void SetBackgroundImage(int graphic, int redraw_mask)
1158 struct GraphicInfo *g = &graphic_info[graphic];
1159 struct GraphicInfo g_undefined = { 0 };
1161 if (graphic == IMG_UNDEFINED)
1164 // always use original size bitmap for backgrounds, if existing
1165 Bitmap *bitmap = (g->bitmaps != NULL &&
1166 g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] != NULL ?
1167 g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] : g->bitmap);
1169 // remove every mask before setting mask for window, and
1170 // remove window area mask before setting mask for main or door area
1171 int remove_mask = (redraw_mask == REDRAW_ALL ? 0xffff : REDRAW_ALL);
1173 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
1174 SetBackgroundBitmap(NULL, remove_mask, 0, 0, 0, 0); // !!! FIX THIS !!!
1175 SetBackgroundBitmap(bitmap, redraw_mask,
1177 g->width, g->height);
1180 void SetWindowBackgroundImageIfDefined(int graphic)
1182 if (graphic_info[graphic].bitmap)
1183 SetBackgroundImage(graphic, REDRAW_ALL);
1186 void SetMainBackgroundImageIfDefined(int graphic)
1188 if (graphic_info[graphic].bitmap)
1189 SetBackgroundImage(graphic, REDRAW_FIELD);
1192 void SetDoorBackgroundImageIfDefined(int graphic)
1194 if (graphic_info[graphic].bitmap)
1195 SetBackgroundImage(graphic, REDRAW_DOOR_1);
1198 void SetWindowBackgroundImage(int graphic)
1200 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_ALL);
1203 void SetMainBackgroundImage(int graphic)
1205 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_FIELD);
1208 void SetDoorBackgroundImage(int graphic)
1210 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_DOOR_1);
1213 void SetPanelBackground(void)
1215 SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
1218 void DrawBackground(int x, int y, int width, int height)
1220 // "drawto" might still point to playfield buffer here (hall of fame)
1221 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1223 if (IN_GFX_FIELD_FULL(x, y))
1224 redraw_mask |= REDRAW_FIELD;
1225 else if (IN_GFX_DOOR_1(x, y))
1226 redraw_mask |= REDRAW_DOOR_1;
1227 else if (IN_GFX_DOOR_2(x, y))
1228 redraw_mask |= REDRAW_DOOR_2;
1229 else if (IN_GFX_DOOR_3(x, y))
1230 redraw_mask |= REDRAW_DOOR_3;
1233 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1235 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1237 if (font->bitmap == NULL)
1240 DrawBackground(x, y, width, height);
1243 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1245 struct GraphicInfo *g = &graphic_info[graphic];
1247 if (g->bitmap == NULL)
1250 DrawBackground(x, y, width, height);
1253 static int game_status_last = -1;
1254 static Bitmap *global_border_bitmap_last = NULL;
1255 static Bitmap *global_border_bitmap = NULL;
1256 static int real_sx_last = -1, real_sy_last = -1;
1257 static int full_sxsize_last = -1, full_sysize_last = -1;
1258 static int dx_last = -1, dy_last = -1;
1259 static int dxsize_last = -1, dysize_last = -1;
1260 static int vx_last = -1, vy_last = -1;
1261 static int vxsize_last = -1, vysize_last = -1;
1262 static int ex_last = -1, ey_last = -1;
1263 static int exsize_last = -1, eysize_last = -1;
1265 boolean CheckIfGlobalBorderHasChanged(void)
1267 // if game status has not changed, global border has not changed either
1268 if (game_status == game_status_last)
1271 // determine and store new global border bitmap for current game status
1272 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1274 return (global_border_bitmap_last != global_border_bitmap);
1277 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1279 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1280 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1282 // if game status has not changed, nothing has to be redrawn
1283 if (game_status == game_status_last)
1286 // redraw if last screen was title screen
1287 if (game_status_last == GAME_MODE_TITLE)
1290 // redraw if global screen border has changed
1291 if (CheckIfGlobalBorderHasChanged())
1294 // redraw if position or size of playfield area has changed
1295 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1296 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1299 // redraw if position or size of door area has changed
1300 if (dx_last != DX || dy_last != DY ||
1301 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1304 // redraw if position or size of tape area has changed
1305 if (vx_last != VX || vy_last != VY ||
1306 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1309 // redraw if position or size of editor area has changed
1310 if (ex_last != EX || ey_last != EY ||
1311 exsize_last != EXSIZE || eysize_last != EYSIZE)
1318 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1321 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1323 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1326 void RedrawGlobalBorder(void)
1328 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1330 RedrawGlobalBorderFromBitmap(bitmap);
1332 redraw_mask = REDRAW_ALL;
1335 static void RedrawGlobalBorderIfNeeded(void)
1337 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1338 if (game_status == game_status_last)
1342 // copy current draw buffer to later copy back areas that have not changed
1343 if (game_status_last != GAME_MODE_TITLE)
1344 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1346 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1347 if (CheckIfGlobalBorderRedrawIsNeeded())
1349 // determine and store new global border bitmap for current game status
1350 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1353 // redraw global screen border (or clear, if defined to be empty)
1354 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1356 if (game_status == GAME_MODE_EDITOR)
1357 DrawSpecialEditorDoor();
1359 // copy previous playfield and door areas, if they are defined on both
1360 // previous and current screen and if they still have the same size
1362 if (real_sx_last != -1 && real_sy_last != -1 &&
1363 REAL_SX != -1 && REAL_SY != -1 &&
1364 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1365 BlitBitmap(bitmap_db_store_1, backbuffer,
1366 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1369 if (dx_last != -1 && dy_last != -1 &&
1370 DX != -1 && DY != -1 &&
1371 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1372 BlitBitmap(bitmap_db_store_1, backbuffer,
1373 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1375 if (game_status != GAME_MODE_EDITOR)
1377 if (vx_last != -1 && vy_last != -1 &&
1378 VX != -1 && VY != -1 &&
1379 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1380 BlitBitmap(bitmap_db_store_1, backbuffer,
1381 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1385 if (ex_last != -1 && ey_last != -1 &&
1386 EX != -1 && EY != -1 &&
1387 exsize_last == EXSIZE && eysize_last == EYSIZE)
1388 BlitBitmap(bitmap_db_store_1, backbuffer,
1389 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1392 redraw_mask = REDRAW_ALL;
1395 game_status_last = game_status;
1397 global_border_bitmap_last = global_border_bitmap;
1399 real_sx_last = REAL_SX;
1400 real_sy_last = REAL_SY;
1401 full_sxsize_last = FULL_SXSIZE;
1402 full_sysize_last = FULL_SYSIZE;
1405 dxsize_last = DXSIZE;
1406 dysize_last = DYSIZE;
1409 vxsize_last = VXSIZE;
1410 vysize_last = VYSIZE;
1413 exsize_last = EXSIZE;
1414 eysize_last = EYSIZE;
1417 void ClearField(void)
1419 RedrawGlobalBorderIfNeeded();
1421 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1422 // (when entering hall of fame after playing)
1423 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1425 // !!! maybe this should be done before clearing the background !!!
1426 if (game_status == GAME_MODE_PLAYING)
1428 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1429 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1433 SetDrawtoField(DRAW_TO_BACKBUFFER);
1437 void MarkTileDirty(int x, int y)
1439 redraw_mask |= REDRAW_FIELD;
1442 void SetBorderElement(void)
1446 BorderElement = EL_EMPTY;
1448 // only the R'n'D game engine may use an additional steelwall border
1449 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1452 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1454 for (x = 0; x < lev_fieldx; x++)
1456 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1457 BorderElement = EL_STEELWALL;
1459 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1465 void FloodFillLevelExt(int start_x, int start_y, int fill_element,
1466 int max_array_fieldx, int max_array_fieldy,
1467 short field[max_array_fieldx][max_array_fieldy],
1468 int max_fieldx, int max_fieldy)
1470 static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY];
1471 struct XY *check = xy_topdown;
1472 int old_element = field[start_x][start_y];
1475 // do nothing if start field already has the desired content
1476 if (old_element == fill_element)
1479 stack_buffer[stack_pos++] = (struct XY){ start_x, start_y };
1481 while (stack_pos > 0)
1483 struct XY current = stack_buffer[--stack_pos];
1486 field[current.x][current.y] = fill_element;
1488 for (i = 0; i < 4; i++)
1490 int x = current.x + check[i].x;
1491 int y = current.y + check[i].y;
1493 // check for stack buffer overflow (should not happen)
1494 if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY)
1495 Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug.");
1497 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1498 stack_buffer[stack_pos++] = (struct XY){ x, y };
1503 void FloodFillLevel(int from_x, int from_y, int fill_element,
1504 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1505 int max_fieldx, int max_fieldy)
1507 FloodFillLevelExt(from_x, from_y, fill_element,
1508 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1509 max_fieldx, max_fieldy);
1512 void SetRandomAnimationValue(int x, int y)
1514 gfx.anim_random_frame = GfxRandom[x][y];
1517 int getGraphicAnimationFrame(int graphic, int sync_frame)
1519 // animation synchronized with global frame counter, not move position
1520 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1521 sync_frame = FrameCounter;
1522 else if (graphic_info[graphic].anim_global_anim_sync)
1523 sync_frame = getGlobalAnimSyncFrame();
1525 return getAnimationFrame(graphic_info[graphic].anim_frames,
1526 graphic_info[graphic].anim_delay,
1527 graphic_info[graphic].anim_mode,
1528 graphic_info[graphic].anim_start_frame,
1532 int getGraphicAnimationFrameXY(int graphic, int lx, int ly)
1534 if (graphic_info[graphic].anim_mode & ANIM_TILED)
1536 struct GraphicInfo *g = &graphic_info[graphic];
1537 int xsize = MAX(1, g->anim_frames_per_line);
1538 int ysize = MAX(1, g->anim_frames / xsize);
1539 int xoffset = g->anim_start_frame % xsize;
1540 int yoffset = g->anim_start_frame % ysize;
1541 // may be needed if screen field is significantly larger than playfield
1542 int x = (lx + xoffset + SCR_FIELDX * xsize) % xsize;
1543 int y = (ly + yoffset + SCR_FIELDY * ysize) % ysize;
1544 int sync_frame = y * xsize + x;
1546 return sync_frame % g->anim_frames;
1548 else if (graphic_info[graphic].anim_mode & ANIM_RANDOM_STATIC)
1550 struct GraphicInfo *g = &graphic_info[graphic];
1551 // may be needed if screen field is significantly larger than playfield
1552 int x = (lx + SCR_FIELDX * lev_fieldx) % lev_fieldx;
1553 int y = (ly + SCR_FIELDY * lev_fieldy) % lev_fieldy;
1554 int sync_frame = GfxRandomStatic[x][y];
1556 return sync_frame % g->anim_frames;
1560 int sync_frame = (IN_LEV_FIELD(lx, ly) ? GfxFrame[lx][ly] : -1);
1562 return getGraphicAnimationFrame(graphic, sync_frame);
1566 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1568 struct GraphicInfo *g = &graphic_info[graphic];
1569 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1571 if (tilesize == gfx.standard_tile_size)
1572 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1573 else if (tilesize == game.tile_size)
1574 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1576 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1579 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1580 boolean get_backside)
1582 struct GraphicInfo *g = &graphic_info[graphic];
1583 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1584 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1586 if (g->offset_y == 0) // frames are ordered horizontally
1588 int max_width = g->anim_frames_per_line * g->width;
1589 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1591 *x = pos % max_width;
1592 *y = src_y % g->height + pos / max_width * g->height;
1594 else if (g->offset_x == 0) // frames are ordered vertically
1596 int max_height = g->anim_frames_per_line * g->height;
1597 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1599 *x = src_x % g->width + pos / max_height * g->width;
1600 *y = pos % max_height;
1602 else // frames are ordered diagonally
1604 *x = src_x + frame * g->offset_x;
1605 *y = src_y + frame * g->offset_y;
1609 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1610 Bitmap **bitmap, int *x, int *y,
1611 boolean get_backside)
1613 struct GraphicInfo *g = &graphic_info[graphic];
1615 // if no graphics defined at all, use fallback graphics
1616 if (g->bitmaps == NULL)
1617 *g = graphic_info[IMG_CHAR_EXCLAM];
1619 // if no in-game graphics defined, always use standard graphic size
1620 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1621 tilesize = TILESIZE;
1623 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1624 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1626 *x = *x * tilesize / g->tile_size;
1627 *y = *y * tilesize / g->tile_size;
1630 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1631 Bitmap **bitmap, int *x, int *y)
1633 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1636 void getFixedGraphicSource(int graphic, int frame,
1637 Bitmap **bitmap, int *x, int *y)
1639 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1642 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1644 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1647 void getGlobalAnimGraphicSource(int graphic, int frame,
1648 Bitmap **bitmap, int *x, int *y)
1650 struct GraphicInfo *g = &graphic_info[graphic];
1652 // if no graphics defined at all, use fallback graphics
1653 if (g->bitmaps == NULL)
1654 *g = graphic_info[IMG_CHAR_EXCLAM];
1656 // use original size graphics, if existing, else use standard size graphics
1657 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1658 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1660 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1662 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1665 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1666 int *x, int *y, boolean get_backside)
1668 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1672 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1674 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1677 void DrawGraphic(int x, int y, int graphic, int frame)
1680 if (!IN_SCR_FIELD(x, y))
1682 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1683 Debug("draw:DrawGraphic", "This should never happen!");
1689 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1692 MarkTileDirty(x, y);
1695 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1698 if (!IN_SCR_FIELD(x, y))
1700 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1702 Debug("draw:DrawFixedGraphic", "This should never happen!");
1708 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1710 MarkTileDirty(x, y);
1713 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1719 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1721 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1724 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1730 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1731 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1734 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1737 if (!IN_SCR_FIELD(x, y))
1739 Debug("draw:DrawGraphicThruMask", "x = %d, y = %d, graphic = %d",
1741 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1747 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1750 MarkTileDirty(x, y);
1753 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1756 if (!IN_SCR_FIELD(x, y))
1758 Debug("draw:DrawFixedGraphicThruMask", "x = %d, y = %d, graphic = %d",
1760 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1766 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1768 MarkTileDirty(x, y);
1771 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1777 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1779 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1783 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1784 int graphic, int frame)
1789 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1791 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1795 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1797 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1799 MarkTileDirty(x / tilesize, y / tilesize);
1802 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1805 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1806 graphic, frame, tilesize);
1807 MarkTileDirty(x / tilesize, y / tilesize);
1810 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1816 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1817 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1820 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1821 int frame, int tilesize)
1826 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1827 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1830 void DrawMiniGraphic(int x, int y, int graphic)
1832 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX, SY + y * MINI_TILEY, graphic);
1833 MarkTileDirty(x / 2, y / 2);
1836 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1841 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1842 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1845 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1846 int graphic, int frame,
1847 int cut_mode, int mask_mode)
1852 int width = TILEX, height = TILEY;
1855 if (dx || dy) // shifted graphic
1857 if (x < BX1) // object enters playfield from the left
1864 else if (x > BX2) // object enters playfield from the right
1870 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1876 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1878 else if (dx) // general horizontal movement
1879 MarkTileDirty(x + SIGN(dx), y);
1881 if (y < BY1) // object enters playfield from the top
1883 if (cut_mode == CUT_BELOW) // object completely above top border
1891 else if (y > BY2) // object enters playfield from the bottom
1897 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1903 else if (dy > 0 && cut_mode == CUT_ABOVE)
1905 if (y == BY2) // object completely above bottom border
1911 MarkTileDirty(x, y + 1);
1912 } // object leaves playfield to the bottom
1913 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1915 else if (dy) // general vertical movement
1916 MarkTileDirty(x, y + SIGN(dy));
1920 if (!IN_SCR_FIELD(x, y))
1922 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1924 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1930 width = width * TILESIZE_VAR / TILESIZE;
1931 height = height * TILESIZE_VAR / TILESIZE;
1932 cx = cx * TILESIZE_VAR / TILESIZE;
1933 cy = cy * TILESIZE_VAR / TILESIZE;
1934 dx = dx * TILESIZE_VAR / TILESIZE;
1935 dy = dy * TILESIZE_VAR / TILESIZE;
1937 if (width > 0 && height > 0)
1939 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1944 dst_x = FX + x * TILEX_VAR + dx;
1945 dst_y = FY + y * TILEY_VAR + dy;
1947 if (mask_mode == USE_MASKING)
1948 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1951 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1954 MarkTileDirty(x, y);
1958 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1959 int graphic, int frame,
1960 int cut_mode, int mask_mode)
1965 int width = TILEX_VAR, height = TILEY_VAR;
1968 int x2 = x + SIGN(dx);
1969 int y2 = y + SIGN(dy);
1971 // movement with two-tile animations must be sync'ed with movement position,
1972 // not with current GfxFrame (which can be higher when using slow movement)
1973 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1974 int anim_frames = graphic_info[graphic].anim_frames;
1976 // (we also need anim_delay here for movement animations with less frames)
1977 int anim_delay = graphic_info[graphic].anim_delay;
1978 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1980 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1981 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1983 // re-calculate animation frame for two-tile movement animation
1984 frame = getGraphicAnimationFrame(graphic, sync_frame);
1986 // check if movement start graphic inside screen area and should be drawn
1987 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1989 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1991 dst_x = FX + x1 * TILEX_VAR;
1992 dst_y = FY + y1 * TILEY_VAR;
1994 if (mask_mode == USE_MASKING)
1995 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1998 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
2001 MarkTileDirty(x1, y1);
2004 // check if movement end graphic inside screen area and should be drawn
2005 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
2007 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
2009 dst_x = FX + x2 * TILEX_VAR;
2010 dst_y = FY + y2 * TILEY_VAR;
2012 if (mask_mode == USE_MASKING)
2013 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
2016 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
2019 MarkTileDirty(x2, y2);
2023 static void DrawGraphicShifted(int x, int y, int dx, int dy,
2024 int graphic, int frame,
2025 int cut_mode, int mask_mode)
2029 DrawGraphic(x, y, graphic, frame);
2034 if (graphic_info[graphic].double_movement) // EM style movement images
2035 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2037 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2040 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
2041 int graphic, int frame, int cut_mode)
2043 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
2046 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
2047 int cut_mode, int mask_mode)
2049 int lx = LEVELX(x), ly = LEVELY(y);
2053 if (IN_LEV_FIELD(lx, ly))
2055 if (element == EL_EMPTY)
2056 element = GfxElementEmpty[lx][ly];
2058 SetRandomAnimationValue(lx, ly);
2060 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2061 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2063 // do not use double (EM style) movement graphic when not moving
2064 if (graphic_info[graphic].double_movement && !dx && !dy)
2066 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2067 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2070 if (game.use_masked_elements && (dx || dy))
2071 mask_mode = USE_MASKING;
2073 else // border element
2075 graphic = el2img(element);
2076 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2079 if (element == EL_EXPANDABLE_WALL)
2081 boolean left_stopped = FALSE, right_stopped = FALSE;
2083 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2084 left_stopped = TRUE;
2085 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2086 right_stopped = TRUE;
2088 if (left_stopped && right_stopped)
2090 else if (left_stopped)
2092 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2093 frame = graphic_info[graphic].anim_frames - 1;
2095 else if (right_stopped)
2097 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2098 frame = graphic_info[graphic].anim_frames - 1;
2103 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2104 else if (mask_mode == USE_MASKING)
2105 DrawGraphicThruMask(x, y, graphic, frame);
2107 DrawGraphic(x, y, graphic, frame);
2110 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2111 int cut_mode, int mask_mode)
2113 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2114 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2115 cut_mode, mask_mode);
2118 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2121 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2124 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2127 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2130 void DrawLevelElementThruMask(int x, int y, int element)
2132 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2135 void DrawLevelFieldThruMask(int x, int y)
2137 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2140 // !!! implementation of quicksand is totally broken !!!
2141 #define IS_CRUMBLED_TILE(x, y, e) \
2142 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2143 !IS_MOVING(x, y) || \
2144 (e) == EL_QUICKSAND_EMPTYING || \
2145 (e) == EL_QUICKSAND_FAST_EMPTYING))
2147 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2152 int width, height, cx, cy;
2153 int sx = SCREENX(x), sy = SCREENY(y);
2154 int crumbled_border_size = graphic_info[graphic].border_size;
2155 int crumbled_tile_size = graphic_info[graphic].tile_size;
2156 int crumbled_border_size_var =
2157 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2160 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2162 for (i = 1; i < 4; i++)
2164 int dxx = (i & 1 ? dx : 0);
2165 int dyy = (i & 2 ? dy : 0);
2168 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2171 // check if neighbour field is of same crumble type
2172 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2173 graphic_info[graphic].class ==
2174 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2176 // return if check prevents inner corner
2177 if (same == (dxx == dx && dyy == dy))
2181 // if we reach this point, we have an inner corner
2183 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2185 width = crumbled_border_size_var;
2186 height = crumbled_border_size_var;
2187 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2188 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2190 if (game.use_masked_elements)
2192 int graphic0 = el2img(EL_EMPTY);
2193 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2194 Bitmap *src_bitmap0;
2197 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2199 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2201 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2203 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2205 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2208 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2210 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2213 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2218 int width, height, bx, by, cx, cy;
2219 int sx = SCREENX(x), sy = SCREENY(y);
2220 int crumbled_border_size = graphic_info[graphic].border_size;
2221 int crumbled_tile_size = graphic_info[graphic].tile_size;
2222 int crumbled_border_size_var =
2223 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2224 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2227 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2229 // only needed when using masked elements
2230 int graphic0 = el2img(EL_EMPTY);
2231 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2232 Bitmap *src_bitmap0;
2235 if (game.use_masked_elements)
2236 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2238 // draw simple, sloppy, non-corner-accurate crumbled border
2240 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2241 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2242 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2243 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2245 if (game.use_masked_elements)
2247 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2249 FX + sx * TILEX_VAR + cx,
2250 FY + sy * TILEY_VAR + cy);
2252 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2254 FX + sx * TILEX_VAR + cx,
2255 FY + sy * TILEY_VAR + cy);
2258 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2260 FX + sx * TILEX_VAR + cx,
2261 FY + sy * TILEY_VAR + cy);
2263 // (remaining middle border part must be at least as big as corner part)
2264 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2265 crumbled_border_size_var >= TILESIZE_VAR / 3)
2268 // correct corners of crumbled border, if needed
2270 for (i = -1; i <= 1; i += 2)
2272 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2273 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2274 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2277 // check if neighbour field is of same crumble type
2278 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2279 graphic_info[graphic].class ==
2280 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2282 // no crumbled corner, but continued crumbled border
2284 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2285 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2286 int b1 = (i == 1 ? crumbled_border_size_var :
2287 TILESIZE_VAR - 2 * crumbled_border_size_var);
2289 width = crumbled_border_size_var;
2290 height = crumbled_border_size_var;
2292 if (dir == 1 || dir == 2)
2307 if (game.use_masked_elements)
2309 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2311 FX + sx * TILEX_VAR + cx,
2312 FY + sy * TILEY_VAR + cy);
2314 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2316 FX + sx * TILEX_VAR + cx,
2317 FY + sy * TILEY_VAR + cy);
2320 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2322 FX + sx * TILEX_VAR + cx,
2323 FY + sy * TILEY_VAR + cy);
2328 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2330 int sx = SCREENX(x), sy = SCREENY(y);
2333 struct XY *xy = xy_topdown;
2335 if (!IN_LEV_FIELD(x, y))
2338 element = TILE_GFX_ELEMENT(x, y);
2340 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2342 if (!IN_SCR_FIELD(sx, sy))
2345 // crumble field borders towards direct neighbour fields
2346 for (i = 0; i < 4; i++)
2348 int xx = x + xy[i].x;
2349 int yy = y + xy[i].y;
2351 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2354 // check if neighbour field is of same crumble type
2355 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2356 graphic_info[graphic].class ==
2357 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2360 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2363 // crumble inner field corners towards corner neighbour fields
2364 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2365 graphic_info[graphic].anim_frames == 2)
2367 for (i = 0; i < 4; i++)
2369 int dx = (i & 1 ? +1 : -1);
2370 int dy = (i & 2 ? +1 : -1);
2372 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2376 MarkTileDirty(sx, sy);
2378 else // center field is not crumbled -- crumble neighbour fields
2380 // crumble field borders of direct neighbour fields
2381 for (i = 0; i < 4; i++)
2383 int xx = x + xy[i].x;
2384 int yy = y + xy[i].y;
2385 int sxx = sx + xy[i].x;
2386 int syy = sy + xy[i].y;
2388 if (!IN_LEV_FIELD(xx, yy) ||
2389 !IN_SCR_FIELD(sxx, syy))
2392 // do not crumble fields that are being digged or snapped
2393 if (Tile[xx][yy] == EL_EMPTY ||
2394 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2397 element = TILE_GFX_ELEMENT(xx, yy);
2399 if (!IS_CRUMBLED_TILE(xx, yy, element))
2402 graphic = el_act2crm(element, ACTION_DEFAULT);
2404 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2406 MarkTileDirty(sxx, syy);
2409 // crumble inner field corners of corner neighbour fields
2410 for (i = 0; i < 4; i++)
2412 int dx = (i & 1 ? +1 : -1);
2413 int dy = (i & 2 ? +1 : -1);
2419 if (!IN_LEV_FIELD(xx, yy) ||
2420 !IN_SCR_FIELD(sxx, syy))
2423 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2426 element = TILE_GFX_ELEMENT(xx, yy);
2428 if (!IS_CRUMBLED_TILE(xx, yy, element))
2431 graphic = el_act2crm(element, ACTION_DEFAULT);
2433 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2434 graphic_info[graphic].anim_frames == 2)
2435 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2437 MarkTileDirty(sxx, syy);
2442 void DrawLevelFieldCrumbled(int x, int y)
2446 if (!IN_LEV_FIELD(x, y))
2449 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2450 GfxElement[x][y] != EL_UNDEFINED &&
2451 GFX_CRUMBLED(GfxElement[x][y]))
2453 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2458 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2460 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2463 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2466 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2467 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2468 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2469 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2470 int sx = SCREENX(x), sy = SCREENY(y);
2472 DrawScreenGraphic(sx, sy, graphic1, frame1);
2473 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2476 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2478 int sx = SCREENX(x), sy = SCREENY(y);
2479 struct XY *xy = xy_topdown;
2482 // crumble direct neighbour fields (required for field borders)
2483 for (i = 0; i < 4; i++)
2485 int xx = x + xy[i].x;
2486 int yy = y + xy[i].y;
2487 int sxx = sx + xy[i].x;
2488 int syy = sy + xy[i].y;
2490 if (!IN_LEV_FIELD(xx, yy) ||
2491 !IN_SCR_FIELD(sxx, syy) ||
2492 !GFX_CRUMBLED(Tile[xx][yy]) ||
2496 DrawLevelField(xx, yy);
2499 // crumble corner neighbour fields (required for inner field corners)
2500 for (i = 0; i < 4; i++)
2502 int dx = (i & 1 ? +1 : -1);
2503 int dy = (i & 2 ? +1 : -1);
2509 if (!IN_LEV_FIELD(xx, yy) ||
2510 !IN_SCR_FIELD(sxx, syy) ||
2511 !GFX_CRUMBLED(Tile[xx][yy]) ||
2515 int element = TILE_GFX_ELEMENT(xx, yy);
2516 int graphic = el_act2crm(element, ACTION_DEFAULT);
2518 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2519 graphic_info[graphic].anim_frames == 2)
2520 DrawLevelField(xx, yy);
2524 static int getBorderElement(int x, int y)
2528 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2529 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2530 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2531 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2532 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2533 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2534 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2536 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2537 int steel_position = (x == -1 && y == -1 ? 0 :
2538 x == lev_fieldx && y == -1 ? 1 :
2539 x == -1 && y == lev_fieldy ? 2 :
2540 x == lev_fieldx && y == lev_fieldy ? 3 :
2541 x == -1 || x == lev_fieldx ? 4 :
2542 y == -1 || y == lev_fieldy ? 5 : 6);
2544 return border[steel_position][steel_type];
2547 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2549 if (game.use_masked_elements)
2551 if (graphic != el2img(EL_EMPTY))
2552 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2554 DrawGraphicThruMask(x, y, graphic, frame);
2558 DrawGraphic(x, y, graphic, frame);
2562 void DrawLevelGraphic(int x, int y, int graphic, int frame)
2564 DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2567 void DrawScreenElement(int x, int y, int element)
2569 int mask_mode = NO_MASKING;
2571 if (game.use_masked_elements)
2573 int lx = LEVELX(x), ly = LEVELY(y);
2575 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2577 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2579 mask_mode = USE_MASKING;
2583 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2584 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2587 void DrawLevelElement(int x, int y, int element)
2589 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2590 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2593 void DrawScreenField(int x, int y)
2595 int lx = LEVELX(x), ly = LEVELY(y);
2596 int element, content;
2598 if (!IN_LEV_FIELD(lx, ly))
2600 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2603 element = getBorderElement(lx, ly);
2605 DrawScreenElement(x, y, element);
2610 element = Tile[lx][ly];
2611 content = Store[lx][ly];
2613 if (IS_MOVING(lx, ly))
2615 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2616 boolean cut_mode = NO_CUTTING;
2618 if (element == EL_QUICKSAND_EMPTYING ||
2619 element == EL_QUICKSAND_FAST_EMPTYING ||
2620 element == EL_MAGIC_WALL_EMPTYING ||
2621 element == EL_BD_MAGIC_WALL_EMPTYING ||
2622 element == EL_DC_MAGIC_WALL_EMPTYING ||
2623 element == EL_AMOEBA_DROPPING)
2624 cut_mode = CUT_ABOVE;
2625 else if (element == EL_QUICKSAND_FILLING ||
2626 element == EL_QUICKSAND_FAST_FILLING ||
2627 element == EL_MAGIC_WALL_FILLING ||
2628 element == EL_BD_MAGIC_WALL_FILLING ||
2629 element == EL_DC_MAGIC_WALL_FILLING)
2630 cut_mode = CUT_BELOW;
2632 if (cut_mode == CUT_ABOVE)
2633 DrawScreenElement(x, y, element);
2635 DrawScreenElement(x, y, EL_EMPTY);
2637 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2639 int dir = MovDir[lx][ly];
2640 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2641 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2643 if (IN_SCR_FIELD(newx, newy))
2644 DrawScreenElement(newx, newy, EL_EMPTY);
2648 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2649 else if (cut_mode == NO_CUTTING)
2650 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2653 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2655 if (cut_mode == CUT_BELOW &&
2656 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2657 DrawLevelElement(lx, ly + 1, element);
2660 if (content == EL_ACID)
2662 int dir = MovDir[lx][ly];
2663 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2664 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2666 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2668 // prevent target field from being drawn again (but without masking)
2669 // (this would happen if target field is scanned after moving element)
2670 Stop[newlx][newly] = TRUE;
2673 else if (IS_BLOCKED(lx, ly))
2678 boolean cut_mode = NO_CUTTING;
2679 int element_old, content_old;
2681 Blocked2Moving(lx, ly, &oldx, &oldy);
2684 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2685 MovDir[oldx][oldy] == MV_RIGHT);
2687 element_old = Tile[oldx][oldy];
2688 content_old = Store[oldx][oldy];
2690 if (element_old == EL_QUICKSAND_EMPTYING ||
2691 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2692 element_old == EL_MAGIC_WALL_EMPTYING ||
2693 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2694 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2695 element_old == EL_AMOEBA_DROPPING)
2696 cut_mode = CUT_ABOVE;
2698 DrawScreenElement(x, y, EL_EMPTY);
2701 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2703 else if (cut_mode == NO_CUTTING)
2704 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2707 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2710 else if (IS_DRAWABLE(element))
2711 DrawScreenElement(x, y, element);
2713 DrawScreenElement(x, y, EL_EMPTY);
2716 void DrawLevelField(int x, int y)
2718 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2719 DrawScreenField(SCREENX(x), SCREENY(y));
2720 else if (IS_MOVING(x, y))
2724 Moving2Blocked(x, y, &newx, &newy);
2725 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2726 DrawScreenField(SCREENX(newx), SCREENY(newy));
2728 else if (IS_BLOCKED(x, y))
2732 Blocked2Moving(x, y, &oldx, &oldy);
2733 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2734 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2738 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2739 int (*el2img_function)(int), boolean masked,
2740 int element_bits_draw)
2742 int element_base = map_mm_wall_element(element);
2743 int element_bits = (IS_DF_WALL(element) ?
2744 element - EL_DF_WALL_START :
2745 IS_MM_WALL(element) ?
2746 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2747 int graphic = el2img_function(element_base);
2748 int tilesize_draw = tilesize / 2;
2753 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2755 for (i = 0; i < 4; i++)
2757 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2758 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2760 if (!(element_bits_draw & (1 << i)))
2763 if (element_bits & (1 << i))
2766 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2767 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2769 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2770 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2775 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2776 tilesize_draw, tilesize_draw);
2781 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2782 boolean masked, int element_bits_draw)
2784 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2785 element, tilesize, el2edimg, masked, element_bits_draw);
2788 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2789 int (*el2img_function)(int))
2791 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2795 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2798 if (IS_MM_WALL(element))
2800 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2801 element, tilesize, el2edimg, masked, 0x000f);
2805 int graphic = el2edimg(element);
2808 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2810 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2814 void DrawSizedElement(int x, int y, int element, int tilesize)
2816 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2819 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2821 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2824 void DrawMiniElement(int x, int y, int element)
2828 graphic = el2edimg(element);
2829 DrawMiniGraphic(x, y, graphic);
2832 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2835 int x = sx + scroll_x, y = sy + scroll_y;
2837 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2838 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2839 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2840 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2842 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2845 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2847 int x = sx + scroll_x, y = sy + scroll_y;
2849 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2850 DrawMiniElement(sx, sy, EL_EMPTY);
2851 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2852 DrawMiniElement(sx, sy, Tile[x][y]);
2854 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2857 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2858 int x, int y, int xsize, int ysize,
2859 int tile_width, int tile_height)
2863 int dst_x = startx + x * tile_width;
2864 int dst_y = starty + y * tile_height;
2865 int width = graphic_info[graphic].width;
2866 int height = graphic_info[graphic].height;
2867 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2868 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2869 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2870 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2871 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2872 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2873 boolean draw_masked = graphic_info[graphic].draw_masked;
2875 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2877 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2879 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2883 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2884 inner_sx + (x - 1) * tile_width % inner_width);
2885 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2886 inner_sy + (y - 1) * tile_height % inner_height);
2889 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2892 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2896 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2897 int x, int y, int xsize, int ysize,
2900 int font_width = getFontWidth(font_nr);
2901 int font_height = getFontHeight(font_nr);
2903 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2904 font_width, font_height);
2907 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2909 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2910 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2911 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2912 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2913 boolean no_delay = (tape.warp_forward);
2914 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2915 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2916 DelayCounter anim_delay = { anim_delay_value };
2917 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2918 int font_width = getFontWidth(font_nr);
2919 int font_height = getFontHeight(font_nr);
2920 int max_xsize = level.envelope[envelope_nr].xsize;
2921 int max_ysize = level.envelope[envelope_nr].ysize;
2922 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2923 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2924 int xend = max_xsize;
2925 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2926 int xstep = (xstart < xend ? 1 : 0);
2927 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2929 int end = MAX(xend - xstart, yend - ystart);
2932 for (i = start; i <= end; i++)
2934 int last_frame = end; // last frame of this "for" loop
2935 int x = xstart + i * xstep;
2936 int y = ystart + i * ystep;
2937 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2938 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2939 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2940 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2943 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2945 BlitScreenToBitmap(backbuffer);
2947 SetDrawtoField(DRAW_TO_BACKBUFFER);
2949 for (yy = 0; yy < ysize; yy++)
2950 for (xx = 0; xx < xsize; xx++)
2951 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2953 DrawTextArea(sx + font_width, sy + font_height,
2954 level.envelope[envelope_nr].text, font_nr, max_xsize,
2955 xsize - 2, ysize - 2, 0, mask_mode,
2956 level.envelope[envelope_nr].autowrap,
2957 level.envelope[envelope_nr].centered, FALSE);
2959 redraw_mask |= REDRAW_FIELD;
2962 SkipUntilDelayReached(&anim_delay, &i, last_frame);
2965 ClearAutoRepeatKeyEvents();
2968 void ShowEnvelope(int envelope_nr)
2970 int element = EL_ENVELOPE_1 + envelope_nr;
2971 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2972 int sound_opening = element_info[element].sound[ACTION_OPENING];
2973 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2974 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2975 boolean no_delay = (tape.warp_forward);
2976 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2977 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2978 int anim_mode = graphic_info[graphic].anim_mode;
2979 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2980 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2981 boolean overlay_enabled = GetOverlayEnabled();
2983 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2985 SetOverlayEnabled(FALSE);
2988 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2990 if (anim_mode == ANIM_DEFAULT)
2991 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2993 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2996 Delay_WithScreenUpdates(wait_delay_value);
2998 WaitForEventToContinue();
3001 SetOverlayEnabled(overlay_enabled);
3003 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3005 if (anim_mode != ANIM_NONE)
3006 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
3008 if (anim_mode == ANIM_DEFAULT)
3009 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
3011 game.envelope_active = FALSE;
3013 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3015 redraw_mask |= REDRAW_FIELD;
3019 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
3020 int xsize, int ysize)
3022 if (!global.use_envelope_request)
3025 if (request.bitmap == NULL ||
3026 xsize > request.xsize ||
3027 ysize > request.ysize)
3029 if (request.bitmap != NULL)
3030 FreeBitmap(request.bitmap);
3032 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
3034 SDL_Surface *surface = request.bitmap->surface;
3036 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
3037 Fail("SDLGetNativeSurface() failed");
3040 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3042 // create masked surface for request bitmap, if needed
3043 if (graphic_info[IMG_BACKGROUND_REQUEST].draw_masked)
3045 SDL_Surface *surface = request.bitmap->surface;
3046 SDL_Surface *surface_masked = request.bitmap->surface_masked;
3048 SDLBlitSurface(surface, surface_masked, 0, 0, xsize, ysize, 0, 0);
3049 SDL_SetColorKey(surface_masked, SET_TRANSPARENT_PIXEL,
3050 SDL_MapRGB(surface_masked->format, 0x00, 0x00, 0x00));
3053 SDLFreeBitmapTextures(request.bitmap);
3054 SDLCreateBitmapTextures(request.bitmap);
3056 // set envelope request run-time values
3059 request.xsize = xsize;
3060 request.ysize = ysize;
3063 void DrawEnvelopeRequestToScreen(int drawing_target)
3065 if (global.use_envelope_request &&
3066 game.request_active &&
3067 drawing_target == DRAW_TO_SCREEN)
3069 if (graphic_info[IMG_BACKGROUND_REQUEST].draw_masked)
3070 BlitToScreenMasked(request.bitmap, 0, 0, request.xsize, request.ysize,
3071 request.sx, request.sy);
3073 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3074 request.sx, request.sy);
3078 static void setRequestBasePosition(int *x, int *y)
3080 int sx_base, sy_base;
3082 if (request.x != -1)
3083 sx_base = request.x;
3084 else if (request.align == ALIGN_LEFT)
3086 else if (request.align == ALIGN_RIGHT)
3087 sx_base = SX + SXSIZE;
3089 sx_base = SX + SXSIZE / 2;
3091 if (request.y != -1)
3092 sy_base = request.y;
3093 else if (request.valign == VALIGN_TOP)
3095 else if (request.valign == VALIGN_BOTTOM)
3096 sy_base = SY + SYSIZE;
3098 sy_base = SY + SYSIZE / 2;
3104 static void setRequestPositionExt(int *x, int *y, int width, int height,
3105 boolean add_border_size)
3107 int border_size = request.border_size;
3108 int sx_base, sy_base;
3111 setRequestBasePosition(&sx_base, &sy_base);
3113 if (request.align == ALIGN_LEFT)
3115 else if (request.align == ALIGN_RIGHT)
3116 sx = sx_base - width;
3118 sx = sx_base - width / 2;
3120 if (request.valign == VALIGN_TOP)
3122 else if (request.valign == VALIGN_BOTTOM)
3123 sy = sy_base - height;
3125 sy = sy_base - height / 2;
3127 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3128 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3130 if (add_border_size)
3140 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3142 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3145 static void DrawEnvelopeRequest(char *text, unsigned int req_state)
3147 DrawBuffer *drawto_last = drawto;
3148 char *text_final = text;
3149 char *text_door_style = NULL;
3150 int graphic = IMG_BACKGROUND_REQUEST;
3151 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3152 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3153 int font_nr = FONT_REQUEST;
3154 int font_width = getFontWidth(font_nr);
3155 int font_height = getFontHeight(font_nr);
3156 int border_size = request.border_size;
3157 int line_spacing = request.line_spacing;
3158 int line_height = font_height + line_spacing;
3159 int max_text_width = request.width - 2 * border_size;
3160 int max_text_height = request.height - 2 * border_size;
3161 int line_length = max_text_width / font_width;
3162 int max_lines = max_text_height / line_height;
3163 int text_width = line_length * font_width;
3164 int width = request.width;
3165 int height = request.height;
3166 int tile_size = MAX(request.step_offset, 1);
3167 int x_steps = width / tile_size;
3168 int y_steps = height / tile_size;
3169 int sx_offset = border_size;
3170 int sy_offset = border_size;
3174 if (request.centered)
3175 sx_offset = (request.width - text_width) / 2;
3177 if (request.wrap_single_words && !request.autowrap)
3179 char *src_text_ptr, *dst_text_ptr;
3181 text_door_style = checked_malloc(2 * strlen(text) + 1);
3183 src_text_ptr = text;
3184 dst_text_ptr = text_door_style;
3186 while (*src_text_ptr)
3188 if (*src_text_ptr == ' ' ||
3189 *src_text_ptr == '?' ||
3190 *src_text_ptr == '!')
3191 *dst_text_ptr++ = '\n';
3193 if (*src_text_ptr != ' ')
3194 *dst_text_ptr++ = *src_text_ptr;
3199 *dst_text_ptr = '\0';
3201 text_final = text_door_style;
3204 setRequestPosition(&sx, &sy, FALSE);
3206 // draw complete envelope request to temporary bitmap
3207 drawto = bitmap_db_store_1;
3209 ClearRectangle(drawto, sx, sy, width, height);
3211 for (y = 0; y < y_steps; y++)
3212 for (x = 0; x < x_steps; x++)
3213 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3214 x, y, x_steps, y_steps,
3215 tile_size, tile_size);
3217 // force DOOR font inside door area
3218 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3220 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3221 line_length, -1, max_lines, line_spacing, mask_mode,
3222 request.autowrap, request.centered, FALSE);
3226 if (req_state & REQ_ASK)
3228 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3229 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3230 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3231 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3233 else if (req_state & REQ_CONFIRM)
3235 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3236 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3238 else if (req_state & REQ_PLAYER)
3240 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3241 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3242 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3243 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3246 // restore pointer to drawing buffer
3247 drawto = drawto_last;
3249 // prepare complete envelope request from temporary bitmap
3250 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy, width, height);
3252 if (text_door_style)
3253 free(text_door_style);
3256 static void AnimateEnvelopeRequest(int anim_mode, int action)
3258 int delay_value_normal = request.step_delay;
3259 int delay_value_fast = delay_value_normal / 2;
3260 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3261 boolean no_delay = (tape.warp_forward);
3262 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3263 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3264 DelayCounter anim_delay = { anim_delay_value };
3266 int tile_size = MAX(request.step_offset, 1);
3267 int max_xsize = request.width / tile_size;
3268 int max_ysize = request.height / tile_size;
3269 int max_xsize_inner = max_xsize - 2;
3270 int max_ysize_inner = max_ysize - 2;
3272 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3273 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3274 int xend = max_xsize_inner;
3275 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3276 int xstep = (xstart < xend ? 1 : 0);
3277 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3279 int end = MAX(xend - xstart, yend - ystart);
3282 if (setup.quick_doors)
3289 for (i = start; i <= end; i++)
3291 int last_frame = end; // last frame of this "for" loop
3292 int x = xstart + i * xstep;
3293 int y = ystart + i * ystep;
3294 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3295 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3296 int xsize_size_left = (xsize - 1) * tile_size;
3297 int ysize_size_top = (ysize - 1) * tile_size;
3298 int max_xsize_pos = (max_xsize - 1) * tile_size;
3299 int max_ysize_pos = (max_ysize - 1) * tile_size;
3300 int width = xsize * tile_size;
3301 int height = ysize * tile_size;
3306 setRequestPosition(&src_x, &src_y, FALSE);
3307 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3309 for (yy = 0; yy < 2; yy++)
3311 for (xx = 0; xx < 2; xx++)
3313 int src_xx = src_x + xx * max_xsize_pos;
3314 int src_yy = src_y + yy * max_ysize_pos;
3315 int dst_xx = dst_x + xx * xsize_size_left;
3316 int dst_yy = dst_y + yy * ysize_size_top;
3317 int xx_size = (xx ? tile_size : xsize_size_left);
3318 int yy_size = (yy ? tile_size : ysize_size_top);
3320 // draw partial (animated) envelope request to temporary bitmap
3321 BlitBitmap(bitmap_db_store_1, bitmap_db_store_2,
3322 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3326 // prepare partial (animated) envelope request from temporary bitmap
3327 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, dst_x, dst_y,
3330 redraw_mask |= REDRAW_FIELD;
3334 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3337 ClearAutoRepeatKeyEvents();
3340 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3342 int graphic = IMG_BACKGROUND_REQUEST;
3343 int sound_opening = SND_REQUEST_OPENING;
3344 int sound_closing = SND_REQUEST_CLOSING;
3345 int anim_mode_1 = request.anim_mode; // (higher priority)
3346 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3347 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3348 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3349 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3351 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3353 if (action == ACTION_OPENING)
3355 DrawEnvelopeRequest(text, req_state);
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;
3378 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3380 if (IS_MM_WALL(element))
3382 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3388 int graphic = el2preimg(element);
3390 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3391 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3396 void DrawLevel(int draw_background_mask)
3400 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3401 SetDrawBackgroundMask(draw_background_mask);
3405 for (x = BX1; x <= BX2; x++)
3406 for (y = BY1; y <= BY2; y++)
3407 DrawScreenField(x, y);
3409 redraw_mask |= REDRAW_FIELD;
3412 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3417 for (x = 0; x < size_x; x++)
3418 for (y = 0; y < size_y; y++)
3419 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3421 redraw_mask |= REDRAW_FIELD;
3424 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3428 for (x = 0; x < size_x; x++)
3429 for (y = 0; y < size_y; y++)
3430 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3432 redraw_mask |= REDRAW_FIELD;
3435 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3437 boolean show_level_border = (BorderElement != EL_EMPTY);
3438 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3439 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3440 int tile_size = preview.tile_size;
3441 int preview_width = preview.xsize * tile_size;
3442 int preview_height = preview.ysize * tile_size;
3443 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3444 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3445 int real_preview_width = real_preview_xsize * tile_size;
3446 int real_preview_height = real_preview_ysize * tile_size;
3447 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3448 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3451 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3454 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3456 dst_x += (preview_width - real_preview_width) / 2;
3457 dst_y += (preview_height - real_preview_height) / 2;
3459 for (x = 0; x < real_preview_xsize; x++)
3461 for (y = 0; y < real_preview_ysize; y++)
3463 int lx = from_x + x + (show_level_border ? -1 : 0);
3464 int ly = from_y + y + (show_level_border ? -1 : 0);
3465 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3466 getBorderElement(lx, ly));
3468 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3469 element, tile_size);
3473 redraw_mask |= REDRAW_FIELD;
3476 #define MICROLABEL_EMPTY 0
3477 #define MICROLABEL_LEVEL_NAME 1
3478 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3479 #define MICROLABEL_LEVEL_AUTHOR 3
3480 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3481 #define MICROLABEL_IMPORTED_FROM 5
3482 #define MICROLABEL_IMPORTED_BY_HEAD 6
3483 #define MICROLABEL_IMPORTED_BY 7
3485 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3487 int max_text_width = SXSIZE;
3488 int font_width = getFontWidth(font_nr);
3490 if (pos->align == ALIGN_CENTER)
3491 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3492 else if (pos->align == ALIGN_RIGHT)
3493 max_text_width = pos->x;
3495 max_text_width = SXSIZE - pos->x;
3497 return max_text_width / font_width;
3500 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3502 char label_text[MAX_OUTPUT_LINESIZE + 1];
3503 int max_len_label_text;
3504 int font_nr = pos->font;
3507 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3510 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3511 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3512 mode == MICROLABEL_IMPORTED_BY_HEAD)
3513 font_nr = pos->font_alt;
3515 max_len_label_text = getMaxTextLength(pos, font_nr);
3517 if (pos->size != -1)
3518 max_len_label_text = pos->size;
3520 for (i = 0; i < max_len_label_text; i++)
3521 label_text[i] = ' ';
3522 label_text[max_len_label_text] = '\0';
3524 if (strlen(label_text) > 0)
3525 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3528 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3529 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3530 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3531 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3532 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3533 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3534 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3535 max_len_label_text);
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);
3541 redraw_mask |= REDRAW_FIELD;
3544 static void DrawPreviewLevelLabel(int mode)
3546 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3549 static void DrawPreviewLevelInfo(int mode)
3551 if (mode == MICROLABEL_LEVEL_NAME)
3552 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3553 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3554 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3557 static void DrawPreviewLevelExt(boolean restart)
3559 static DelayCounter scroll_delay = { 0 };
3560 static DelayCounter label_delay = { 0 };
3561 static int from_x, from_y, scroll_direction;
3562 static int label_state, label_counter;
3563 boolean show_level_border = (BorderElement != EL_EMPTY);
3564 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3565 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3567 scroll_delay.value = preview.step_delay;
3568 label_delay.value = MICROLEVEL_LABEL_DELAY;
3575 if (preview.anim_mode == ANIM_CENTERED)
3577 if (level_xsize > preview.xsize)
3578 from_x = (level_xsize - preview.xsize) / 2;
3579 if (level_ysize > preview.ysize)
3580 from_y = (level_ysize - preview.ysize) / 2;
3583 from_x += preview.xoffset;
3584 from_y += preview.yoffset;
3586 scroll_direction = MV_RIGHT;
3590 DrawPreviewLevelPlayfield(from_x, from_y);
3591 DrawPreviewLevelLabel(label_state);
3593 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3594 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3596 // initialize delay counters
3597 ResetDelayCounter(&scroll_delay);
3598 ResetDelayCounter(&label_delay);
3600 if (leveldir_current->name)
3602 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3603 char label_text[MAX_OUTPUT_LINESIZE + 1];
3604 int font_nr = pos->font;
3605 int max_len_label_text = getMaxTextLength(pos, font_nr);
3607 if (pos->size != -1)
3608 max_len_label_text = pos->size;
3610 strncpy(label_text, leveldir_current->name, max_len_label_text);
3611 label_text[max_len_label_text] = '\0';
3613 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3614 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3620 // scroll preview level, if needed
3621 if (preview.anim_mode != ANIM_NONE &&
3622 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3623 DelayReached(&scroll_delay))
3625 switch (scroll_direction)
3630 from_x -= preview.step_offset;
3631 from_x = (from_x < 0 ? 0 : from_x);
3634 scroll_direction = MV_UP;
3638 if (from_x < level_xsize - preview.xsize)
3640 from_x += preview.step_offset;
3641 from_x = (from_x > level_xsize - preview.xsize ?
3642 level_xsize - preview.xsize : from_x);
3645 scroll_direction = MV_DOWN;
3651 from_y -= preview.step_offset;
3652 from_y = (from_y < 0 ? 0 : from_y);
3655 scroll_direction = MV_RIGHT;
3659 if (from_y < level_ysize - preview.ysize)
3661 from_y += preview.step_offset;
3662 from_y = (from_y > level_ysize - preview.ysize ?
3663 level_ysize - preview.ysize : from_y);
3666 scroll_direction = MV_LEFT;
3673 DrawPreviewLevelPlayfield(from_x, from_y);
3676 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3677 // redraw micro level label, if needed
3678 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3679 !strEqual(level.author, ANONYMOUS_NAME) &&
3680 !strEqual(level.author, leveldir_current->name) &&
3681 DelayReached(&label_delay))
3683 int max_label_counter = 23;
3685 if (leveldir_current->imported_from != NULL &&
3686 strlen(leveldir_current->imported_from) > 0)
3687 max_label_counter += 14;
3688 if (leveldir_current->imported_by != NULL &&
3689 strlen(leveldir_current->imported_by) > 0)
3690 max_label_counter += 14;
3692 label_counter = (label_counter + 1) % max_label_counter;
3693 label_state = (label_counter >= 0 && label_counter <= 7 ?
3694 MICROLABEL_LEVEL_NAME :
3695 label_counter >= 9 && label_counter <= 12 ?
3696 MICROLABEL_LEVEL_AUTHOR_HEAD :
3697 label_counter >= 14 && label_counter <= 21 ?
3698 MICROLABEL_LEVEL_AUTHOR :
3699 label_counter >= 23 && label_counter <= 26 ?
3700 MICROLABEL_IMPORTED_FROM_HEAD :
3701 label_counter >= 28 && label_counter <= 35 ?
3702 MICROLABEL_IMPORTED_FROM :
3703 label_counter >= 37 && label_counter <= 40 ?
3704 MICROLABEL_IMPORTED_BY_HEAD :
3705 label_counter >= 42 && label_counter <= 49 ?
3706 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3708 if (leveldir_current->imported_from == NULL &&
3709 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3710 label_state == MICROLABEL_IMPORTED_FROM))
3711 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3712 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3714 DrawPreviewLevelLabel(label_state);
3718 void DrawPreviewPlayers(void)
3720 if (game_status != GAME_MODE_MAIN)
3723 // do not draw preview players if level preview redefined, but players aren't
3724 if (preview.redefined && !menu.main.preview_players.redefined)
3727 boolean player_found[MAX_PLAYERS];
3728 int num_players = 0;
3731 for (i = 0; i < MAX_PLAYERS; i++)
3732 player_found[i] = FALSE;
3734 // check which players can be found in the level (simple approach)
3735 for (x = 0; x < lev_fieldx; x++)
3737 for (y = 0; y < lev_fieldy; y++)
3739 int element = level.field[x][y];
3741 if (IS_PLAYER_ELEMENT(element))
3743 int player_nr = GET_PLAYER_NR(element);
3745 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3747 if (!player_found[player_nr])
3750 player_found[player_nr] = TRUE;
3755 struct TextPosInfo *pos = &menu.main.preview_players;
3756 int tile_size = pos->tile_size;
3757 int border_size = pos->border_size;
3758 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3759 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3760 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3761 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3762 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3763 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3764 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3765 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3766 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3767 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3768 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3769 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3771 // clear area in which the players will be drawn
3772 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3773 max_players_width, max_players_height);
3775 if (!network.enabled && !setup.team_mode)
3778 // only draw players if level is suited for team mode
3779 if (num_players < 2)
3782 // draw all players that were found in the level
3783 for (i = 0; i < MAX_PLAYERS; i++)
3785 if (player_found[i])
3787 int graphic = el2img(EL_PLAYER_1 + i);
3789 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3791 xpos += player_xoffset;
3792 ypos += player_yoffset;
3797 void DrawPreviewLevelInitial(void)
3799 DrawPreviewLevelExt(TRUE);
3800 DrawPreviewPlayers();
3803 void DrawPreviewLevelAnimation(void)
3805 DrawPreviewLevelExt(FALSE);
3808 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3809 int border_size, int font_nr)
3811 int graphic = el2img(EL_PLAYER_1 + player_nr);
3812 int font_height = getFontHeight(font_nr);
3813 int player_height = MAX(tile_size, font_height);
3814 int xoffset_text = tile_size + border_size;
3815 int yoffset_text = (player_height - font_height) / 2;
3816 int yoffset_graphic = (player_height - tile_size) / 2;
3817 char *player_name = getNetworkPlayerName(player_nr + 1);
3819 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3821 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3824 static void DrawNetworkPlayersExt(boolean force)
3826 if (game_status != GAME_MODE_MAIN)
3829 if (!network.connected && !force)
3832 // do not draw network players if level preview redefined, but players aren't
3833 if (preview.redefined && !menu.main.network_players.redefined)
3836 int num_players = 0;
3839 for (i = 0; i < MAX_PLAYERS; i++)
3840 if (stored_player[i].connected_network)
3843 struct TextPosInfo *pos = &menu.main.network_players;
3844 int tile_size = pos->tile_size;
3845 int border_size = pos->border_size;
3846 int xoffset_text = tile_size + border_size;
3847 int font_nr = pos->font;
3848 int font_width = getFontWidth(font_nr);
3849 int font_height = getFontHeight(font_nr);
3850 int player_height = MAX(tile_size, font_height);
3851 int player_yoffset = player_height + border_size;
3852 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3853 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3854 int all_players_height = num_players * player_yoffset - border_size;
3855 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3856 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3857 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3859 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3860 max_players_width, max_players_height);
3862 // first draw local network player ...
3863 for (i = 0; i < MAX_PLAYERS; i++)
3865 if (stored_player[i].connected_network &&
3866 stored_player[i].connected_locally)
3868 char *player_name = getNetworkPlayerName(i + 1);
3869 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3870 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3872 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3874 ypos += player_yoffset;
3878 // ... then draw all other network players
3879 for (i = 0; i < MAX_PLAYERS; i++)
3881 if (stored_player[i].connected_network &&
3882 !stored_player[i].connected_locally)
3884 char *player_name = getNetworkPlayerName(i + 1);
3885 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3886 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3888 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3890 ypos += player_yoffset;
3895 void DrawNetworkPlayers(void)
3897 DrawNetworkPlayersExt(FALSE);
3900 void ClearNetworkPlayers(void)
3902 DrawNetworkPlayersExt(TRUE);
3905 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3906 int graphic, int lx, int ly,
3909 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3911 if (mask_mode == USE_MASKING)
3912 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3914 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3917 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3918 int graphic, int sync_frame, int mask_mode)
3920 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3922 if (mask_mode == USE_MASKING)
3923 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3925 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3928 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3929 int graphic, int sync_frame, int tilesize,
3932 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3934 if (mask_mode == USE_MASKING)
3935 DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
3937 DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
3940 static void DrawGraphicAnimation(int x, int y, int graphic)
3942 int lx = LEVELX(x), ly = LEVELY(y);
3943 int mask_mode = NO_MASKING;
3945 if (!IN_SCR_FIELD(x, y))
3948 if (game.use_masked_elements)
3950 if (Tile[lx][ly] != EL_EMPTY)
3952 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3954 mask_mode = USE_MASKING;
3958 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3959 graphic, lx, ly, mask_mode);
3961 MarkTileDirty(x, y);
3964 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3966 int lx = LEVELX(x), ly = LEVELY(y);
3967 int mask_mode = NO_MASKING;
3969 if (!IN_SCR_FIELD(x, y))
3972 if (game.use_masked_elements)
3974 if (Tile[lx][ly] != EL_EMPTY)
3976 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3978 mask_mode = USE_MASKING;
3982 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3983 graphic, lx, ly, mask_mode);
3985 MarkTileDirty(x, y);
3988 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3990 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3993 void DrawLevelElementAnimation(int x, int y, int element)
3995 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3997 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4000 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
4002 int sx = SCREENX(x), sy = SCREENY(y);
4004 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4007 if (Tile[x][y] == EL_EMPTY)
4008 graphic = el2img(GfxElementEmpty[x][y]);
4010 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4013 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4016 DrawGraphicAnimation(sx, sy, graphic);
4019 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4020 DrawLevelFieldCrumbled(x, y);
4022 if (GFX_CRUMBLED(Tile[x][y]))
4023 DrawLevelFieldCrumbled(x, y);
4027 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4029 int sx = SCREENX(x), sy = SCREENY(y);
4032 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4035 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4037 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4040 DrawGraphicAnimation(sx, sy, graphic);
4042 if (GFX_CRUMBLED(element))
4043 DrawLevelFieldCrumbled(x, y);
4046 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4048 if (player->use_murphy)
4050 // this works only because currently only one player can be "murphy" ...
4051 static int last_horizontal_dir = MV_LEFT;
4052 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4054 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4055 last_horizontal_dir = move_dir;
4057 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4059 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4061 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4067 return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4070 static boolean equalGraphics(int graphic1, int graphic2)
4072 struct GraphicInfo *g1 = &graphic_info[graphic1];
4073 struct GraphicInfo *g2 = &graphic_info[graphic2];
4075 return (g1->bitmap == g2->bitmap &&
4076 g1->src_x == g2->src_x &&
4077 g1->src_y == g2->src_y &&
4078 g1->anim_frames == g2->anim_frames &&
4079 g1->anim_delay == g2->anim_delay &&
4080 g1->anim_mode == g2->anim_mode);
4083 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4087 DRAW_PLAYER_STAGE_INIT = 0,
4088 DRAW_PLAYER_STAGE_LAST_FIELD,
4089 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4090 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4091 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4092 DRAW_PLAYER_STAGE_PLAYER,
4094 DRAW_PLAYER_STAGE_PLAYER,
4095 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4097 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4098 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4100 NUM_DRAW_PLAYER_STAGES
4103 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4105 static int static_last_player_graphic[MAX_PLAYERS];
4106 static int static_last_player_frame[MAX_PLAYERS];
4107 static boolean static_player_is_opaque[MAX_PLAYERS];
4108 static boolean draw_player[MAX_PLAYERS];
4109 int pnr = player->index_nr;
4111 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4113 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4114 static_last_player_frame[pnr] = player->Frame;
4115 static_player_is_opaque[pnr] = FALSE;
4117 draw_player[pnr] = TRUE;
4120 if (!draw_player[pnr])
4124 if (!IN_LEV_FIELD(player->jx, player->jy))
4126 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4127 Debug("draw:DrawPlayerExt", "This should never happen!");
4129 draw_player[pnr] = FALSE;
4135 int last_player_graphic = static_last_player_graphic[pnr];
4136 int last_player_frame = static_last_player_frame[pnr];
4137 boolean player_is_opaque = static_player_is_opaque[pnr];
4139 int jx = player->jx;
4140 int jy = player->jy;
4141 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4142 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4143 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4144 int last_jx = (player->is_moving ? jx - dx : jx);
4145 int last_jy = (player->is_moving ? jy - dy : jy);
4146 int next_jx = jx + dx;
4147 int next_jy = jy + dy;
4148 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4149 int sx = SCREENX(jx);
4150 int sy = SCREENY(jy);
4151 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4152 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4153 int element = Tile[jx][jy];
4154 int last_element = Tile[last_jx][last_jy];
4155 int action = (player->is_pushing ? ACTION_PUSHING :
4156 player->is_digging ? ACTION_DIGGING :
4157 player->is_collecting ? ACTION_COLLECTING :
4158 player->is_moving ? ACTION_MOVING :
4159 player->is_snapping ? ACTION_SNAPPING :
4160 player->is_dropping ? ACTION_DROPPING :
4161 player->is_waiting ? player->action_waiting :
4164 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4166 // ------------------------------------------------------------------------
4167 // initialize drawing the player
4168 // ------------------------------------------------------------------------
4170 draw_player[pnr] = FALSE;
4172 // GfxElement[][] is set to the element the player is digging or collecting;
4173 // remove also for off-screen player if the player is not moving anymore
4174 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4175 GfxElement[jx][jy] = EL_UNDEFINED;
4177 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4180 if (element == EL_EXPLOSION)
4183 InitPlayerGfxAnimation(player, action, move_dir);
4185 draw_player[pnr] = TRUE;
4187 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4189 // ------------------------------------------------------------------------
4190 // draw things in the field the player is leaving, if needed
4191 // ------------------------------------------------------------------------
4193 if (!IN_SCR_FIELD(sx, sy))
4194 draw_player[pnr] = FALSE;
4196 if (!player->is_moving)
4199 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4201 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4203 if (last_element == EL_DYNAMITE_ACTIVE ||
4204 last_element == EL_EM_DYNAMITE_ACTIVE ||
4205 last_element == EL_SP_DISK_RED_ACTIVE)
4206 DrawDynamite(last_jx, last_jy);
4208 DrawLevelFieldThruMask(last_jx, last_jy);
4210 else if (last_element == EL_DYNAMITE_ACTIVE ||
4211 last_element == EL_EM_DYNAMITE_ACTIVE ||
4212 last_element == EL_SP_DISK_RED_ACTIVE)
4213 DrawDynamite(last_jx, last_jy);
4215 DrawLevelField(last_jx, last_jy);
4217 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4219 // ------------------------------------------------------------------------
4220 // draw things behind the player, if needed
4221 // ------------------------------------------------------------------------
4225 DrawLevelElement(jx, jy, Back[jx][jy]);
4230 if (IS_ACTIVE_BOMB(element))
4232 DrawLevelElement(jx, jy, EL_EMPTY);
4237 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4239 int old_element = GfxElement[jx][jy];
4240 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4241 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4243 if (GFX_CRUMBLED(old_element))
4244 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4246 DrawScreenGraphic(sx, sy, old_graphic, frame);
4248 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4249 static_player_is_opaque[pnr] = TRUE;
4253 GfxElement[jx][jy] = EL_UNDEFINED;
4255 // make sure that pushed elements are drawn with correct frame rate
4256 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4258 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4259 GfxFrame[jx][jy] = player->StepFrame;
4261 DrawLevelField(jx, jy);
4264 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4266 // ------------------------------------------------------------------------
4267 // draw things the player is pushing, if needed
4268 // ------------------------------------------------------------------------
4270 if (!player->is_pushing || !player->is_moving)
4273 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4276 int gfx_frame = GfxFrame[jx][jy];
4278 if (!IS_MOVING(jx, jy)) // push movement already finished
4280 element = Tile[next_jx][next_jy];
4281 gfx_frame = GfxFrame[next_jx][next_jy];
4284 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4285 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4286 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4288 // draw background element under pushed element (like the Sokoban field)
4289 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4291 // this allows transparent pushing animation over non-black background
4294 DrawLevelElement(jx, jy, Back[jx][jy]);
4296 DrawLevelElement(jx, jy, EL_EMPTY);
4299 if (Back[next_jx][next_jy])
4300 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4302 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4304 int px = SCREENX(jx), py = SCREENY(jy);
4305 int pxx = (TILEX - ABS(sxx)) * dx;
4306 int pyy = (TILEY - ABS(syy)) * dy;
4309 // do not draw (EM style) pushing animation when pushing is finished
4310 // (two-tile animations usually do not contain start and end frame)
4311 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4312 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4314 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4316 // masked drawing is needed for EMC style (double) movement graphics
4317 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4318 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4321 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4323 // ------------------------------------------------------------------------
4324 // draw player himself
4325 // ------------------------------------------------------------------------
4327 int graphic = getPlayerGraphic(player, move_dir);
4329 // in the case of changed player action or direction, prevent the current
4330 // animation frame from being restarted for identical animations
4331 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4332 player->Frame = last_player_frame;
4334 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4336 if (player_is_opaque)
4337 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4339 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4341 if (SHIELD_ON(player))
4343 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4344 IMG_SHIELD_NORMAL_ACTIVE);
4345 frame = getGraphicAnimationFrame(graphic, -1);
4347 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4350 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4352 // ------------------------------------------------------------------------
4353 // draw things in front of player (active dynamite or dynabombs)
4354 // ------------------------------------------------------------------------
4356 if (IS_ACTIVE_BOMB(element))
4358 int graphic = el2img(element);
4359 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4361 if (game.emulation == EMU_SUPAPLEX)
4362 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4364 DrawGraphicThruMask(sx, sy, graphic, frame);
4367 if (player_is_moving && last_element == EL_EXPLOSION)
4369 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4370 GfxElement[last_jx][last_jy] : EL_EMPTY);
4371 int graphic = el_act2img(element, ACTION_EXPLODING);
4372 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4373 int phase = ExplodePhase[last_jx][last_jy] - 1;
4374 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4377 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4380 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4382 // ------------------------------------------------------------------------
4383 // draw elements the player is just walking/passing through/under
4384 // ------------------------------------------------------------------------
4386 if (player_is_moving)
4388 // handle the field the player is leaving ...
4389 if (IS_ACCESSIBLE_INSIDE(last_element))
4390 DrawLevelField(last_jx, last_jy);
4391 else if (IS_ACCESSIBLE_UNDER(last_element))
4392 DrawLevelFieldThruMask(last_jx, last_jy);
4395 // do not redraw accessible elements if the player is just pushing them
4396 if (!player_is_moving || !player->is_pushing)
4398 // ... and the field the player is entering
4399 if (IS_ACCESSIBLE_INSIDE(element))
4400 DrawLevelField(jx, jy);
4401 else if (IS_ACCESSIBLE_UNDER(element))
4402 DrawLevelFieldThruMask(jx, jy);
4405 MarkTileDirty(sx, sy);
4409 void DrawPlayer(struct PlayerInfo *player)
4413 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4414 DrawPlayerExt(player, i);
4417 void DrawAllPlayers(void)
4421 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4422 for (j = 0; j < MAX_PLAYERS; j++)
4423 if (stored_player[j].active)
4424 DrawPlayerExt(&stored_player[j], i);
4427 void DrawPlayerField(int x, int y)
4429 if (!IS_PLAYER(x, y))
4432 DrawPlayer(PLAYERINFO(x, y));
4435 // ----------------------------------------------------------------------------
4437 void WaitForEventToContinue(void)
4439 boolean first_wait = TRUE;
4440 boolean still_wait = TRUE;
4442 if (program.headless)
4445 // simulate releasing mouse button over last gadget, if still pressed
4447 HandleGadgets(-1, -1, 0);
4449 button_status = MB_RELEASED;
4452 ClearPlayerAction();
4458 if (NextValidEvent(&event))
4462 case EVENT_BUTTONPRESS:
4463 case EVENT_FINGERPRESS:
4467 case EVENT_BUTTONRELEASE:
4468 case EVENT_FINGERRELEASE:
4469 still_wait = first_wait;
4472 case EVENT_KEYPRESS:
4473 case SDL_CONTROLLERBUTTONDOWN:
4474 case SDL_JOYBUTTONDOWN:
4479 HandleOtherEvents(&event);
4483 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4488 if (!PendingEvent())
4493 #define MAX_REQUEST_LINES 13
4494 #define MAX_REQUEST_LINE_FONT1_LEN 7
4495 #define MAX_REQUEST_LINE_FONT2_LEN 10
4497 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4499 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4501 int draw_buffer_last = GetDrawtoField();
4502 int width = request.width;
4503 int height = request.height;
4507 // when showing request dialog after game ended, deactivate game panel
4508 if (game_just_ended)
4509 game.panel.active = FALSE;
4511 setRequestPosition(&sx, &sy, FALSE);
4513 button_status = MB_RELEASED;
4515 request_gadget_id = -1;
4520 if (game_just_ended)
4522 SetDrawtoField(draw_buffer_game);
4524 HandleGameActions();
4526 SetDrawtoField(DRAW_TO_BACKBUFFER);
4533 while (NextValidEvent(&event))
4537 case EVENT_BUTTONPRESS:
4538 case EVENT_BUTTONRELEASE:
4539 case EVENT_MOTIONNOTIFY:
4541 DrawBuffer *drawto_last = drawto;
4544 if (event.type == EVENT_MOTIONNOTIFY)
4549 motion_status = TRUE;
4550 mx = ((MotionEvent *) &event)->x;
4551 my = ((MotionEvent *) &event)->y;
4555 motion_status = FALSE;
4556 mx = ((ButtonEvent *) &event)->x;
4557 my = ((ButtonEvent *) &event)->y;
4558 if (event.type == EVENT_BUTTONPRESS)
4559 button_status = ((ButtonEvent *) &event)->button;
4561 button_status = MB_RELEASED;
4564 if (global.use_envelope_request)
4566 // draw changed button states to temporary bitmap
4567 drawto = bitmap_db_store_1;
4570 // this sets 'request_gadget_id'
4571 HandleGadgets(mx, my, button_status);
4573 if (global.use_envelope_request)
4575 // restore pointer to drawing buffer
4576 drawto = drawto_last;
4578 // prepare complete envelope request from temporary bitmap
4579 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy,
4583 switch (request_gadget_id)
4585 case TOOL_CTRL_ID_YES:
4586 case TOOL_CTRL_ID_TOUCH_YES:
4589 case TOOL_CTRL_ID_NO:
4590 case TOOL_CTRL_ID_TOUCH_NO:
4593 case TOOL_CTRL_ID_CONFIRM:
4594 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4595 result = TRUE | FALSE;
4598 case TOOL_CTRL_ID_PLAYER_1:
4601 case TOOL_CTRL_ID_PLAYER_2:
4604 case TOOL_CTRL_ID_PLAYER_3:
4607 case TOOL_CTRL_ID_PLAYER_4:
4612 // only check clickable animations if no request gadget clicked
4613 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4620 case SDL_WINDOWEVENT:
4621 HandleWindowEvent((WindowEvent *) &event);
4624 case SDL_APP_WILLENTERBACKGROUND:
4625 case SDL_APP_DIDENTERBACKGROUND:
4626 case SDL_APP_WILLENTERFOREGROUND:
4627 case SDL_APP_DIDENTERFOREGROUND:
4628 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4631 case EVENT_KEYPRESS:
4633 Key key = GetEventKey((KeyEvent *)&event);
4638 if (req_state & REQ_CONFIRM)
4647 #if defined(KSYM_Rewind)
4648 case KSYM_Rewind: // for Amazon Fire TV remote
4657 #if defined(KSYM_FastForward)
4658 case KSYM_FastForward: // for Amazon Fire TV remote
4664 HandleKeysDebug(key, KEY_PRESSED);
4668 if (req_state & REQ_PLAYER)
4670 int old_player_nr = setup.network_player_nr;
4673 result = old_player_nr + 1;
4678 result = old_player_nr + 1;
4709 case EVENT_FINGERRELEASE:
4710 case EVENT_KEYRELEASE:
4711 ClearPlayerAction();
4714 case SDL_CONTROLLERBUTTONDOWN:
4715 switch (event.cbutton.button)
4717 case SDL_CONTROLLER_BUTTON_A:
4718 case SDL_CONTROLLER_BUTTON_X:
4719 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4720 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4724 case SDL_CONTROLLER_BUTTON_B:
4725 case SDL_CONTROLLER_BUTTON_Y:
4726 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4727 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4728 case SDL_CONTROLLER_BUTTON_BACK:
4733 if (req_state & REQ_PLAYER)
4735 int old_player_nr = setup.network_player_nr;
4738 result = old_player_nr + 1;
4740 switch (event.cbutton.button)
4742 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4743 case SDL_CONTROLLER_BUTTON_Y:
4747 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4748 case SDL_CONTROLLER_BUTTON_B:
4752 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4753 case SDL_CONTROLLER_BUTTON_A:
4757 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4758 case SDL_CONTROLLER_BUTTON_X:
4769 case SDL_CONTROLLERBUTTONUP:
4770 HandleJoystickEvent(&event);
4771 ClearPlayerAction();
4775 HandleOtherEvents(&event);
4780 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4782 int joy = AnyJoystick();
4784 if (joy & JOY_BUTTON_1)
4786 else if (joy & JOY_BUTTON_2)
4789 else if (AnyJoystick())
4791 int joy = AnyJoystick();
4793 if (req_state & REQ_PLAYER)
4797 else if (joy & JOY_RIGHT)
4799 else if (joy & JOY_DOWN)
4801 else if (joy & JOY_LEFT)
4809 SetDrawtoField(draw_buffer_last);
4814 static boolean RequestDoor(char *text, unsigned int req_state)
4816 int draw_buffer_last = GetDrawtoField();
4817 unsigned int old_door_state;
4818 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4819 int font_nr = FONT_TEXT_2;
4824 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4826 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4827 font_nr = FONT_TEXT_1;
4830 if (game_status == GAME_MODE_PLAYING)
4831 BlitScreenToBitmap(backbuffer);
4833 // disable deactivated drawing when quick-loading level tape recording
4834 if (tape.playing && tape.deactivate_display)
4835 TapeDeactivateDisplayOff(TRUE);
4837 SetMouseCursor(CURSOR_DEFAULT);
4839 // pause network game while waiting for request to answer
4840 if (network.enabled &&
4841 game_status == GAME_MODE_PLAYING &&
4842 !game.all_players_gone &&
4843 req_state & REQUEST_WAIT_FOR_INPUT)
4844 SendToServer_PausePlaying();
4846 old_door_state = GetDoorState();
4848 // simulate releasing mouse button over last gadget, if still pressed
4850 HandleGadgets(-1, -1, 0);
4854 // draw released gadget before proceeding
4857 if (old_door_state & DOOR_OPEN_1)
4859 CloseDoor(DOOR_CLOSE_1);
4861 // save old door content
4862 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4863 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4866 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4867 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4869 // clear door drawing field
4870 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4872 // force DOOR font inside door area
4873 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4875 // write text for request
4876 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4878 char text_line[max_request_line_len + 1];
4884 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4886 tc = *(text_ptr + tx);
4887 // if (!tc || tc == ' ')
4888 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4892 if ((tc == '?' || tc == '!') && tl == 0)
4902 strncpy(text_line, text_ptr, tl);
4905 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4906 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4907 text_line, font_nr);
4909 text_ptr += tl + (tc == ' ' ? 1 : 0);
4910 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4915 if (req_state & REQ_ASK)
4917 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4918 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4919 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4920 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4922 else if (req_state & REQ_CONFIRM)
4924 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4925 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4927 else if (req_state & REQ_PLAYER)
4929 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4930 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4931 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4932 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4935 // copy request gadgets to door backbuffer
4936 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4938 OpenDoor(DOOR_OPEN_1);
4940 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4942 if (game_status == GAME_MODE_PLAYING)
4944 SetPanelBackground();
4945 SetDrawBackgroundMask(REDRAW_DOOR_1);
4949 SetDrawBackgroundMask(REDRAW_FIELD);
4955 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4957 // ---------- handle request buttons ----------
4958 result = RequestHandleEvents(req_state, draw_buffer_last);
4962 if (!(req_state & REQ_STAY_OPEN))
4964 CloseDoor(DOOR_CLOSE_1);
4966 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4967 (req_state & REQ_REOPEN))
4968 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4973 if (game_status == GAME_MODE_PLAYING)
4975 SetPanelBackground();
4976 SetDrawBackgroundMask(REDRAW_DOOR_1);
4980 SetDrawBackgroundMask(REDRAW_FIELD);
4983 // continue network game after request
4984 if (network.enabled &&
4985 game_status == GAME_MODE_PLAYING &&
4986 !game.all_players_gone &&
4987 req_state & REQUEST_WAIT_FOR_INPUT)
4988 SendToServer_ContinuePlaying();
4990 // restore deactivated drawing when quick-loading level tape recording
4991 if (tape.playing && tape.deactivate_display)
4992 TapeDeactivateDisplayOn();
4997 static boolean RequestEnvelope(char *text, unsigned int req_state)
4999 int draw_buffer_last = GetDrawtoField();
5002 if (game_status == GAME_MODE_PLAYING)
5003 BlitScreenToBitmap(backbuffer);
5005 // disable deactivated drawing when quick-loading level tape recording
5006 if (tape.playing && tape.deactivate_display)
5007 TapeDeactivateDisplayOff(TRUE);
5009 SetMouseCursor(CURSOR_DEFAULT);
5011 // pause network game while waiting for request to answer
5012 if (network.enabled &&
5013 game_status == GAME_MODE_PLAYING &&
5014 !game.all_players_gone &&
5015 req_state & REQUEST_WAIT_FOR_INPUT)
5016 SendToServer_PausePlaying();
5018 // simulate releasing mouse button over last gadget, if still pressed
5020 HandleGadgets(-1, -1, 0);
5024 // (replace with setting corresponding request background)
5025 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5026 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5028 // clear door drawing field
5029 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5031 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5033 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5035 if (game_status == GAME_MODE_PLAYING)
5037 SetPanelBackground();
5038 SetDrawBackgroundMask(REDRAW_DOOR_1);
5042 SetDrawBackgroundMask(REDRAW_FIELD);
5048 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5050 // ---------- handle request buttons ----------
5051 result = RequestHandleEvents(req_state, draw_buffer_last);
5055 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5059 if (game_status == GAME_MODE_PLAYING)
5061 SetPanelBackground();
5062 SetDrawBackgroundMask(REDRAW_DOOR_1);
5066 SetDrawBackgroundMask(REDRAW_FIELD);
5069 // continue network game after request
5070 if (network.enabled &&
5071 game_status == GAME_MODE_PLAYING &&
5072 !game.all_players_gone &&
5073 req_state & REQUEST_WAIT_FOR_INPUT)
5074 SendToServer_ContinuePlaying();
5076 // restore deactivated drawing when quick-loading level tape recording
5077 if (tape.playing && tape.deactivate_display)
5078 TapeDeactivateDisplayOn();
5083 boolean Request(char *text, unsigned int req_state)
5085 boolean overlay_enabled = GetOverlayEnabled();
5088 game.request_active = TRUE;
5090 SetOverlayEnabled(FALSE);
5092 if (global.use_envelope_request)
5093 result = RequestEnvelope(text, req_state);
5095 result = RequestDoor(text, req_state);
5097 SetOverlayEnabled(overlay_enabled);
5099 game.request_active = FALSE;
5104 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5106 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5107 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5110 if (dpo1->sort_priority != dpo2->sort_priority)
5111 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5113 compare_result = dpo1->nr - dpo2->nr;
5115 return compare_result;
5118 void InitGraphicCompatibilityInfo_Doors(void)
5124 struct DoorInfo *door;
5128 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5129 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5131 { -1, -1, -1, NULL }
5133 struct Rect door_rect_list[] =
5135 { DX, DY, DXSIZE, DYSIZE },
5136 { VX, VY, VXSIZE, VYSIZE }
5140 for (i = 0; doors[i].door_token != -1; i++)
5142 int door_token = doors[i].door_token;
5143 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5144 int part_1 = doors[i].part_1;
5145 int part_8 = doors[i].part_8;
5146 int part_2 = part_1 + 1;
5147 int part_3 = part_1 + 2;
5148 struct DoorInfo *door = doors[i].door;
5149 struct Rect *door_rect = &door_rect_list[door_index];
5150 boolean door_gfx_redefined = FALSE;
5152 // check if any door part graphic definitions have been redefined
5154 for (j = 0; door_part_controls[j].door_token != -1; j++)
5156 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5157 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5159 if (dpc->door_token == door_token && fi->redefined)
5160 door_gfx_redefined = TRUE;
5163 // check for old-style door graphic/animation modifications
5165 if (!door_gfx_redefined)
5167 if (door->anim_mode & ANIM_STATIC_PANEL)
5169 door->panel.step_xoffset = 0;
5170 door->panel.step_yoffset = 0;
5173 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5175 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5176 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5177 int num_door_steps, num_panel_steps;
5179 // remove door part graphics other than the two default wings
5181 for (j = 0; door_part_controls[j].door_token != -1; j++)
5183 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5184 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5186 if (dpc->graphic >= part_3 &&
5187 dpc->graphic <= part_8)
5191 // set graphics and screen positions of the default wings
5193 g_part_1->width = door_rect->width;
5194 g_part_1->height = door_rect->height;
5195 g_part_2->width = door_rect->width;
5196 g_part_2->height = door_rect->height;
5197 g_part_2->src_x = door_rect->width;
5198 g_part_2->src_y = g_part_1->src_y;
5200 door->part_2.x = door->part_1.x;
5201 door->part_2.y = door->part_1.y;
5203 if (door->width != -1)
5205 g_part_1->width = door->width;
5206 g_part_2->width = door->width;
5208 // special treatment for graphics and screen position of right wing
5209 g_part_2->src_x += door_rect->width - door->width;
5210 door->part_2.x += door_rect->width - door->width;
5213 if (door->height != -1)
5215 g_part_1->height = door->height;
5216 g_part_2->height = door->height;
5218 // special treatment for graphics and screen position of bottom wing
5219 g_part_2->src_y += door_rect->height - door->height;
5220 door->part_2.y += door_rect->height - door->height;
5223 // set animation delays for the default wings and panels
5225 door->part_1.step_delay = door->step_delay;
5226 door->part_2.step_delay = door->step_delay;
5227 door->panel.step_delay = door->step_delay;
5229 // set animation draw order for the default wings
5231 door->part_1.sort_priority = 2; // draw left wing over ...
5232 door->part_2.sort_priority = 1; // ... right wing
5234 // set animation draw offset for the default wings
5236 if (door->anim_mode & ANIM_HORIZONTAL)
5238 door->part_1.step_xoffset = door->step_offset;
5239 door->part_1.step_yoffset = 0;
5240 door->part_2.step_xoffset = door->step_offset * -1;
5241 door->part_2.step_yoffset = 0;
5243 num_door_steps = g_part_1->width / door->step_offset;
5245 else // ANIM_VERTICAL
5247 door->part_1.step_xoffset = 0;
5248 door->part_1.step_yoffset = door->step_offset;
5249 door->part_2.step_xoffset = 0;
5250 door->part_2.step_yoffset = door->step_offset * -1;
5252 num_door_steps = g_part_1->height / door->step_offset;
5255 // set animation draw offset for the default panels
5257 if (door->step_offset > 1)
5259 num_panel_steps = 2 * door_rect->height / door->step_offset;
5260 door->panel.start_step = num_panel_steps - num_door_steps;
5261 door->panel.start_step_closing = door->panel.start_step;
5265 num_panel_steps = door_rect->height / door->step_offset;
5266 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5267 door->panel.start_step_closing = door->panel.start_step;
5268 door->panel.step_delay *= 2;
5275 void InitDoors(void)
5279 for (i = 0; door_part_controls[i].door_token != -1; i++)
5281 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5282 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5284 // initialize "start_step_opening" and "start_step_closing", if needed
5285 if (dpc->pos->start_step_opening == 0 &&
5286 dpc->pos->start_step_closing == 0)
5288 // dpc->pos->start_step_opening = dpc->pos->start_step;
5289 dpc->pos->start_step_closing = dpc->pos->start_step;
5292 // fill structure for door part draw order (sorted below)
5294 dpo->sort_priority = dpc->pos->sort_priority;
5297 // sort door part controls according to sort_priority and graphic number
5298 qsort(door_part_order, MAX_DOOR_PARTS,
5299 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5302 unsigned int OpenDoor(unsigned int door_state)
5304 if (door_state & DOOR_COPY_BACK)
5306 if (door_state & DOOR_OPEN_1)
5307 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5308 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5310 if (door_state & DOOR_OPEN_2)
5311 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5312 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5314 door_state &= ~DOOR_COPY_BACK;
5317 return MoveDoor(door_state);
5320 unsigned int CloseDoor(unsigned int door_state)
5322 unsigned int old_door_state = GetDoorState();
5324 if (!(door_state & DOOR_NO_COPY_BACK))
5326 if (old_door_state & DOOR_OPEN_1)
5327 BlitBitmap(backbuffer, bitmap_db_door_1,
5328 DX, DY, DXSIZE, DYSIZE, 0, 0);
5330 if (old_door_state & DOOR_OPEN_2)
5331 BlitBitmap(backbuffer, bitmap_db_door_2,
5332 VX, VY, VXSIZE, VYSIZE, 0, 0);
5334 door_state &= ~DOOR_NO_COPY_BACK;
5337 return MoveDoor(door_state);
5340 unsigned int GetDoorState(void)
5342 return MoveDoor(DOOR_GET_STATE);
5345 unsigned int SetDoorState(unsigned int door_state)
5347 return MoveDoor(door_state | DOOR_SET_STATE);
5350 static int euclid(int a, int b)
5352 return (b ? euclid(b, a % b) : a);
5355 unsigned int MoveDoor(unsigned int door_state)
5357 struct Rect door_rect_list[] =
5359 { DX, DY, DXSIZE, DYSIZE },
5360 { VX, VY, VXSIZE, VYSIZE }
5362 static int door1 = DOOR_CLOSE_1;
5363 static int door2 = DOOR_CLOSE_2;
5364 DelayCounter door_delay = { 0 };
5367 if (door_state == DOOR_GET_STATE)
5368 return (door1 | door2);
5370 if (door_state & DOOR_SET_STATE)
5372 if (door_state & DOOR_ACTION_1)
5373 door1 = door_state & DOOR_ACTION_1;
5374 if (door_state & DOOR_ACTION_2)
5375 door2 = door_state & DOOR_ACTION_2;
5377 return (door1 | door2);
5380 if (!(door_state & DOOR_FORCE_REDRAW))
5382 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5383 door_state &= ~DOOR_OPEN_1;
5384 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5385 door_state &= ~DOOR_CLOSE_1;
5386 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5387 door_state &= ~DOOR_OPEN_2;
5388 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5389 door_state &= ~DOOR_CLOSE_2;
5392 if (global.autoplay_leveldir)
5394 door_state |= DOOR_NO_DELAY;
5395 door_state &= ~DOOR_CLOSE_ALL;
5398 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5399 door_state |= DOOR_NO_DELAY;
5401 if (door_state & DOOR_ACTION)
5403 boolean door_panel_drawn[NUM_DOORS];
5404 boolean panel_has_doors[NUM_DOORS];
5405 boolean door_part_skip[MAX_DOOR_PARTS];
5406 boolean door_part_done[MAX_DOOR_PARTS];
5407 boolean door_part_done_all;
5408 int num_steps[MAX_DOOR_PARTS];
5409 int max_move_delay = 0; // delay for complete animations of all doors
5410 int max_step_delay = 0; // delay (ms) between two animation frames
5411 int num_move_steps = 0; // number of animation steps for all doors
5412 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5413 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5417 for (i = 0; i < NUM_DOORS; i++)
5418 panel_has_doors[i] = FALSE;
5420 for (i = 0; i < MAX_DOOR_PARTS; i++)
5422 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5423 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5424 int door_token = dpc->door_token;
5426 door_part_done[i] = FALSE;
5427 door_part_skip[i] = (!(door_state & door_token) ||
5431 for (i = 0; i < MAX_DOOR_PARTS; i++)
5433 int nr = door_part_order[i].nr;
5434 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5435 struct DoorPartPosInfo *pos = dpc->pos;
5436 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5437 int door_token = dpc->door_token;
5438 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5439 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5440 int step_xoffset = ABS(pos->step_xoffset);
5441 int step_yoffset = ABS(pos->step_yoffset);
5442 int step_delay = pos->step_delay;
5443 int current_door_state = door_state & door_token;
5444 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5445 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5446 boolean part_opening = (is_panel ? door_closing : door_opening);
5447 int start_step = (part_opening ? pos->start_step_opening :
5448 pos->start_step_closing);
5449 float move_xsize = (step_xoffset ? g->width : 0);
5450 float move_ysize = (step_yoffset ? g->height : 0);
5451 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5452 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5453 int move_steps = (move_xsteps && move_ysteps ?
5454 MIN(move_xsteps, move_ysteps) :
5455 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5456 int move_delay = move_steps * step_delay;
5458 if (door_part_skip[nr])
5461 max_move_delay = MAX(max_move_delay, move_delay);
5462 max_step_delay = (max_step_delay == 0 ? step_delay :
5463 euclid(max_step_delay, step_delay));
5464 num_steps[nr] = move_steps;
5468 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5470 panel_has_doors[door_index] = TRUE;
5474 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5476 num_move_steps = max_move_delay / max_step_delay;
5477 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5479 door_delay.value = max_step_delay;
5481 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5483 start = num_move_steps - 1;
5487 // opening door sound has priority over simultaneously closing door
5488 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5490 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5492 if (door_state & DOOR_OPEN_1)
5493 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5494 if (door_state & DOOR_OPEN_2)
5495 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5497 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5499 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5501 if (door_state & DOOR_CLOSE_1)
5502 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5503 if (door_state & DOOR_CLOSE_2)
5504 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5508 for (k = start; k < num_move_steps; k++)
5510 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5512 door_part_done_all = TRUE;
5514 for (i = 0; i < NUM_DOORS; i++)
5515 door_panel_drawn[i] = FALSE;
5517 for (i = 0; i < MAX_DOOR_PARTS; i++)
5519 int nr = door_part_order[i].nr;
5520 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5521 struct DoorPartPosInfo *pos = dpc->pos;
5522 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5523 int door_token = dpc->door_token;
5524 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5525 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5526 boolean is_panel_and_door_has_closed = FALSE;
5527 struct Rect *door_rect = &door_rect_list[door_index];
5528 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5530 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5531 int current_door_state = door_state & door_token;
5532 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5533 boolean door_closing = !door_opening;
5534 boolean part_opening = (is_panel ? door_closing : door_opening);
5535 boolean part_closing = !part_opening;
5536 int start_step = (part_opening ? pos->start_step_opening :
5537 pos->start_step_closing);
5538 int step_delay = pos->step_delay;
5539 int step_factor = step_delay / max_step_delay;
5540 int k1 = (step_factor ? k / step_factor + 1 : k);
5541 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5542 int kk = MAX(0, k2);
5545 int src_x, src_y, src_xx, src_yy;
5546 int dst_x, dst_y, dst_xx, dst_yy;
5549 if (door_part_skip[nr])
5552 if (!(door_state & door_token))
5560 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5561 int kk_door = MAX(0, k2_door);
5562 int sync_frame = kk_door * door_delay.value;
5563 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5565 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5566 &g_src_x, &g_src_y);
5571 if (!door_panel_drawn[door_index])
5573 ClearRectangle(drawto, door_rect->x, door_rect->y,
5574 door_rect->width, door_rect->height);
5576 door_panel_drawn[door_index] = TRUE;
5579 // draw opening or closing door parts
5581 if (pos->step_xoffset < 0) // door part on right side
5584 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5587 if (dst_xx + width > door_rect->width)
5588 width = door_rect->width - dst_xx;
5590 else // door part on left side
5593 dst_xx = pos->x - kk * pos->step_xoffset;
5597 src_xx = ABS(dst_xx);
5601 width = g->width - src_xx;
5603 if (width > door_rect->width)
5604 width = door_rect->width;
5606 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5609 if (pos->step_yoffset < 0) // door part on bottom side
5612 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5615 if (dst_yy + height > door_rect->height)
5616 height = door_rect->height - dst_yy;
5618 else // door part on top side
5621 dst_yy = pos->y - kk * pos->step_yoffset;
5625 src_yy = ABS(dst_yy);
5629 height = g->height - src_yy;
5632 src_x = g_src_x + src_xx;
5633 src_y = g_src_y + src_yy;
5635 dst_x = door_rect->x + dst_xx;
5636 dst_y = door_rect->y + dst_yy;
5638 is_panel_and_door_has_closed =
5641 panel_has_doors[door_index] &&
5642 k >= num_move_steps_doors_only - 1);
5644 if (width >= 0 && width <= g->width &&
5645 height >= 0 && height <= g->height &&
5646 !is_panel_and_door_has_closed)
5648 if (is_panel || !pos->draw_masked)
5649 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5652 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5656 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5658 if ((part_opening && (width < 0 || height < 0)) ||
5659 (part_closing && (width >= g->width && height >= g->height)))
5660 door_part_done[nr] = TRUE;
5662 // continue door part animations, but not panel after door has closed
5663 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5664 door_part_done_all = FALSE;
5667 if (!(door_state & DOOR_NO_DELAY))
5671 SkipUntilDelayReached(&door_delay, &k, last_frame);
5673 // prevent OS (Windows) from complaining about program not responding
5677 if (door_part_done_all)
5681 if (!(door_state & DOOR_NO_DELAY))
5683 // wait for specified door action post delay
5684 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5685 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5686 else if (door_state & DOOR_ACTION_1)
5687 door_delay.value = door_1.post_delay;
5688 else if (door_state & DOOR_ACTION_2)
5689 door_delay.value = door_2.post_delay;
5691 while (!DelayReached(&door_delay))
5696 if (door_state & DOOR_ACTION_1)
5697 door1 = door_state & DOOR_ACTION_1;
5698 if (door_state & DOOR_ACTION_2)
5699 door2 = door_state & DOOR_ACTION_2;
5701 // draw masked border over door area
5702 DrawMaskedBorder(REDRAW_DOOR_1);
5703 DrawMaskedBorder(REDRAW_DOOR_2);
5705 ClearAutoRepeatKeyEvents();
5707 return (door1 | door2);
5710 static boolean useSpecialEditorDoor(void)
5712 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5713 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5715 // do not draw special editor door if editor border defined or redefined
5716 if (graphic_info[graphic].bitmap != NULL || redefined)
5719 // do not draw special editor door if global border defined to be empty
5720 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5723 // do not draw special editor door if viewport definitions do not match
5727 EY + EYSIZE != VY + VYSIZE)
5733 void DrawSpecialEditorDoor(void)
5735 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5736 int top_border_width = gfx1->width;
5737 int top_border_height = gfx1->height;
5738 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5739 int ex = EX - outer_border;
5740 int ey = EY - outer_border;
5741 int vy = VY - outer_border;
5742 int exsize = EXSIZE + 2 * outer_border;
5744 if (!useSpecialEditorDoor())
5747 // draw bigger level editor toolbox window
5748 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5749 top_border_width, top_border_height, ex, ey - top_border_height);
5750 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5751 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5753 redraw_mask |= REDRAW_ALL;
5756 void UndrawSpecialEditorDoor(void)
5758 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5759 int top_border_width = gfx1->width;
5760 int top_border_height = gfx1->height;
5761 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5762 int ex = EX - outer_border;
5763 int ey = EY - outer_border;
5764 int ey_top = ey - top_border_height;
5765 int exsize = EXSIZE + 2 * outer_border;
5766 int eysize = EYSIZE + 2 * outer_border;
5768 if (!useSpecialEditorDoor())
5771 // draw normal tape recorder window
5772 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5774 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5775 ex, ey_top, top_border_width, top_border_height,
5777 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5778 ex, ey, exsize, eysize, ex, ey);
5782 // if screen background is set to "[NONE]", clear editor toolbox window
5783 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5784 ClearRectangle(drawto, ex, ey, exsize, eysize);
5787 redraw_mask |= REDRAW_ALL;
5791 // ---------- new tool button stuff -------------------------------------------
5796 struct TextPosInfo *pos;
5798 boolean is_touch_button;
5800 } toolbutton_info[NUM_TOOL_BUTTONS] =
5803 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5804 TOOL_CTRL_ID_YES, FALSE, "yes"
5807 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5808 TOOL_CTRL_ID_NO, FALSE, "no"
5811 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5812 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5815 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5816 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5819 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5820 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5823 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5824 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5827 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5828 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5831 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5832 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5835 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5836 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5839 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5840 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5844 void CreateToolButtons(void)
5848 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5850 int graphic = toolbutton_info[i].graphic;
5851 struct GraphicInfo *gfx = &graphic_info[graphic];
5852 struct TextPosInfo *pos = toolbutton_info[i].pos;
5853 struct GadgetInfo *gi;
5854 Bitmap *deco_bitmap = None;
5855 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5856 unsigned int event_mask = GD_EVENT_RELEASED;
5857 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5858 int base_x = (is_touch_button ? 0 : DX);
5859 int base_y = (is_touch_button ? 0 : DY);
5860 int gd_x = gfx->src_x;
5861 int gd_y = gfx->src_y;
5862 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5863 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5868 // do not use touch buttons if overlay touch buttons are disabled
5869 if (is_touch_button && !setup.touch.overlay_buttons)
5872 if (global.use_envelope_request && !is_touch_button)
5874 setRequestPosition(&base_x, &base_y, TRUE);
5876 // check if request buttons are outside of envelope and fix, if needed
5877 if (x < 0 || x + gfx->width > request.width ||
5878 y < 0 || y + gfx->height > request.height)
5880 if (id == TOOL_CTRL_ID_YES)
5883 y = request.height - 2 * request.border_size - gfx->height;
5885 else if (id == TOOL_CTRL_ID_NO)
5887 x = request.width - 2 * request.border_size - gfx->width;
5888 y = request.height - 2 * request.border_size - gfx->height;
5890 else if (id == TOOL_CTRL_ID_CONFIRM)
5892 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5893 y = request.height - 2 * request.border_size - gfx->height;
5895 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5897 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5899 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5900 y = request.height - 2 * request.border_size - gfx->height * 2;
5902 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5903 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5908 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5911 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5913 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5914 pos->size, &deco_bitmap, &deco_x, &deco_y);
5915 deco_xpos = (gfx->width - pos->size) / 2;
5916 deco_ypos = (gfx->height - pos->size) / 2;
5919 gi = CreateGadget(GDI_CUSTOM_ID, id,
5920 GDI_IMAGE_ID, graphic,
5921 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5924 GDI_WIDTH, gfx->width,
5925 GDI_HEIGHT, gfx->height,
5926 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5927 GDI_STATE, GD_BUTTON_UNPRESSED,
5928 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5929 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5930 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5931 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5932 GDI_DECORATION_SIZE, pos->size, pos->size,
5933 GDI_DECORATION_SHIFTING, 1, 1,
5934 GDI_DIRECT_DRAW, FALSE,
5935 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5936 GDI_EVENT_MASK, event_mask,
5937 GDI_CALLBACK_ACTION, HandleToolButtons,
5941 Fail("cannot create gadget");
5943 tool_gadget[id] = gi;
5947 void FreeToolButtons(void)
5951 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5952 FreeGadget(tool_gadget[i]);
5955 static void UnmapToolButtons(void)
5959 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5960 UnmapGadget(tool_gadget[i]);
5963 static void HandleToolButtons(struct GadgetInfo *gi)
5965 request_gadget_id = gi->custom_id;
5968 static struct Mapping_EM_to_RND_object
5971 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5972 boolean is_backside; // backside of moving element
5978 em_object_mapping_list[GAME_TILE_MAX + 1] =
5981 Zborder, FALSE, FALSE,
5985 Zplayer, FALSE, FALSE,
5994 Ztank, FALSE, FALSE,
5998 Zeater, FALSE, FALSE,
6002 Zdynamite, FALSE, FALSE,
6006 Zboom, FALSE, FALSE,
6011 Xchain, FALSE, FALSE,
6012 EL_DEFAULT, ACTION_EXPLODING, -1
6015 Xboom_bug, FALSE, FALSE,
6016 EL_BUG, ACTION_EXPLODING, -1
6019 Xboom_tank, FALSE, FALSE,
6020 EL_SPACESHIP, ACTION_EXPLODING, -1
6023 Xboom_android, FALSE, FALSE,
6024 EL_EMC_ANDROID, ACTION_OTHER, -1
6027 Xboom_1, FALSE, FALSE,
6028 EL_DEFAULT, ACTION_EXPLODING, -1
6031 Xboom_2, FALSE, FALSE,
6032 EL_DEFAULT, ACTION_EXPLODING, -1
6036 Xblank, TRUE, FALSE,
6041 Xsplash_e, FALSE, FALSE,
6042 EL_ACID_SPLASH_RIGHT, -1, -1
6045 Xsplash_w, FALSE, FALSE,
6046 EL_ACID_SPLASH_LEFT, -1, -1
6050 Xplant, TRUE, FALSE,
6051 EL_EMC_PLANT, -1, -1
6054 Yplant, FALSE, FALSE,
6055 EL_EMC_PLANT, -1, -1
6059 Xacid_1, TRUE, FALSE,
6063 Xacid_2, FALSE, FALSE,
6067 Xacid_3, FALSE, FALSE,
6071 Xacid_4, FALSE, FALSE,
6075 Xacid_5, FALSE, FALSE,
6079 Xacid_6, FALSE, FALSE,
6083 Xacid_7, FALSE, FALSE,
6087 Xacid_8, FALSE, FALSE,
6092 Xfake_acid_1, TRUE, FALSE,
6093 EL_EMC_FAKE_ACID, -1, -1
6096 Xfake_acid_2, FALSE, FALSE,
6097 EL_EMC_FAKE_ACID, -1, -1
6100 Xfake_acid_3, FALSE, FALSE,
6101 EL_EMC_FAKE_ACID, -1, -1
6104 Xfake_acid_4, FALSE, FALSE,
6105 EL_EMC_FAKE_ACID, -1, -1
6108 Xfake_acid_5, FALSE, FALSE,
6109 EL_EMC_FAKE_ACID, -1, -1
6112 Xfake_acid_6, FALSE, FALSE,
6113 EL_EMC_FAKE_ACID, -1, -1
6116 Xfake_acid_7, FALSE, FALSE,
6117 EL_EMC_FAKE_ACID, -1, -1
6120 Xfake_acid_8, FALSE, FALSE,
6121 EL_EMC_FAKE_ACID, -1, -1
6125 Xfake_acid_1_player, FALSE, FALSE,
6126 EL_EMC_FAKE_ACID, -1, -1
6129 Xfake_acid_2_player, FALSE, FALSE,
6130 EL_EMC_FAKE_ACID, -1, -1
6133 Xfake_acid_3_player, FALSE, FALSE,
6134 EL_EMC_FAKE_ACID, -1, -1
6137 Xfake_acid_4_player, FALSE, FALSE,
6138 EL_EMC_FAKE_ACID, -1, -1
6141 Xfake_acid_5_player, FALSE, FALSE,
6142 EL_EMC_FAKE_ACID, -1, -1
6145 Xfake_acid_6_player, FALSE, FALSE,
6146 EL_EMC_FAKE_ACID, -1, -1
6149 Xfake_acid_7_player, FALSE, FALSE,
6150 EL_EMC_FAKE_ACID, -1, -1
6153 Xfake_acid_8_player, FALSE, FALSE,
6154 EL_EMC_FAKE_ACID, -1, -1
6158 Xgrass, TRUE, FALSE,
6159 EL_EMC_GRASS, -1, -1
6162 Ygrass_nB, FALSE, FALSE,
6163 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6166 Ygrass_eB, FALSE, FALSE,
6167 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6170 Ygrass_sB, FALSE, FALSE,
6171 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6174 Ygrass_wB, FALSE, FALSE,
6175 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6183 Ydirt_nB, FALSE, FALSE,
6184 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6187 Ydirt_eB, FALSE, FALSE,
6188 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6191 Ydirt_sB, FALSE, FALSE,
6192 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6195 Ydirt_wB, FALSE, FALSE,
6196 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6200 Xandroid, TRUE, FALSE,
6201 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6204 Xandroid_1_n, FALSE, FALSE,
6205 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6208 Xandroid_2_n, FALSE, FALSE,
6209 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6212 Xandroid_1_e, FALSE, FALSE,
6213 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6216 Xandroid_2_e, FALSE, FALSE,
6217 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6220 Xandroid_1_w, FALSE, FALSE,
6221 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6224 Xandroid_2_w, FALSE, FALSE,
6225 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6228 Xandroid_1_s, FALSE, FALSE,
6229 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6232 Xandroid_2_s, FALSE, FALSE,
6233 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6236 Yandroid_n, FALSE, FALSE,
6237 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6240 Yandroid_nB, FALSE, TRUE,
6241 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6244 Yandroid_ne, FALSE, FALSE,
6245 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6248 Yandroid_neB, FALSE, TRUE,
6249 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6252 Yandroid_e, FALSE, FALSE,
6253 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6256 Yandroid_eB, FALSE, TRUE,
6257 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6260 Yandroid_se, FALSE, FALSE,
6261 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6264 Yandroid_seB, FALSE, TRUE,
6265 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6268 Yandroid_s, FALSE, FALSE,
6269 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6272 Yandroid_sB, FALSE, TRUE,
6273 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6276 Yandroid_sw, FALSE, FALSE,
6277 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6280 Yandroid_swB, FALSE, TRUE,
6281 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6284 Yandroid_w, FALSE, FALSE,
6285 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6288 Yandroid_wB, FALSE, TRUE,
6289 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6292 Yandroid_nw, FALSE, FALSE,
6293 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6296 Yandroid_nwB, FALSE, TRUE,
6297 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6301 Xeater_n, TRUE, FALSE,
6302 EL_YAMYAM_UP, -1, -1
6305 Xeater_e, TRUE, FALSE,
6306 EL_YAMYAM_RIGHT, -1, -1
6309 Xeater_w, TRUE, FALSE,
6310 EL_YAMYAM_LEFT, -1, -1
6313 Xeater_s, TRUE, FALSE,
6314 EL_YAMYAM_DOWN, -1, -1
6317 Yeater_n, FALSE, FALSE,
6318 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6321 Yeater_nB, FALSE, TRUE,
6322 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6325 Yeater_e, FALSE, FALSE,
6326 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6329 Yeater_eB, FALSE, TRUE,
6330 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6333 Yeater_s, FALSE, FALSE,
6334 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6337 Yeater_sB, FALSE, TRUE,
6338 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6341 Yeater_w, FALSE, FALSE,
6342 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6345 Yeater_wB, FALSE, TRUE,
6346 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6349 Yeater_stone, FALSE, FALSE,
6350 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6353 Yeater_spring, FALSE, FALSE,
6354 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6358 Xalien, TRUE, FALSE,
6362 Xalien_pause, FALSE, FALSE,
6366 Yalien_n, FALSE, FALSE,
6367 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6370 Yalien_nB, FALSE, TRUE,
6371 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6374 Yalien_e, FALSE, FALSE,
6375 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6378 Yalien_eB, FALSE, TRUE,
6379 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6382 Yalien_s, FALSE, FALSE,
6383 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6386 Yalien_sB, FALSE, TRUE,
6387 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6390 Yalien_w, FALSE, FALSE,
6391 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6394 Yalien_wB, FALSE, TRUE,
6395 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6398 Yalien_stone, FALSE, FALSE,
6399 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6402 Yalien_spring, FALSE, FALSE,
6403 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6407 Xbug_1_n, TRUE, FALSE,
6411 Xbug_1_e, TRUE, FALSE,
6412 EL_BUG_RIGHT, -1, -1
6415 Xbug_1_s, TRUE, FALSE,
6419 Xbug_1_w, TRUE, FALSE,
6423 Xbug_2_n, FALSE, FALSE,
6427 Xbug_2_e, FALSE, FALSE,
6428 EL_BUG_RIGHT, -1, -1
6431 Xbug_2_s, FALSE, FALSE,
6435 Xbug_2_w, FALSE, FALSE,
6439 Ybug_n, FALSE, FALSE,
6440 EL_BUG, ACTION_MOVING, MV_BIT_UP
6443 Ybug_nB, FALSE, TRUE,
6444 EL_BUG, ACTION_MOVING, MV_BIT_UP
6447 Ybug_e, FALSE, FALSE,
6448 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6451 Ybug_eB, FALSE, TRUE,
6452 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6455 Ybug_s, FALSE, FALSE,
6456 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6459 Ybug_sB, FALSE, TRUE,
6460 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6463 Ybug_w, FALSE, FALSE,
6464 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6467 Ybug_wB, FALSE, TRUE,
6468 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6471 Ybug_w_n, FALSE, FALSE,
6472 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6475 Ybug_n_e, FALSE, FALSE,
6476 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6479 Ybug_e_s, FALSE, FALSE,
6480 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6483 Ybug_s_w, FALSE, FALSE,
6484 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6487 Ybug_e_n, FALSE, FALSE,
6488 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6491 Ybug_s_e, FALSE, FALSE,
6492 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6495 Ybug_w_s, FALSE, FALSE,
6496 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6499 Ybug_n_w, FALSE, FALSE,
6500 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6503 Ybug_stone, FALSE, FALSE,
6504 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6507 Ybug_spring, FALSE, FALSE,
6508 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6512 Xtank_1_n, TRUE, FALSE,
6513 EL_SPACESHIP_UP, -1, -1
6516 Xtank_1_e, TRUE, FALSE,
6517 EL_SPACESHIP_RIGHT, -1, -1
6520 Xtank_1_s, TRUE, FALSE,
6521 EL_SPACESHIP_DOWN, -1, -1
6524 Xtank_1_w, TRUE, FALSE,
6525 EL_SPACESHIP_LEFT, -1, -1
6528 Xtank_2_n, FALSE, FALSE,
6529 EL_SPACESHIP_UP, -1, -1
6532 Xtank_2_e, FALSE, FALSE,
6533 EL_SPACESHIP_RIGHT, -1, -1
6536 Xtank_2_s, FALSE, FALSE,
6537 EL_SPACESHIP_DOWN, -1, -1
6540 Xtank_2_w, FALSE, FALSE,
6541 EL_SPACESHIP_LEFT, -1, -1
6544 Ytank_n, FALSE, FALSE,
6545 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6548 Ytank_nB, FALSE, TRUE,
6549 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6552 Ytank_e, FALSE, FALSE,
6553 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6556 Ytank_eB, FALSE, TRUE,
6557 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6560 Ytank_s, FALSE, FALSE,
6561 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6564 Ytank_sB, FALSE, TRUE,
6565 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6568 Ytank_w, FALSE, FALSE,
6569 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6572 Ytank_wB, FALSE, TRUE,
6573 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6576 Ytank_w_n, FALSE, FALSE,
6577 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6580 Ytank_n_e, FALSE, FALSE,
6581 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6584 Ytank_e_s, FALSE, FALSE,
6585 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6588 Ytank_s_w, FALSE, FALSE,
6589 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6592 Ytank_e_n, FALSE, FALSE,
6593 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6596 Ytank_s_e, FALSE, FALSE,
6597 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6600 Ytank_w_s, FALSE, FALSE,
6601 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6604 Ytank_n_w, FALSE, FALSE,
6605 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6608 Ytank_stone, FALSE, FALSE,
6609 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6612 Ytank_spring, FALSE, FALSE,
6613 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6617 Xemerald, TRUE, FALSE,
6621 Xemerald_pause, FALSE, FALSE,
6625 Xemerald_fall, FALSE, FALSE,
6629 Xemerald_shine, FALSE, FALSE,
6630 EL_EMERALD, ACTION_TWINKLING, -1
6633 Yemerald_s, FALSE, FALSE,
6634 EL_EMERALD, ACTION_FALLING, -1
6637 Yemerald_sB, FALSE, TRUE,
6638 EL_EMERALD, ACTION_FALLING, -1
6641 Yemerald_e, FALSE, FALSE,
6642 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6645 Yemerald_eB, FALSE, TRUE,
6646 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6649 Yemerald_w, FALSE, FALSE,
6650 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6653 Yemerald_wB, FALSE, TRUE,
6654 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6657 Yemerald_blank, FALSE, FALSE,
6658 EL_EMERALD, ACTION_COLLECTING, -1
6662 Xdiamond, TRUE, FALSE,
6666 Xdiamond_pause, FALSE, FALSE,
6670 Xdiamond_fall, FALSE, FALSE,
6674 Xdiamond_shine, FALSE, FALSE,
6675 EL_DIAMOND, ACTION_TWINKLING, -1
6678 Ydiamond_s, FALSE, FALSE,
6679 EL_DIAMOND, ACTION_FALLING, -1
6682 Ydiamond_sB, FALSE, TRUE,
6683 EL_DIAMOND, ACTION_FALLING, -1
6686 Ydiamond_e, FALSE, FALSE,
6687 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6690 Ydiamond_eB, FALSE, TRUE,
6691 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6694 Ydiamond_w, FALSE, FALSE,
6695 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6698 Ydiamond_wB, FALSE, TRUE,
6699 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6702 Ydiamond_blank, FALSE, FALSE,
6703 EL_DIAMOND, ACTION_COLLECTING, -1
6706 Ydiamond_stone, FALSE, FALSE,
6707 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6711 Xstone, TRUE, FALSE,
6715 Xstone_pause, FALSE, FALSE,
6719 Xstone_fall, FALSE, FALSE,
6723 Ystone_s, FALSE, FALSE,
6724 EL_ROCK, ACTION_FALLING, -1
6727 Ystone_sB, FALSE, TRUE,
6728 EL_ROCK, ACTION_FALLING, -1
6731 Ystone_e, FALSE, FALSE,
6732 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6735 Ystone_eB, FALSE, TRUE,
6736 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6739 Ystone_w, FALSE, FALSE,
6740 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6743 Ystone_wB, FALSE, TRUE,
6744 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6752 Xbomb_pause, FALSE, FALSE,
6756 Xbomb_fall, FALSE, FALSE,
6760 Ybomb_s, FALSE, FALSE,
6761 EL_BOMB, ACTION_FALLING, -1
6764 Ybomb_sB, FALSE, TRUE,
6765 EL_BOMB, ACTION_FALLING, -1
6768 Ybomb_e, FALSE, FALSE,
6769 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6772 Ybomb_eB, FALSE, TRUE,
6773 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6776 Ybomb_w, FALSE, FALSE,
6777 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6780 Ybomb_wB, FALSE, TRUE,
6781 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6784 Ybomb_blank, FALSE, FALSE,
6785 EL_BOMB, ACTION_ACTIVATING, -1
6793 Xnut_pause, FALSE, FALSE,
6797 Xnut_fall, FALSE, FALSE,
6801 Ynut_s, FALSE, FALSE,
6802 EL_NUT, ACTION_FALLING, -1
6805 Ynut_sB, FALSE, TRUE,
6806 EL_NUT, ACTION_FALLING, -1
6809 Ynut_e, FALSE, FALSE,
6810 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6813 Ynut_eB, FALSE, TRUE,
6814 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6817 Ynut_w, FALSE, FALSE,
6818 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6821 Ynut_wB, FALSE, TRUE,
6822 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6825 Ynut_stone, FALSE, FALSE,
6826 EL_NUT, ACTION_BREAKING, -1
6830 Xspring, TRUE, FALSE,
6834 Xspring_pause, FALSE, FALSE,
6838 Xspring_e, TRUE, FALSE,
6839 EL_SPRING_RIGHT, -1, -1
6842 Xspring_w, TRUE, FALSE,
6843 EL_SPRING_LEFT, -1, -1
6846 Xspring_fall, FALSE, FALSE,
6850 Yspring_s, FALSE, FALSE,
6851 EL_SPRING, ACTION_FALLING, -1
6854 Yspring_sB, FALSE, TRUE,
6855 EL_SPRING, ACTION_FALLING, -1
6858 Yspring_e, FALSE, FALSE,
6859 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6862 Yspring_eB, FALSE, TRUE,
6863 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6866 Yspring_w, FALSE, FALSE,
6867 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6870 Yspring_wB, FALSE, TRUE,
6871 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6874 Yspring_alien_e, FALSE, FALSE,
6875 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6878 Yspring_alien_eB, FALSE, TRUE,
6879 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6882 Yspring_alien_w, FALSE, FALSE,
6883 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6886 Yspring_alien_wB, FALSE, TRUE,
6887 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6891 Xpush_emerald_e, FALSE, FALSE,
6892 EL_EMERALD, -1, MV_BIT_RIGHT
6895 Xpush_emerald_w, FALSE, FALSE,
6896 EL_EMERALD, -1, MV_BIT_LEFT
6899 Xpush_diamond_e, FALSE, FALSE,
6900 EL_DIAMOND, -1, MV_BIT_RIGHT
6903 Xpush_diamond_w, FALSE, FALSE,
6904 EL_DIAMOND, -1, MV_BIT_LEFT
6907 Xpush_stone_e, FALSE, FALSE,
6908 EL_ROCK, -1, MV_BIT_RIGHT
6911 Xpush_stone_w, FALSE, FALSE,
6912 EL_ROCK, -1, MV_BIT_LEFT
6915 Xpush_bomb_e, FALSE, FALSE,
6916 EL_BOMB, -1, MV_BIT_RIGHT
6919 Xpush_bomb_w, FALSE, FALSE,
6920 EL_BOMB, -1, MV_BIT_LEFT
6923 Xpush_nut_e, FALSE, FALSE,
6924 EL_NUT, -1, MV_BIT_RIGHT
6927 Xpush_nut_w, FALSE, FALSE,
6928 EL_NUT, -1, MV_BIT_LEFT
6931 Xpush_spring_e, FALSE, FALSE,
6932 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6935 Xpush_spring_w, FALSE, FALSE,
6936 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6940 Xdynamite, TRUE, FALSE,
6941 EL_EM_DYNAMITE, -1, -1
6944 Ydynamite_blank, FALSE, FALSE,
6945 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6948 Xdynamite_1, TRUE, FALSE,
6949 EL_EM_DYNAMITE_ACTIVE, -1, -1
6952 Xdynamite_2, FALSE, FALSE,
6953 EL_EM_DYNAMITE_ACTIVE, -1, -1
6956 Xdynamite_3, FALSE, FALSE,
6957 EL_EM_DYNAMITE_ACTIVE, -1, -1
6960 Xdynamite_4, FALSE, FALSE,
6961 EL_EM_DYNAMITE_ACTIVE, -1, -1
6965 Xkey_1, TRUE, FALSE,
6969 Xkey_2, TRUE, FALSE,
6973 Xkey_3, TRUE, FALSE,
6977 Xkey_4, TRUE, FALSE,
6981 Xkey_5, TRUE, FALSE,
6982 EL_EMC_KEY_5, -1, -1
6985 Xkey_6, TRUE, FALSE,
6986 EL_EMC_KEY_6, -1, -1
6989 Xkey_7, TRUE, FALSE,
6990 EL_EMC_KEY_7, -1, -1
6993 Xkey_8, TRUE, FALSE,
6994 EL_EMC_KEY_8, -1, -1
6998 Xdoor_1, TRUE, FALSE,
6999 EL_EM_GATE_1, -1, -1
7002 Xdoor_2, TRUE, FALSE,
7003 EL_EM_GATE_2, -1, -1
7006 Xdoor_3, TRUE, FALSE,
7007 EL_EM_GATE_3, -1, -1
7010 Xdoor_4, TRUE, FALSE,
7011 EL_EM_GATE_4, -1, -1
7014 Xdoor_5, TRUE, FALSE,
7015 EL_EMC_GATE_5, -1, -1
7018 Xdoor_6, TRUE, FALSE,
7019 EL_EMC_GATE_6, -1, -1
7022 Xdoor_7, TRUE, FALSE,
7023 EL_EMC_GATE_7, -1, -1
7026 Xdoor_8, TRUE, FALSE,
7027 EL_EMC_GATE_8, -1, -1
7031 Xfake_door_1, TRUE, FALSE,
7032 EL_EM_GATE_1_GRAY, -1, -1
7035 Xfake_door_2, TRUE, FALSE,
7036 EL_EM_GATE_2_GRAY, -1, -1
7039 Xfake_door_3, TRUE, FALSE,
7040 EL_EM_GATE_3_GRAY, -1, -1
7043 Xfake_door_4, TRUE, FALSE,
7044 EL_EM_GATE_4_GRAY, -1, -1
7047 Xfake_door_5, TRUE, FALSE,
7048 EL_EMC_GATE_5_GRAY, -1, -1
7051 Xfake_door_6, TRUE, FALSE,
7052 EL_EMC_GATE_6_GRAY, -1, -1
7055 Xfake_door_7, TRUE, FALSE,
7056 EL_EMC_GATE_7_GRAY, -1, -1
7059 Xfake_door_8, TRUE, FALSE,
7060 EL_EMC_GATE_8_GRAY, -1, -1
7064 Xballoon, TRUE, FALSE,
7068 Yballoon_n, FALSE, FALSE,
7069 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7072 Yballoon_nB, FALSE, TRUE,
7073 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7076 Yballoon_e, FALSE, FALSE,
7077 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7080 Yballoon_eB, FALSE, TRUE,
7081 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7084 Yballoon_s, FALSE, FALSE,
7085 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7088 Yballoon_sB, FALSE, TRUE,
7089 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7092 Yballoon_w, FALSE, FALSE,
7093 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7096 Yballoon_wB, FALSE, TRUE,
7097 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7101 Xball_1, TRUE, FALSE,
7102 EL_EMC_MAGIC_BALL, -1, -1
7105 Yball_1, FALSE, FALSE,
7106 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7109 Xball_2, FALSE, FALSE,
7110 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7113 Yball_2, FALSE, FALSE,
7114 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7117 Yball_blank, FALSE, FALSE,
7118 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7122 Xamoeba_1, TRUE, FALSE,
7123 EL_AMOEBA_DRY, ACTION_OTHER, -1
7126 Xamoeba_2, FALSE, FALSE,
7127 EL_AMOEBA_DRY, ACTION_OTHER, -1
7130 Xamoeba_3, FALSE, FALSE,
7131 EL_AMOEBA_DRY, ACTION_OTHER, -1
7134 Xamoeba_4, FALSE, FALSE,
7135 EL_AMOEBA_DRY, ACTION_OTHER, -1
7138 Xamoeba_5, TRUE, FALSE,
7139 EL_AMOEBA_WET, ACTION_OTHER, -1
7142 Xamoeba_6, FALSE, FALSE,
7143 EL_AMOEBA_WET, ACTION_OTHER, -1
7146 Xamoeba_7, FALSE, FALSE,
7147 EL_AMOEBA_WET, ACTION_OTHER, -1
7150 Xamoeba_8, FALSE, FALSE,
7151 EL_AMOEBA_WET, ACTION_OTHER, -1
7156 EL_AMOEBA_DROP, ACTION_GROWING, -1
7159 Xdrip_fall, FALSE, FALSE,
7160 EL_AMOEBA_DROP, -1, -1
7163 Xdrip_stretch, FALSE, FALSE,
7164 EL_AMOEBA_DROP, ACTION_FALLING, -1
7167 Xdrip_stretchB, FALSE, TRUE,
7168 EL_AMOEBA_DROP, ACTION_FALLING, -1
7171 Ydrip_1_s, FALSE, FALSE,
7172 EL_AMOEBA_DROP, ACTION_FALLING, -1
7175 Ydrip_1_sB, FALSE, TRUE,
7176 EL_AMOEBA_DROP, ACTION_FALLING, -1
7179 Ydrip_2_s, FALSE, FALSE,
7180 EL_AMOEBA_DROP, ACTION_FALLING, -1
7183 Ydrip_2_sB, FALSE, TRUE,
7184 EL_AMOEBA_DROP, ACTION_FALLING, -1
7188 Xwonderwall, TRUE, FALSE,
7189 EL_MAGIC_WALL, -1, -1
7192 Ywonderwall, FALSE, FALSE,
7193 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7197 Xwheel, TRUE, FALSE,
7198 EL_ROBOT_WHEEL, -1, -1
7201 Ywheel, FALSE, FALSE,
7202 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7206 Xswitch, TRUE, FALSE,
7207 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7210 Yswitch, FALSE, FALSE,
7211 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7215 Xbumper, TRUE, FALSE,
7216 EL_EMC_SPRING_BUMPER, -1, -1
7219 Ybumper, FALSE, FALSE,
7220 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7224 Xacid_nw, TRUE, FALSE,
7225 EL_ACID_POOL_TOPLEFT, -1, -1
7228 Xacid_ne, TRUE, FALSE,
7229 EL_ACID_POOL_TOPRIGHT, -1, -1
7232 Xacid_sw, TRUE, FALSE,
7233 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7236 Xacid_s, TRUE, FALSE,
7237 EL_ACID_POOL_BOTTOM, -1, -1
7240 Xacid_se, TRUE, FALSE,
7241 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7245 Xfake_blank, TRUE, FALSE,
7246 EL_INVISIBLE_WALL, -1, -1
7249 Yfake_blank, FALSE, FALSE,
7250 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7254 Xfake_grass, TRUE, FALSE,
7255 EL_EMC_FAKE_GRASS, -1, -1
7258 Yfake_grass, FALSE, FALSE,
7259 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7263 Xfake_amoeba, TRUE, FALSE,
7264 EL_EMC_DRIPPER, -1, -1
7267 Yfake_amoeba, FALSE, FALSE,
7268 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7272 Xlenses, TRUE, FALSE,
7273 EL_EMC_LENSES, -1, -1
7277 Xmagnify, TRUE, FALSE,
7278 EL_EMC_MAGNIFIER, -1, -1
7283 EL_QUICKSAND_EMPTY, -1, -1
7286 Xsand_stone, TRUE, FALSE,
7287 EL_QUICKSAND_FULL, -1, -1
7290 Xsand_stonein_1, FALSE, TRUE,
7291 EL_ROCK, ACTION_FILLING, -1
7294 Xsand_stonein_2, FALSE, TRUE,
7295 EL_ROCK, ACTION_FILLING, -1
7298 Xsand_stonein_3, FALSE, TRUE,
7299 EL_ROCK, ACTION_FILLING, -1
7302 Xsand_stonein_4, FALSE, TRUE,
7303 EL_ROCK, ACTION_FILLING, -1
7306 Xsand_sandstone_1, FALSE, FALSE,
7307 EL_QUICKSAND_FILLING, -1, -1
7310 Xsand_sandstone_2, FALSE, FALSE,
7311 EL_QUICKSAND_FILLING, -1, -1
7314 Xsand_sandstone_3, FALSE, FALSE,
7315 EL_QUICKSAND_FILLING, -1, -1
7318 Xsand_sandstone_4, FALSE, FALSE,
7319 EL_QUICKSAND_FILLING, -1, -1
7322 Xsand_stonesand_1, FALSE, FALSE,
7323 EL_QUICKSAND_EMPTYING, -1, -1
7326 Xsand_stonesand_2, FALSE, FALSE,
7327 EL_QUICKSAND_EMPTYING, -1, -1
7330 Xsand_stonesand_3, FALSE, FALSE,
7331 EL_QUICKSAND_EMPTYING, -1, -1
7334 Xsand_stonesand_4, FALSE, FALSE,
7335 EL_QUICKSAND_EMPTYING, -1, -1
7338 Xsand_stoneout_1, FALSE, FALSE,
7339 EL_ROCK, ACTION_EMPTYING, -1
7342 Xsand_stoneout_2, FALSE, FALSE,
7343 EL_ROCK, ACTION_EMPTYING, -1
7346 Xsand_stonesand_quickout_1, FALSE, FALSE,
7347 EL_QUICKSAND_EMPTYING, -1, -1
7350 Xsand_stonesand_quickout_2, FALSE, FALSE,
7351 EL_QUICKSAND_EMPTYING, -1, -1
7355 Xslide_ns, TRUE, FALSE,
7356 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7359 Yslide_ns_blank, FALSE, FALSE,
7360 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7363 Xslide_ew, TRUE, FALSE,
7364 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7367 Yslide_ew_blank, FALSE, FALSE,
7368 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7372 Xwind_n, TRUE, FALSE,
7373 EL_BALLOON_SWITCH_UP, -1, -1
7376 Xwind_e, TRUE, FALSE,
7377 EL_BALLOON_SWITCH_RIGHT, -1, -1
7380 Xwind_s, TRUE, FALSE,
7381 EL_BALLOON_SWITCH_DOWN, -1, -1
7384 Xwind_w, TRUE, FALSE,
7385 EL_BALLOON_SWITCH_LEFT, -1, -1
7388 Xwind_any, TRUE, FALSE,
7389 EL_BALLOON_SWITCH_ANY, -1, -1
7392 Xwind_stop, TRUE, FALSE,
7393 EL_BALLOON_SWITCH_NONE, -1, -1
7398 EL_EM_EXIT_CLOSED, -1, -1
7401 Xexit_1, TRUE, FALSE,
7402 EL_EM_EXIT_OPEN, -1, -1
7405 Xexit_2, FALSE, FALSE,
7406 EL_EM_EXIT_OPEN, -1, -1
7409 Xexit_3, FALSE, FALSE,
7410 EL_EM_EXIT_OPEN, -1, -1
7414 Xpause, FALSE, FALSE,
7419 Xwall_1, TRUE, FALSE,
7423 Xwall_2, TRUE, FALSE,
7424 EL_EMC_WALL_14, -1, -1
7427 Xwall_3, TRUE, FALSE,
7428 EL_EMC_WALL_15, -1, -1
7431 Xwall_4, TRUE, FALSE,
7432 EL_EMC_WALL_16, -1, -1
7436 Xroundwall_1, TRUE, FALSE,
7437 EL_WALL_SLIPPERY, -1, -1
7440 Xroundwall_2, TRUE, FALSE,
7441 EL_EMC_WALL_SLIPPERY_2, -1, -1
7444 Xroundwall_3, TRUE, FALSE,
7445 EL_EMC_WALL_SLIPPERY_3, -1, -1
7448 Xroundwall_4, TRUE, FALSE,
7449 EL_EMC_WALL_SLIPPERY_4, -1, -1
7453 Xsteel_1, TRUE, FALSE,
7454 EL_STEELWALL, -1, -1
7457 Xsteel_2, TRUE, FALSE,
7458 EL_EMC_STEELWALL_2, -1, -1
7461 Xsteel_3, TRUE, FALSE,
7462 EL_EMC_STEELWALL_3, -1, -1
7465 Xsteel_4, TRUE, FALSE,
7466 EL_EMC_STEELWALL_4, -1, -1
7470 Xdecor_1, TRUE, FALSE,
7471 EL_EMC_WALL_8, -1, -1
7474 Xdecor_2, TRUE, FALSE,
7475 EL_EMC_WALL_6, -1, -1
7478 Xdecor_3, TRUE, FALSE,
7479 EL_EMC_WALL_4, -1, -1
7482 Xdecor_4, TRUE, FALSE,
7483 EL_EMC_WALL_7, -1, -1
7486 Xdecor_5, TRUE, FALSE,
7487 EL_EMC_WALL_5, -1, -1
7490 Xdecor_6, TRUE, FALSE,
7491 EL_EMC_WALL_9, -1, -1
7494 Xdecor_7, TRUE, FALSE,
7495 EL_EMC_WALL_10, -1, -1
7498 Xdecor_8, TRUE, FALSE,
7499 EL_EMC_WALL_1, -1, -1
7502 Xdecor_9, TRUE, FALSE,
7503 EL_EMC_WALL_2, -1, -1
7506 Xdecor_10, TRUE, FALSE,
7507 EL_EMC_WALL_3, -1, -1
7510 Xdecor_11, TRUE, FALSE,
7511 EL_EMC_WALL_11, -1, -1
7514 Xdecor_12, TRUE, FALSE,
7515 EL_EMC_WALL_12, -1, -1
7519 Xalpha_0, TRUE, FALSE,
7520 EL_CHAR('0'), -1, -1
7523 Xalpha_1, TRUE, FALSE,
7524 EL_CHAR('1'), -1, -1
7527 Xalpha_2, TRUE, FALSE,
7528 EL_CHAR('2'), -1, -1
7531 Xalpha_3, TRUE, FALSE,
7532 EL_CHAR('3'), -1, -1
7535 Xalpha_4, TRUE, FALSE,
7536 EL_CHAR('4'), -1, -1
7539 Xalpha_5, TRUE, FALSE,
7540 EL_CHAR('5'), -1, -1
7543 Xalpha_6, TRUE, FALSE,
7544 EL_CHAR('6'), -1, -1
7547 Xalpha_7, TRUE, FALSE,
7548 EL_CHAR('7'), -1, -1
7551 Xalpha_8, TRUE, FALSE,
7552 EL_CHAR('8'), -1, -1
7555 Xalpha_9, TRUE, FALSE,
7556 EL_CHAR('9'), -1, -1
7559 Xalpha_excla, TRUE, FALSE,
7560 EL_CHAR('!'), -1, -1
7563 Xalpha_apost, TRUE, FALSE,
7564 EL_CHAR('\''), -1, -1
7567 Xalpha_comma, TRUE, FALSE,
7568 EL_CHAR(','), -1, -1
7571 Xalpha_minus, TRUE, FALSE,
7572 EL_CHAR('-'), -1, -1
7575 Xalpha_perio, TRUE, FALSE,
7576 EL_CHAR('.'), -1, -1
7579 Xalpha_colon, TRUE, FALSE,
7580 EL_CHAR(':'), -1, -1
7583 Xalpha_quest, TRUE, FALSE,
7584 EL_CHAR('?'), -1, -1
7587 Xalpha_a, TRUE, FALSE,
7588 EL_CHAR('A'), -1, -1
7591 Xalpha_b, TRUE, FALSE,
7592 EL_CHAR('B'), -1, -1
7595 Xalpha_c, TRUE, FALSE,
7596 EL_CHAR('C'), -1, -1
7599 Xalpha_d, TRUE, FALSE,
7600 EL_CHAR('D'), -1, -1
7603 Xalpha_e, TRUE, FALSE,
7604 EL_CHAR('E'), -1, -1
7607 Xalpha_f, TRUE, FALSE,
7608 EL_CHAR('F'), -1, -1
7611 Xalpha_g, TRUE, FALSE,
7612 EL_CHAR('G'), -1, -1
7615 Xalpha_h, TRUE, FALSE,
7616 EL_CHAR('H'), -1, -1
7619 Xalpha_i, TRUE, FALSE,
7620 EL_CHAR('I'), -1, -1
7623 Xalpha_j, TRUE, FALSE,
7624 EL_CHAR('J'), -1, -1
7627 Xalpha_k, TRUE, FALSE,
7628 EL_CHAR('K'), -1, -1
7631 Xalpha_l, TRUE, FALSE,
7632 EL_CHAR('L'), -1, -1
7635 Xalpha_m, TRUE, FALSE,
7636 EL_CHAR('M'), -1, -1
7639 Xalpha_n, TRUE, FALSE,
7640 EL_CHAR('N'), -1, -1
7643 Xalpha_o, TRUE, FALSE,
7644 EL_CHAR('O'), -1, -1
7647 Xalpha_p, TRUE, FALSE,
7648 EL_CHAR('P'), -1, -1
7651 Xalpha_q, TRUE, FALSE,
7652 EL_CHAR('Q'), -1, -1
7655 Xalpha_r, TRUE, FALSE,
7656 EL_CHAR('R'), -1, -1
7659 Xalpha_s, TRUE, FALSE,
7660 EL_CHAR('S'), -1, -1
7663 Xalpha_t, TRUE, FALSE,
7664 EL_CHAR('T'), -1, -1
7667 Xalpha_u, TRUE, FALSE,
7668 EL_CHAR('U'), -1, -1
7671 Xalpha_v, TRUE, FALSE,
7672 EL_CHAR('V'), -1, -1
7675 Xalpha_w, TRUE, FALSE,
7676 EL_CHAR('W'), -1, -1
7679 Xalpha_x, TRUE, FALSE,
7680 EL_CHAR('X'), -1, -1
7683 Xalpha_y, TRUE, FALSE,
7684 EL_CHAR('Y'), -1, -1
7687 Xalpha_z, TRUE, FALSE,
7688 EL_CHAR('Z'), -1, -1
7691 Xalpha_arrow_e, TRUE, FALSE,
7692 EL_CHAR('>'), -1, -1
7695 Xalpha_arrow_w, TRUE, FALSE,
7696 EL_CHAR('<'), -1, -1
7699 Xalpha_copyr, TRUE, FALSE,
7700 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7704 Ykey_1_blank, FALSE, FALSE,
7705 EL_EM_KEY_1, ACTION_COLLECTING, -1
7708 Ykey_2_blank, FALSE, FALSE,
7709 EL_EM_KEY_2, ACTION_COLLECTING, -1
7712 Ykey_3_blank, FALSE, FALSE,
7713 EL_EM_KEY_3, ACTION_COLLECTING, -1
7716 Ykey_4_blank, FALSE, FALSE,
7717 EL_EM_KEY_4, ACTION_COLLECTING, -1
7720 Ykey_5_blank, FALSE, FALSE,
7721 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7724 Ykey_6_blank, FALSE, FALSE,
7725 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7728 Ykey_7_blank, FALSE, FALSE,
7729 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7732 Ykey_8_blank, FALSE, FALSE,
7733 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7736 Ylenses_blank, FALSE, FALSE,
7737 EL_EMC_LENSES, ACTION_COLLECTING, -1
7740 Ymagnify_blank, FALSE, FALSE,
7741 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7744 Ygrass_blank, FALSE, FALSE,
7745 EL_EMC_GRASS, ACTION_SNAPPING, -1
7748 Ydirt_blank, FALSE, FALSE,
7749 EL_SAND, ACTION_SNAPPING, -1
7758 static struct Mapping_EM_to_RND_player
7767 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7771 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7775 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7779 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7783 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7787 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7791 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7795 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7799 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7803 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7807 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7811 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7815 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7819 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7823 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7827 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7831 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7835 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7839 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7843 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7847 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7851 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7855 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7859 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7863 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7867 EL_PLAYER_1, ACTION_DEFAULT, -1,
7871 EL_PLAYER_2, ACTION_DEFAULT, -1,
7875 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7879 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7883 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7887 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7891 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7895 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7899 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7903 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7907 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7911 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7915 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7919 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7923 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7927 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7931 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7935 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7939 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7943 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7947 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7951 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7955 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7959 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7963 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7967 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7971 EL_PLAYER_3, ACTION_DEFAULT, -1,
7975 EL_PLAYER_4, ACTION_DEFAULT, -1,
7984 int map_element_RND_to_EM_cave(int element_rnd)
7986 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7987 static boolean mapping_initialized = FALSE;
7989 if (!mapping_initialized)
7993 // return "Xalpha_quest" for all undefined elements in mapping array
7994 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7995 mapping_RND_to_EM[i] = Xalpha_quest;
7997 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7998 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7999 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
8000 em_object_mapping_list[i].element_em;
8002 mapping_initialized = TRUE;
8005 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
8007 Warn("invalid RND level element %d", element_rnd);
8012 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
8015 int map_element_EM_to_RND_cave(int element_em_cave)
8017 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8018 static boolean mapping_initialized = FALSE;
8020 if (!mapping_initialized)
8024 // return "EL_UNKNOWN" for all undefined elements in mapping array
8025 for (i = 0; i < GAME_TILE_MAX; i++)
8026 mapping_EM_to_RND[i] = EL_UNKNOWN;
8028 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8029 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8030 em_object_mapping_list[i].element_rnd;
8032 mapping_initialized = TRUE;
8035 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8037 Warn("invalid EM cave element %d", element_em_cave);
8042 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8045 int map_element_EM_to_RND_game(int element_em_game)
8047 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8048 static boolean mapping_initialized = FALSE;
8050 if (!mapping_initialized)
8054 // return "EL_UNKNOWN" for all undefined elements in mapping array
8055 for (i = 0; i < GAME_TILE_MAX; i++)
8056 mapping_EM_to_RND[i] = EL_UNKNOWN;
8058 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8059 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8060 em_object_mapping_list[i].element_rnd;
8062 mapping_initialized = TRUE;
8065 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8067 Warn("invalid EM game element %d", element_em_game);
8072 return mapping_EM_to_RND[element_em_game];
8075 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8077 struct LevelInfo_EM *level_em = level->native_em_level;
8078 struct CAVE *cav = level_em->cav;
8081 for (i = 0; i < GAME_TILE_MAX; i++)
8082 cav->android_array[i] = Cblank;
8084 for (i = 0; i < level->num_android_clone_elements; i++)
8086 int element_rnd = level->android_clone_element[i];
8087 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8089 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8090 if (em_object_mapping_list[j].element_rnd == element_rnd)
8091 cav->android_array[em_object_mapping_list[j].element_em] =
8096 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8098 struct LevelInfo_EM *level_em = level->native_em_level;
8099 struct CAVE *cav = level_em->cav;
8102 level->num_android_clone_elements = 0;
8104 for (i = 0; i < GAME_TILE_MAX; i++)
8106 int element_em_cave = cav->android_array[i];
8108 boolean element_found = FALSE;
8110 if (element_em_cave == Cblank)
8113 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8115 for (j = 0; j < level->num_android_clone_elements; j++)
8116 if (level->android_clone_element[j] == element_rnd)
8117 element_found = TRUE;
8121 level->android_clone_element[level->num_android_clone_elements++] =
8124 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8129 if (level->num_android_clone_elements == 0)
8131 level->num_android_clone_elements = 1;
8132 level->android_clone_element[0] = EL_EMPTY;
8136 int map_direction_RND_to_EM(int direction)
8138 return (direction == MV_UP ? 0 :
8139 direction == MV_RIGHT ? 1 :
8140 direction == MV_DOWN ? 2 :
8141 direction == MV_LEFT ? 3 :
8145 int map_direction_EM_to_RND(int direction)
8147 return (direction == 0 ? MV_UP :
8148 direction == 1 ? MV_RIGHT :
8149 direction == 2 ? MV_DOWN :
8150 direction == 3 ? MV_LEFT :
8154 int map_element_RND_to_SP(int element_rnd)
8156 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8158 if (element_rnd >= EL_SP_START &&
8159 element_rnd <= EL_SP_END)
8160 element_sp = element_rnd - EL_SP_START;
8161 else if (element_rnd == EL_EMPTY_SPACE)
8163 else if (element_rnd == EL_INVISIBLE_WALL)
8169 int map_element_SP_to_RND(int element_sp)
8171 int element_rnd = EL_UNKNOWN;
8173 if (element_sp >= 0x00 &&
8175 element_rnd = EL_SP_START + element_sp;
8176 else if (element_sp == 0x28)
8177 element_rnd = EL_INVISIBLE_WALL;
8182 int map_action_SP_to_RND(int action_sp)
8186 case actActive: return ACTION_ACTIVE;
8187 case actImpact: return ACTION_IMPACT;
8188 case actExploding: return ACTION_EXPLODING;
8189 case actDigging: return ACTION_DIGGING;
8190 case actSnapping: return ACTION_SNAPPING;
8191 case actCollecting: return ACTION_COLLECTING;
8192 case actPassing: return ACTION_PASSING;
8193 case actPushing: return ACTION_PUSHING;
8194 case actDropping: return ACTION_DROPPING;
8196 default: return ACTION_DEFAULT;
8200 int map_element_RND_to_MM(int element_rnd)
8202 return (element_rnd >= EL_MM_START_1 &&
8203 element_rnd <= EL_MM_END_1 ?
8204 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8206 element_rnd >= EL_MM_START_2 &&
8207 element_rnd <= EL_MM_END_2 ?
8208 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8210 element_rnd >= EL_MM_START_3 &&
8211 element_rnd <= EL_MM_END_3 ?
8212 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
8214 element_rnd >= EL_CHAR_START &&
8215 element_rnd <= EL_CHAR_END ?
8216 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8218 element_rnd >= EL_MM_RUNTIME_START &&
8219 element_rnd <= EL_MM_RUNTIME_END ?
8220 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8222 EL_MM_EMPTY_NATIVE);
8225 int map_element_MM_to_RND(int element_mm)
8227 return (element_mm == EL_MM_EMPTY_NATIVE ||
8228 element_mm == EL_DF_EMPTY_NATIVE ?
8231 element_mm >= EL_MM_START_1_NATIVE &&
8232 element_mm <= EL_MM_END_1_NATIVE ?
8233 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8235 element_mm >= EL_MM_START_2_NATIVE &&
8236 element_mm <= EL_MM_END_2_NATIVE ?
8237 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8239 element_mm >= EL_MM_START_3_NATIVE &&
8240 element_mm <= EL_MM_END_3_NATIVE ?
8241 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
8243 element_mm >= EL_MM_CHAR_START_NATIVE &&
8244 element_mm <= EL_MM_CHAR_END_NATIVE ?
8245 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8247 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8248 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8249 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8254 int map_action_MM_to_RND(int action_mm)
8256 // all MM actions are defined to exactly match their RND counterparts
8260 int map_sound_MM_to_RND(int sound_mm)
8264 case SND_MM_GAME_LEVELTIME_CHARGING:
8265 return SND_GAME_LEVELTIME_CHARGING;
8267 case SND_MM_GAME_HEALTH_CHARGING:
8268 return SND_GAME_HEALTH_CHARGING;
8271 return SND_UNDEFINED;
8275 int map_mm_wall_element(int element)
8277 return (element >= EL_MM_STEEL_WALL_START &&
8278 element <= EL_MM_STEEL_WALL_END ?
8281 element >= EL_MM_WOODEN_WALL_START &&
8282 element <= EL_MM_WOODEN_WALL_END ?
8285 element >= EL_MM_ICE_WALL_START &&
8286 element <= EL_MM_ICE_WALL_END ?
8289 element >= EL_MM_AMOEBA_WALL_START &&
8290 element <= EL_MM_AMOEBA_WALL_END ?
8293 element >= EL_DF_STEEL_WALL_START &&
8294 element <= EL_DF_STEEL_WALL_END ?
8297 element >= EL_DF_WOODEN_WALL_START &&
8298 element <= EL_DF_WOODEN_WALL_END ?
8304 int map_mm_wall_element_editor(int element)
8308 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8309 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8310 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8311 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8312 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8313 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8315 default: return element;
8319 int get_next_element(int element)
8323 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8324 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8325 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8326 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8327 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8328 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8329 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8330 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8331 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8332 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8333 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8335 default: return element;
8339 int el2img_mm(int element_mm)
8341 return el2img(map_element_MM_to_RND(element_mm));
8344 int el_act2img_mm(int element_mm, int action)
8346 return el_act2img(map_element_MM_to_RND(element_mm), action);
8349 int el_act_dir2img(int element, int action, int direction)
8351 element = GFX_ELEMENT(element);
8352 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8354 // direction_graphic[][] == graphic[] for undefined direction graphics
8355 return element_info[element].direction_graphic[action][direction];
8358 static int el_act_dir2crm(int element, int action, int direction)
8360 element = GFX_ELEMENT(element);
8361 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8363 // direction_graphic[][] == graphic[] for undefined direction graphics
8364 return element_info[element].direction_crumbled[action][direction];
8367 int el_act2img(int element, int action)
8369 element = GFX_ELEMENT(element);
8371 return element_info[element].graphic[action];
8374 int el_act2crm(int element, int action)
8376 element = GFX_ELEMENT(element);
8378 return element_info[element].crumbled[action];
8381 int el_dir2img(int element, int direction)
8383 element = GFX_ELEMENT(element);
8385 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8388 int el2baseimg(int element)
8390 return element_info[element].graphic[ACTION_DEFAULT];
8393 int el2img(int element)
8395 element = GFX_ELEMENT(element);
8397 return element_info[element].graphic[ACTION_DEFAULT];
8400 int el2edimg(int element)
8402 element = GFX_ELEMENT(element);
8404 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8407 int el2preimg(int element)
8409 element = GFX_ELEMENT(element);
8411 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8414 int el2panelimg(int element)
8416 element = GFX_ELEMENT(element);
8418 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8421 int font2baseimg(int font_nr)
8423 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8426 int getBeltNrFromBeltElement(int element)
8428 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8429 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8430 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8433 int getBeltNrFromBeltActiveElement(int element)
8435 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8436 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8437 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8440 int getBeltNrFromBeltSwitchElement(int element)
8442 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8443 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8444 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8447 int getBeltDirNrFromBeltElement(int element)
8449 static int belt_base_element[4] =
8451 EL_CONVEYOR_BELT_1_LEFT,
8452 EL_CONVEYOR_BELT_2_LEFT,
8453 EL_CONVEYOR_BELT_3_LEFT,
8454 EL_CONVEYOR_BELT_4_LEFT
8457 int belt_nr = getBeltNrFromBeltElement(element);
8458 int belt_dir_nr = element - belt_base_element[belt_nr];
8460 return (belt_dir_nr % 3);
8463 int getBeltDirNrFromBeltSwitchElement(int element)
8465 static int belt_base_element[4] =
8467 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8468 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8469 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8470 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8473 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8474 int belt_dir_nr = element - belt_base_element[belt_nr];
8476 return (belt_dir_nr % 3);
8479 int getBeltDirFromBeltElement(int element)
8481 static int belt_move_dir[3] =
8488 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8490 return belt_move_dir[belt_dir_nr];
8493 int getBeltDirFromBeltSwitchElement(int element)
8495 static int belt_move_dir[3] =
8502 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8504 return belt_move_dir[belt_dir_nr];
8507 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8509 static int belt_base_element[4] =
8511 EL_CONVEYOR_BELT_1_LEFT,
8512 EL_CONVEYOR_BELT_2_LEFT,
8513 EL_CONVEYOR_BELT_3_LEFT,
8514 EL_CONVEYOR_BELT_4_LEFT
8517 return belt_base_element[belt_nr] + belt_dir_nr;
8520 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8522 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8524 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8527 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8529 static int belt_base_element[4] =
8531 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8532 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8533 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8534 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8537 return belt_base_element[belt_nr] + belt_dir_nr;
8540 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8542 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8544 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8547 boolean swapTiles_EM(boolean is_pre_emc_cave)
8549 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8552 boolean getTeamMode_EM(void)
8554 return game.team_mode || network_playing;
8557 boolean isActivePlayer_EM(int player_nr)
8559 return stored_player[player_nr].active;
8562 unsigned int InitRND(int seed)
8564 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8565 return InitEngineRandom_EM(seed);
8566 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8567 return InitEngineRandom_SP(seed);
8568 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8569 return InitEngineRandom_MM(seed);
8571 return InitEngineRandom_RND(seed);
8574 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8575 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8577 static int get_effective_element_EM(int tile, int frame_em)
8579 int element = object_mapping[tile].element_rnd;
8580 int action = object_mapping[tile].action;
8581 boolean is_backside = object_mapping[tile].is_backside;
8582 boolean action_removing = (action == ACTION_DIGGING ||
8583 action == ACTION_SNAPPING ||
8584 action == ACTION_COLLECTING);
8592 return (frame_em > 5 ? EL_EMPTY : element);
8598 else // frame_em == 7
8609 case Ydiamond_stone:
8613 case Xdrip_stretchB:
8629 case Ymagnify_blank:
8632 case Xsand_stonein_1:
8633 case Xsand_stonein_2:
8634 case Xsand_stonein_3:
8635 case Xsand_stonein_4:
8639 return (is_backside || action_removing ? EL_EMPTY : element);
8644 static boolean check_linear_animation_EM(int tile)
8648 case Xsand_stonesand_1:
8649 case Xsand_stonesand_quickout_1:
8650 case Xsand_sandstone_1:
8651 case Xsand_stonein_1:
8652 case Xsand_stoneout_1:
8680 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8681 boolean has_crumbled_graphics,
8682 int crumbled, int sync_frame)
8684 // if element can be crumbled, but certain action graphics are just empty
8685 // space (like instantly snapping sand to empty space in 1 frame), do not
8686 // treat these empty space graphics as crumbled graphics in EMC engine
8687 if (crumbled == IMG_EMPTY_SPACE)
8688 has_crumbled_graphics = FALSE;
8690 if (has_crumbled_graphics)
8692 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8693 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8694 g_crumbled->anim_delay,
8695 g_crumbled->anim_mode,
8696 g_crumbled->anim_start_frame,
8699 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8700 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8702 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8703 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8705 g_em->has_crumbled_graphics = TRUE;
8709 g_em->crumbled_bitmap = NULL;
8710 g_em->crumbled_src_x = 0;
8711 g_em->crumbled_src_y = 0;
8712 g_em->crumbled_border_size = 0;
8713 g_em->crumbled_tile_size = 0;
8715 g_em->has_crumbled_graphics = FALSE;
8720 void ResetGfxAnimation_EM(int x, int y, int tile)
8726 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8727 int tile, int frame_em, int x, int y)
8729 int action = object_mapping[tile].action;
8730 int direction = object_mapping[tile].direction;
8731 int effective_element = get_effective_element_EM(tile, frame_em);
8732 int graphic = (direction == MV_NONE ?
8733 el_act2img(effective_element, action) :
8734 el_act_dir2img(effective_element, action, direction));
8735 struct GraphicInfo *g = &graphic_info[graphic];
8737 boolean action_removing = (action == ACTION_DIGGING ||
8738 action == ACTION_SNAPPING ||
8739 action == ACTION_COLLECTING);
8740 boolean action_moving = (action == ACTION_FALLING ||
8741 action == ACTION_MOVING ||
8742 action == ACTION_PUSHING ||
8743 action == ACTION_EATING ||
8744 action == ACTION_FILLING ||
8745 action == ACTION_EMPTYING);
8746 boolean action_falling = (action == ACTION_FALLING ||
8747 action == ACTION_FILLING ||
8748 action == ACTION_EMPTYING);
8750 // special case: graphic uses "2nd movement tile" and has defined
8751 // 7 frames for movement animation (or less) => use default graphic
8752 // for last (8th) frame which ends the movement animation
8753 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8755 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8756 graphic = (direction == MV_NONE ?
8757 el_act2img(effective_element, action) :
8758 el_act_dir2img(effective_element, action, direction));
8760 g = &graphic_info[graphic];
8763 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8767 else if (action_moving)
8769 boolean is_backside = object_mapping[tile].is_backside;
8773 int direction = object_mapping[tile].direction;
8774 int move_dir = (action_falling ? MV_DOWN : direction);
8779 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8780 if (g->double_movement && frame_em == 0)
8784 if (move_dir == MV_LEFT)
8785 GfxFrame[x - 1][y] = GfxFrame[x][y];
8786 else if (move_dir == MV_RIGHT)
8787 GfxFrame[x + 1][y] = GfxFrame[x][y];
8788 else if (move_dir == MV_UP)
8789 GfxFrame[x][y - 1] = GfxFrame[x][y];
8790 else if (move_dir == MV_DOWN)
8791 GfxFrame[x][y + 1] = GfxFrame[x][y];
8798 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8799 if (tile == Xsand_stonesand_quickout_1 ||
8800 tile == Xsand_stonesand_quickout_2)
8804 if (graphic_info[graphic].anim_global_sync)
8805 sync_frame = FrameCounter;
8806 else if (graphic_info[graphic].anim_global_anim_sync)
8807 sync_frame = getGlobalAnimSyncFrame();
8808 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8809 sync_frame = GfxFrame[x][y];
8811 sync_frame = 0; // playfield border (pseudo steel)
8813 SetRandomAnimationValue(x, y);
8815 int frame = getAnimationFrame(g->anim_frames,
8818 g->anim_start_frame,
8821 g_em->unique_identifier =
8822 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8825 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8826 int tile, int frame_em, int x, int y)
8828 int action = object_mapping[tile].action;
8829 int direction = object_mapping[tile].direction;
8830 boolean is_backside = object_mapping[tile].is_backside;
8831 int effective_element = get_effective_element_EM(tile, frame_em);
8832 int effective_action = action;
8833 int graphic = (direction == MV_NONE ?
8834 el_act2img(effective_element, effective_action) :
8835 el_act_dir2img(effective_element, effective_action,
8837 int crumbled = (direction == MV_NONE ?
8838 el_act2crm(effective_element, effective_action) :
8839 el_act_dir2crm(effective_element, effective_action,
8841 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8842 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8843 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8844 struct GraphicInfo *g = &graphic_info[graphic];
8847 // special case: graphic uses "2nd movement tile" and has defined
8848 // 7 frames for movement animation (or less) => use default graphic
8849 // for last (8th) frame which ends the movement animation
8850 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8852 effective_action = ACTION_DEFAULT;
8853 graphic = (direction == MV_NONE ?
8854 el_act2img(effective_element, effective_action) :
8855 el_act_dir2img(effective_element, effective_action,
8857 crumbled = (direction == MV_NONE ?
8858 el_act2crm(effective_element, effective_action) :
8859 el_act_dir2crm(effective_element, effective_action,
8862 g = &graphic_info[graphic];
8865 if (graphic_info[graphic].anim_global_sync)
8866 sync_frame = FrameCounter;
8867 else if (graphic_info[graphic].anim_global_anim_sync)
8868 sync_frame = getGlobalAnimSyncFrame();
8869 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8870 sync_frame = GfxFrame[x][y];
8872 sync_frame = 0; // playfield border (pseudo steel)
8874 SetRandomAnimationValue(x, y);
8876 int frame = getAnimationFrame(g->anim_frames,
8879 g->anim_start_frame,
8882 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8883 g->double_movement && is_backside);
8885 // (updating the "crumbled" graphic definitions is probably not really needed,
8886 // as animations for crumbled graphics can't be longer than one EMC cycle)
8887 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8891 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8892 int player_nr, int anim, int frame_em)
8894 int element = player_mapping[player_nr][anim].element_rnd;
8895 int action = player_mapping[player_nr][anim].action;
8896 int direction = player_mapping[player_nr][anim].direction;
8897 int graphic = (direction == MV_NONE ?
8898 el_act2img(element, action) :
8899 el_act_dir2img(element, action, direction));
8900 struct GraphicInfo *g = &graphic_info[graphic];
8903 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8905 stored_player[player_nr].StepFrame = frame_em;
8907 sync_frame = stored_player[player_nr].Frame;
8909 int frame = getAnimationFrame(g->anim_frames,
8912 g->anim_start_frame,
8915 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8916 &g_em->src_x, &g_em->src_y, FALSE);
8919 void InitGraphicInfo_EM(void)
8923 // always start with reliable default values
8924 for (i = 0; i < GAME_TILE_MAX; i++)
8926 object_mapping[i].element_rnd = EL_UNKNOWN;
8927 object_mapping[i].is_backside = FALSE;
8928 object_mapping[i].action = ACTION_DEFAULT;
8929 object_mapping[i].direction = MV_NONE;
8932 // always start with reliable default values
8933 for (p = 0; p < MAX_PLAYERS; p++)
8935 for (i = 0; i < PLY_MAX; i++)
8937 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8938 player_mapping[p][i].action = ACTION_DEFAULT;
8939 player_mapping[p][i].direction = MV_NONE;
8943 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8945 int e = em_object_mapping_list[i].element_em;
8947 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8948 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8950 if (em_object_mapping_list[i].action != -1)
8951 object_mapping[e].action = em_object_mapping_list[i].action;
8953 if (em_object_mapping_list[i].direction != -1)
8954 object_mapping[e].direction =
8955 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8958 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8960 int a = em_player_mapping_list[i].action_em;
8961 int p = em_player_mapping_list[i].player_nr;
8963 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8965 if (em_player_mapping_list[i].action != -1)
8966 player_mapping[p][a].action = em_player_mapping_list[i].action;
8968 if (em_player_mapping_list[i].direction != -1)
8969 player_mapping[p][a].direction =
8970 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8973 for (i = 0; i < GAME_TILE_MAX; i++)
8975 int element = object_mapping[i].element_rnd;
8976 int action = object_mapping[i].action;
8977 int direction = object_mapping[i].direction;
8978 boolean is_backside = object_mapping[i].is_backside;
8979 boolean action_exploding = ((action == ACTION_EXPLODING ||
8980 action == ACTION_SMASHED_BY_ROCK ||
8981 action == ACTION_SMASHED_BY_SPRING) &&
8982 element != EL_DIAMOND);
8983 boolean action_active = (action == ACTION_ACTIVE);
8984 boolean action_other = (action == ACTION_OTHER);
8986 for (j = 0; j < 8; j++)
8988 int effective_element = get_effective_element_EM(i, j);
8989 int effective_action = (j < 7 ? action :
8990 i == Xdrip_stretch ? action :
8991 i == Xdrip_stretchB ? action :
8992 i == Ydrip_1_s ? action :
8993 i == Ydrip_1_sB ? action :
8994 i == Yball_1 ? action :
8995 i == Xball_2 ? action :
8996 i == Yball_2 ? action :
8997 i == Yball_blank ? action :
8998 i == Ykey_1_blank ? action :
8999 i == Ykey_2_blank ? action :
9000 i == Ykey_3_blank ? action :
9001 i == Ykey_4_blank ? action :
9002 i == Ykey_5_blank ? action :
9003 i == Ykey_6_blank ? action :
9004 i == Ykey_7_blank ? action :
9005 i == Ykey_8_blank ? action :
9006 i == Ylenses_blank ? action :
9007 i == Ymagnify_blank ? action :
9008 i == Ygrass_blank ? action :
9009 i == Ydirt_blank ? action :
9010 i == Xsand_stonein_1 ? action :
9011 i == Xsand_stonein_2 ? action :
9012 i == Xsand_stonein_3 ? action :
9013 i == Xsand_stonein_4 ? action :
9014 i == Xsand_stoneout_1 ? action :
9015 i == Xsand_stoneout_2 ? action :
9016 i == Xboom_android ? ACTION_EXPLODING :
9017 action_exploding ? ACTION_EXPLODING :
9018 action_active ? action :
9019 action_other ? action :
9021 int graphic = (el_act_dir2img(effective_element, effective_action,
9023 int crumbled = (el_act_dir2crm(effective_element, effective_action,
9025 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
9026 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
9027 boolean has_action_graphics = (graphic != base_graphic);
9028 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9029 struct GraphicInfo *g = &graphic_info[graphic];
9030 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9033 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9034 boolean special_animation = (action != ACTION_DEFAULT &&
9035 g->anim_frames == 3 &&
9036 g->anim_delay == 2 &&
9037 g->anim_mode & ANIM_LINEAR);
9038 int sync_frame = (i == Xdrip_stretch ? 7 :
9039 i == Xdrip_stretchB ? 7 :
9040 i == Ydrip_2_s ? j + 8 :
9041 i == Ydrip_2_sB ? j + 8 :
9050 i == Xfake_acid_1 ? 0 :
9051 i == Xfake_acid_2 ? 10 :
9052 i == Xfake_acid_3 ? 20 :
9053 i == Xfake_acid_4 ? 30 :
9054 i == Xfake_acid_5 ? 40 :
9055 i == Xfake_acid_6 ? 50 :
9056 i == Xfake_acid_7 ? 60 :
9057 i == Xfake_acid_8 ? 70 :
9058 i == Xfake_acid_1_player ? 0 :
9059 i == Xfake_acid_2_player ? 10 :
9060 i == Xfake_acid_3_player ? 20 :
9061 i == Xfake_acid_4_player ? 30 :
9062 i == Xfake_acid_5_player ? 40 :
9063 i == Xfake_acid_6_player ? 50 :
9064 i == Xfake_acid_7_player ? 60 :
9065 i == Xfake_acid_8_player ? 70 :
9067 i == Yball_2 ? j + 8 :
9068 i == Yball_blank ? j + 1 :
9069 i == Ykey_1_blank ? j + 1 :
9070 i == Ykey_2_blank ? j + 1 :
9071 i == Ykey_3_blank ? j + 1 :
9072 i == Ykey_4_blank ? j + 1 :
9073 i == Ykey_5_blank ? j + 1 :
9074 i == Ykey_6_blank ? j + 1 :
9075 i == Ykey_7_blank ? j + 1 :
9076 i == Ykey_8_blank ? j + 1 :
9077 i == Ylenses_blank ? j + 1 :
9078 i == Ymagnify_blank ? j + 1 :
9079 i == Ygrass_blank ? j + 1 :
9080 i == Ydirt_blank ? j + 1 :
9081 i == Xamoeba_1 ? 0 :
9082 i == Xamoeba_2 ? 1 :
9083 i == Xamoeba_3 ? 2 :
9084 i == Xamoeba_4 ? 3 :
9085 i == Xamoeba_5 ? 0 :
9086 i == Xamoeba_6 ? 1 :
9087 i == Xamoeba_7 ? 2 :
9088 i == Xamoeba_8 ? 3 :
9089 i == Xexit_2 ? j + 8 :
9090 i == Xexit_3 ? j + 16 :
9091 i == Xdynamite_1 ? 0 :
9092 i == Xdynamite_2 ? 8 :
9093 i == Xdynamite_3 ? 16 :
9094 i == Xdynamite_4 ? 24 :
9095 i == Xsand_stonein_1 ? j + 1 :
9096 i == Xsand_stonein_2 ? j + 9 :
9097 i == Xsand_stonein_3 ? j + 17 :
9098 i == Xsand_stonein_4 ? j + 25 :
9099 i == Xsand_stoneout_1 && j == 0 ? 0 :
9100 i == Xsand_stoneout_1 && j == 1 ? 0 :
9101 i == Xsand_stoneout_1 && j == 2 ? 1 :
9102 i == Xsand_stoneout_1 && j == 3 ? 2 :
9103 i == Xsand_stoneout_1 && j == 4 ? 2 :
9104 i == Xsand_stoneout_1 && j == 5 ? 3 :
9105 i == Xsand_stoneout_1 && j == 6 ? 4 :
9106 i == Xsand_stoneout_1 && j == 7 ? 4 :
9107 i == Xsand_stoneout_2 && j == 0 ? 5 :
9108 i == Xsand_stoneout_2 && j == 1 ? 6 :
9109 i == Xsand_stoneout_2 && j == 2 ? 7 :
9110 i == Xsand_stoneout_2 && j == 3 ? 8 :
9111 i == Xsand_stoneout_2 && j == 4 ? 9 :
9112 i == Xsand_stoneout_2 && j == 5 ? 11 :
9113 i == Xsand_stoneout_2 && j == 6 ? 13 :
9114 i == Xsand_stoneout_2 && j == 7 ? 15 :
9115 i == Xboom_bug && j == 1 ? 2 :
9116 i == Xboom_bug && j == 2 ? 2 :
9117 i == Xboom_bug && j == 3 ? 4 :
9118 i == Xboom_bug && j == 4 ? 4 :
9119 i == Xboom_bug && j == 5 ? 2 :
9120 i == Xboom_bug && j == 6 ? 2 :
9121 i == Xboom_bug && j == 7 ? 0 :
9122 i == Xboom_tank && j == 1 ? 2 :
9123 i == Xboom_tank && j == 2 ? 2 :
9124 i == Xboom_tank && j == 3 ? 4 :
9125 i == Xboom_tank && j == 4 ? 4 :
9126 i == Xboom_tank && j == 5 ? 2 :
9127 i == Xboom_tank && j == 6 ? 2 :
9128 i == Xboom_tank && j == 7 ? 0 :
9129 i == Xboom_android && j == 7 ? 6 :
9130 i == Xboom_1 && j == 1 ? 2 :
9131 i == Xboom_1 && j == 2 ? 2 :
9132 i == Xboom_1 && j == 3 ? 4 :
9133 i == Xboom_1 && j == 4 ? 4 :
9134 i == Xboom_1 && j == 5 ? 6 :
9135 i == Xboom_1 && j == 6 ? 6 :
9136 i == Xboom_1 && j == 7 ? 8 :
9137 i == Xboom_2 && j == 0 ? 8 :
9138 i == Xboom_2 && j == 1 ? 8 :
9139 i == Xboom_2 && j == 2 ? 10 :
9140 i == Xboom_2 && j == 3 ? 10 :
9141 i == Xboom_2 && j == 4 ? 10 :
9142 i == Xboom_2 && j == 5 ? 12 :
9143 i == Xboom_2 && j == 6 ? 12 :
9144 i == Xboom_2 && j == 7 ? 12 :
9145 special_animation && j == 4 ? 3 :
9146 effective_action != action ? 0 :
9148 int frame = getAnimationFrame(g->anim_frames,
9151 g->anim_start_frame,
9154 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9155 g->double_movement && is_backside);
9157 g_em->bitmap = src_bitmap;
9158 g_em->src_x = src_x;
9159 g_em->src_y = src_y;
9160 g_em->src_offset_x = 0;
9161 g_em->src_offset_y = 0;
9162 g_em->dst_offset_x = 0;
9163 g_em->dst_offset_y = 0;
9164 g_em->width = TILEX;
9165 g_em->height = TILEY;
9167 g_em->preserve_background = FALSE;
9169 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9172 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9173 effective_action == ACTION_MOVING ||
9174 effective_action == ACTION_PUSHING ||
9175 effective_action == ACTION_EATING)) ||
9176 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9177 effective_action == ACTION_EMPTYING)))
9180 (effective_action == ACTION_FALLING ||
9181 effective_action == ACTION_FILLING ||
9182 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9183 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9184 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9185 int num_steps = (i == Ydrip_1_s ? 16 :
9186 i == Ydrip_1_sB ? 16 :
9187 i == Ydrip_2_s ? 16 :
9188 i == Ydrip_2_sB ? 16 :
9189 i == Xsand_stonein_1 ? 32 :
9190 i == Xsand_stonein_2 ? 32 :
9191 i == Xsand_stonein_3 ? 32 :
9192 i == Xsand_stonein_4 ? 32 :
9193 i == Xsand_stoneout_1 ? 16 :
9194 i == Xsand_stoneout_2 ? 16 : 8);
9195 int cx = ABS(dx) * (TILEX / num_steps);
9196 int cy = ABS(dy) * (TILEY / num_steps);
9197 int step_frame = (i == Ydrip_2_s ? j + 8 :
9198 i == Ydrip_2_sB ? j + 8 :
9199 i == Xsand_stonein_2 ? j + 8 :
9200 i == Xsand_stonein_3 ? j + 16 :
9201 i == Xsand_stonein_4 ? j + 24 :
9202 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9203 int step = (is_backside ? step_frame : num_steps - step_frame);
9205 if (is_backside) // tile where movement starts
9207 if (dx < 0 || dy < 0)
9209 g_em->src_offset_x = cx * step;
9210 g_em->src_offset_y = cy * step;
9214 g_em->dst_offset_x = cx * step;
9215 g_em->dst_offset_y = cy * step;
9218 else // tile where movement ends
9220 if (dx < 0 || dy < 0)
9222 g_em->dst_offset_x = cx * step;
9223 g_em->dst_offset_y = cy * step;
9227 g_em->src_offset_x = cx * step;
9228 g_em->src_offset_y = cy * step;
9232 g_em->width = TILEX - cx * step;
9233 g_em->height = TILEY - cy * step;
9236 // create unique graphic identifier to decide if tile must be redrawn
9237 /* bit 31 - 16 (16 bit): EM style graphic
9238 bit 15 - 12 ( 4 bit): EM style frame
9239 bit 11 - 6 ( 6 bit): graphic width
9240 bit 5 - 0 ( 6 bit): graphic height */
9241 g_em->unique_identifier =
9242 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9246 for (i = 0; i < GAME_TILE_MAX; i++)
9248 for (j = 0; j < 8; j++)
9250 int element = object_mapping[i].element_rnd;
9251 int action = object_mapping[i].action;
9252 int direction = object_mapping[i].direction;
9253 boolean is_backside = object_mapping[i].is_backside;
9254 int graphic_action = el_act_dir2img(element, action, direction);
9255 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9257 if ((action == ACTION_SMASHED_BY_ROCK ||
9258 action == ACTION_SMASHED_BY_SPRING ||
9259 action == ACTION_EATING) &&
9260 graphic_action == graphic_default)
9262 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9263 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9264 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9265 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9268 // no separate animation for "smashed by rock" -- use rock instead
9269 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9270 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9272 g_em->bitmap = g_xx->bitmap;
9273 g_em->src_x = g_xx->src_x;
9274 g_em->src_y = g_xx->src_y;
9275 g_em->src_offset_x = g_xx->src_offset_x;
9276 g_em->src_offset_y = g_xx->src_offset_y;
9277 g_em->dst_offset_x = g_xx->dst_offset_x;
9278 g_em->dst_offset_y = g_xx->dst_offset_y;
9279 g_em->width = g_xx->width;
9280 g_em->height = g_xx->height;
9281 g_em->unique_identifier = g_xx->unique_identifier;
9284 g_em->preserve_background = TRUE;
9289 for (p = 0; p < MAX_PLAYERS; p++)
9291 for (i = 0; i < PLY_MAX; i++)
9293 int element = player_mapping[p][i].element_rnd;
9294 int action = player_mapping[p][i].action;
9295 int direction = player_mapping[p][i].direction;
9297 for (j = 0; j < 8; j++)
9299 int effective_element = element;
9300 int effective_action = action;
9301 int graphic = (direction == MV_NONE ?
9302 el_act2img(effective_element, effective_action) :
9303 el_act_dir2img(effective_element, effective_action,
9305 struct GraphicInfo *g = &graphic_info[graphic];
9306 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9310 int frame = getAnimationFrame(g->anim_frames,
9313 g->anim_start_frame,
9316 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9318 g_em->bitmap = src_bitmap;
9319 g_em->src_x = src_x;
9320 g_em->src_y = src_y;
9321 g_em->src_offset_x = 0;
9322 g_em->src_offset_y = 0;
9323 g_em->dst_offset_x = 0;
9324 g_em->dst_offset_y = 0;
9325 g_em->width = TILEX;
9326 g_em->height = TILEY;
9332 static void CheckSaveEngineSnapshot_EM(int frame,
9333 boolean any_player_moving,
9334 boolean any_player_snapping,
9335 boolean any_player_dropping)
9337 if (frame == 7 && !any_player_dropping)
9339 if (!local_player->was_waiting)
9341 if (!CheckSaveEngineSnapshotToList())
9344 local_player->was_waiting = TRUE;
9347 else if (any_player_moving || any_player_snapping || any_player_dropping)
9349 local_player->was_waiting = FALSE;
9353 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9354 boolean murphy_is_dropping)
9356 if (murphy_is_waiting)
9358 if (!local_player->was_waiting)
9360 if (!CheckSaveEngineSnapshotToList())
9363 local_player->was_waiting = TRUE;
9368 local_player->was_waiting = FALSE;
9372 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9373 boolean button_released)
9375 if (button_released)
9377 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9378 CheckSaveEngineSnapshotToList();
9380 else if (element_clicked)
9382 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9383 CheckSaveEngineSnapshotToList();
9385 game.snapshot.changed_action = TRUE;
9389 boolean CheckSingleStepMode_EM(int frame,
9390 boolean any_player_moving,
9391 boolean any_player_snapping,
9392 boolean any_player_dropping)
9394 if (tape.single_step && tape.recording && !tape.pausing)
9395 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9396 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9398 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9399 any_player_snapping, any_player_dropping);
9401 return tape.pausing;
9404 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9405 boolean murphy_is_dropping)
9407 boolean murphy_starts_dropping = FALSE;
9410 for (i = 0; i < MAX_PLAYERS; i++)
9411 if (stored_player[i].force_dropping)
9412 murphy_starts_dropping = TRUE;
9414 if (tape.single_step && tape.recording && !tape.pausing)
9415 if (murphy_is_waiting && !murphy_starts_dropping)
9416 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9418 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9421 void CheckSingleStepMode_MM(boolean element_clicked,
9422 boolean button_released)
9424 if (tape.single_step && tape.recording && !tape.pausing)
9425 if (button_released)
9426 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9428 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9431 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9432 int graphic, int sync_frame)
9434 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9436 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9439 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9441 return (IS_NEXT_FRAME(sync_frame, graphic));
9444 int getGraphicInfo_Delay(int graphic)
9446 return graphic_info[graphic].anim_delay;
9449 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9451 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9454 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9460 void PlayMenuSoundExt(int sound)
9462 if (sound == SND_UNDEFINED)
9465 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9466 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9469 if (IS_LOOP_SOUND(sound))
9470 PlaySoundLoop(sound);
9475 void PlayMenuSound(void)
9477 PlayMenuSoundExt(menu.sound[game_status]);
9480 void PlayMenuSoundStereo(int sound, int stereo_position)
9482 if (sound == SND_UNDEFINED)
9485 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9486 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9489 if (IS_LOOP_SOUND(sound))
9490 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9492 PlaySoundStereo(sound, stereo_position);
9495 void PlayMenuSoundIfLoopExt(int sound)
9497 if (sound == SND_UNDEFINED)
9500 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9501 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9504 if (IS_LOOP_SOUND(sound))
9505 PlaySoundLoop(sound);
9508 void PlayMenuSoundIfLoop(void)
9510 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9513 void PlayMenuMusicExt(int music)
9515 if (music == MUS_UNDEFINED)
9518 if (!setup.sound_music)
9521 if (IS_LOOP_MUSIC(music))
9522 PlayMusicLoop(music);
9527 void PlayMenuMusic(void)
9529 char *curr_music = getCurrentlyPlayingMusicFilename();
9530 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9532 if (!strEqual(curr_music, next_music))
9533 PlayMenuMusicExt(menu.music[game_status]);
9536 void PlayMenuSoundsAndMusic(void)
9542 static void FadeMenuSounds(void)
9547 static void FadeMenuMusic(void)
9549 char *curr_music = getCurrentlyPlayingMusicFilename();
9550 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9552 if (!strEqual(curr_music, next_music))
9556 void FadeMenuSoundsAndMusic(void)
9562 void PlaySoundActivating(void)
9565 PlaySound(SND_MENU_ITEM_ACTIVATING);
9569 void PlaySoundSelecting(void)
9572 PlaySound(SND_MENU_ITEM_SELECTING);
9576 void ToggleFullscreenIfNeeded(void)
9578 // if setup and video fullscreen state are already matching, nothing do do
9579 if (setup.fullscreen == video.fullscreen_enabled ||
9580 !video.fullscreen_available)
9583 SDLSetWindowFullscreen(setup.fullscreen);
9585 // set setup value according to successfully changed fullscreen mode
9586 setup.fullscreen = video.fullscreen_enabled;
9589 void ChangeWindowScalingIfNeeded(void)
9591 // if setup and video window scaling are already matching, nothing do do
9592 if (setup.window_scaling_percent == video.window_scaling_percent ||
9593 video.fullscreen_enabled)
9596 SDLSetWindowScaling(setup.window_scaling_percent);
9598 // set setup value according to successfully changed window scaling
9599 setup.window_scaling_percent = video.window_scaling_percent;
9602 void ChangeVsyncModeIfNeeded(void)
9604 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9605 int video_vsync_mode = video.vsync_mode;
9607 // if setup and video vsync mode are already matching, nothing do do
9608 if (setup_vsync_mode == video_vsync_mode)
9611 // if renderer is using OpenGL, vsync mode can directly be changed
9612 SDLSetScreenVsyncMode(setup.vsync_mode);
9614 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9615 if (video.vsync_mode == video_vsync_mode)
9617 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9619 // save backbuffer content which gets lost when re-creating screen
9620 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9622 // force re-creating screen and renderer to set new vsync mode
9623 video.fullscreen_enabled = !setup.fullscreen;
9625 // when creating new renderer, destroy textures linked to old renderer
9626 FreeAllImageTextures(); // needs old renderer to free the textures
9628 // re-create screen and renderer (including change of vsync mode)
9629 ChangeVideoModeIfNeeded(setup.fullscreen);
9631 // set setup value according to successfully changed fullscreen mode
9632 setup.fullscreen = video.fullscreen_enabled;
9634 // restore backbuffer content from temporary backbuffer backup bitmap
9635 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9636 FreeBitmap(tmp_backbuffer);
9638 // update visible window/screen
9639 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9641 // when changing vsync mode, re-create textures for new renderer
9642 InitImageTextures();
9645 // set setup value according to successfully changed vsync mode
9646 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9649 static void JoinRectangles(int *x, int *y, int *width, int *height,
9650 int x2, int y2, int width2, int height2)
9652 // do not join with "off-screen" rectangle
9653 if (x2 == -1 || y2 == -1)
9658 *width = MAX(*width, width2);
9659 *height = MAX(*height, height2);
9662 void SetAnimStatus(int anim_status_new)
9664 if (anim_status_new == GAME_MODE_MAIN)
9665 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9666 else if (anim_status_new == GAME_MODE_NAMES)
9667 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9668 else if (anim_status_new == GAME_MODE_SCORES)
9669 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9671 global.anim_status_next = anim_status_new;
9673 // directly set screen modes that are entered without fading
9674 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9675 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9676 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9677 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9678 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9679 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9680 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9681 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9682 global.anim_status = global.anim_status_next;
9685 void SetGameStatus(int game_status_new)
9687 if (game_status_new != game_status)
9688 game_status_last_screen = game_status;
9690 game_status = game_status_new;
9692 SetAnimStatus(game_status_new);
9695 void SetFontStatus(int game_status_new)
9697 static int last_game_status = -1;
9699 if (game_status_new != -1)
9701 // set game status for font use after storing last game status
9702 last_game_status = game_status;
9703 game_status = game_status_new;
9707 // reset game status after font use from last stored game status
9708 game_status = last_game_status;
9712 void ResetFontStatus(void)
9717 void SetLevelSetInfo(char *identifier, int level_nr)
9719 setString(&levelset.identifier, identifier);
9721 levelset.level_nr = level_nr;
9724 boolean CheckIfAllViewportsHaveChanged(void)
9726 // if game status has not changed, viewports have not changed either
9727 if (game_status == game_status_last)
9730 // check if all viewports have changed with current game status
9732 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9733 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9734 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9735 int new_real_sx = vp_playfield->x;
9736 int new_real_sy = vp_playfield->y;
9737 int new_full_sxsize = vp_playfield->width;
9738 int new_full_sysize = vp_playfield->height;
9739 int new_dx = vp_door_1->x;
9740 int new_dy = vp_door_1->y;
9741 int new_dxsize = vp_door_1->width;
9742 int new_dysize = vp_door_1->height;
9743 int new_vx = vp_door_2->x;
9744 int new_vy = vp_door_2->y;
9745 int new_vxsize = vp_door_2->width;
9746 int new_vysize = vp_door_2->height;
9748 boolean playfield_viewport_has_changed =
9749 (new_real_sx != REAL_SX ||
9750 new_real_sy != REAL_SY ||
9751 new_full_sxsize != FULL_SXSIZE ||
9752 new_full_sysize != FULL_SYSIZE);
9754 boolean door_1_viewport_has_changed =
9757 new_dxsize != DXSIZE ||
9758 new_dysize != DYSIZE);
9760 boolean door_2_viewport_has_changed =
9763 new_vxsize != VXSIZE ||
9764 new_vysize != VYSIZE ||
9765 game_status_last == GAME_MODE_EDITOR);
9767 return (playfield_viewport_has_changed &&
9768 door_1_viewport_has_changed &&
9769 door_2_viewport_has_changed);
9772 boolean CheckFadeAll(void)
9774 return (CheckIfGlobalBorderHasChanged() ||
9775 CheckIfAllViewportsHaveChanged());
9778 void ChangeViewportPropertiesIfNeeded(void)
9780 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9781 FALSE : setup.small_game_graphics);
9782 int gfx_game_mode = getGlobalGameStatus(game_status);
9783 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9785 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9786 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9787 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9788 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9789 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9790 int new_win_xsize = vp_window->width;
9791 int new_win_ysize = vp_window->height;
9792 int border_left = vp_playfield->border_left;
9793 int border_right = vp_playfield->border_right;
9794 int border_top = vp_playfield->border_top;
9795 int border_bottom = vp_playfield->border_bottom;
9796 int new_sx = vp_playfield->x + border_left;
9797 int new_sy = vp_playfield->y + border_top;
9798 int new_sxsize = vp_playfield->width - border_left - border_right;
9799 int new_sysize = vp_playfield->height - border_top - border_bottom;
9800 int new_real_sx = vp_playfield->x;
9801 int new_real_sy = vp_playfield->y;
9802 int new_full_sxsize = vp_playfield->width;
9803 int new_full_sysize = vp_playfield->height;
9804 int new_dx = vp_door_1->x;
9805 int new_dy = vp_door_1->y;
9806 int new_dxsize = vp_door_1->width;
9807 int new_dysize = vp_door_1->height;
9808 int new_vx = vp_door_2->x;
9809 int new_vy = vp_door_2->y;
9810 int new_vxsize = vp_door_2->width;
9811 int new_vysize = vp_door_2->height;
9812 int new_ex = vp_door_3->x;
9813 int new_ey = vp_door_3->y;
9814 int new_exsize = vp_door_3->width;
9815 int new_eysize = vp_door_3->height;
9816 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9817 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9818 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9819 int new_scr_fieldx = new_sxsize / tilesize;
9820 int new_scr_fieldy = new_sysize / tilesize;
9821 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9822 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9823 boolean init_gfx_buffers = FALSE;
9824 boolean init_video_buffer = FALSE;
9825 boolean init_gadgets_and_anims = FALSE;
9826 boolean init_em_graphics = FALSE;
9828 if (new_win_xsize != WIN_XSIZE ||
9829 new_win_ysize != WIN_YSIZE)
9831 WIN_XSIZE = new_win_xsize;
9832 WIN_YSIZE = new_win_ysize;
9834 init_video_buffer = TRUE;
9835 init_gfx_buffers = TRUE;
9836 init_gadgets_and_anims = TRUE;
9838 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9841 if (new_scr_fieldx != SCR_FIELDX ||
9842 new_scr_fieldy != SCR_FIELDY)
9844 // this always toggles between MAIN and GAME when using small tile size
9846 SCR_FIELDX = new_scr_fieldx;
9847 SCR_FIELDY = new_scr_fieldy;
9849 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9860 new_sxsize != SXSIZE ||
9861 new_sysize != SYSIZE ||
9862 new_dxsize != DXSIZE ||
9863 new_dysize != DYSIZE ||
9864 new_vxsize != VXSIZE ||
9865 new_vysize != VYSIZE ||
9866 new_exsize != EXSIZE ||
9867 new_eysize != EYSIZE ||
9868 new_real_sx != REAL_SX ||
9869 new_real_sy != REAL_SY ||
9870 new_full_sxsize != FULL_SXSIZE ||
9871 new_full_sysize != FULL_SYSIZE ||
9872 new_tilesize_var != TILESIZE_VAR
9875 // ------------------------------------------------------------------------
9876 // determine next fading area for changed viewport definitions
9877 // ------------------------------------------------------------------------
9879 // start with current playfield area (default fading area)
9882 FADE_SXSIZE = FULL_SXSIZE;
9883 FADE_SYSIZE = FULL_SYSIZE;
9885 // add new playfield area if position or size has changed
9886 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9887 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9889 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9890 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9893 // add current and new door 1 area if position or size has changed
9894 if (new_dx != DX || new_dy != DY ||
9895 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9897 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9898 DX, DY, DXSIZE, DYSIZE);
9899 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9900 new_dx, new_dy, new_dxsize, new_dysize);
9903 // add current and new door 2 area if position or size has changed
9904 if (new_vx != VX || new_vy != VY ||
9905 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9907 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9908 VX, VY, VXSIZE, VYSIZE);
9909 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9910 new_vx, new_vy, new_vxsize, new_vysize);
9913 // ------------------------------------------------------------------------
9914 // handle changed tile size
9915 // ------------------------------------------------------------------------
9917 if (new_tilesize_var != TILESIZE_VAR)
9919 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9921 // changing tile size invalidates scroll values of engine snapshots
9922 FreeEngineSnapshotSingle();
9924 // changing tile size requires update of graphic mapping for EM engine
9925 init_em_graphics = TRUE;
9936 SXSIZE = new_sxsize;
9937 SYSIZE = new_sysize;
9938 DXSIZE = new_dxsize;
9939 DYSIZE = new_dysize;
9940 VXSIZE = new_vxsize;
9941 VYSIZE = new_vysize;
9942 EXSIZE = new_exsize;
9943 EYSIZE = new_eysize;
9944 REAL_SX = new_real_sx;
9945 REAL_SY = new_real_sy;
9946 FULL_SXSIZE = new_full_sxsize;
9947 FULL_SYSIZE = new_full_sysize;
9948 TILESIZE_VAR = new_tilesize_var;
9950 init_gfx_buffers = TRUE;
9951 init_gadgets_and_anims = TRUE;
9953 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9954 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9957 if (init_gfx_buffers)
9959 // Debug("tools:viewport", "init_gfx_buffers");
9961 SCR_FIELDX = new_scr_fieldx_buffers;
9962 SCR_FIELDY = new_scr_fieldy_buffers;
9966 SCR_FIELDX = new_scr_fieldx;
9967 SCR_FIELDY = new_scr_fieldy;
9969 SetDrawDeactivationMask(REDRAW_NONE);
9970 SetDrawBackgroundMask(REDRAW_FIELD);
9973 if (init_video_buffer)
9975 // Debug("tools:viewport", "init_video_buffer");
9977 FreeAllImageTextures(); // needs old renderer to free the textures
9979 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9980 InitImageTextures();
9983 if (init_gadgets_and_anims)
9985 // Debug("tools:viewport", "init_gadgets_and_anims");
9988 InitGlobalAnimations();
9991 if (init_em_graphics)
9993 InitGraphicInfo_EM();
9997 void OpenURL(char *url)
9999 #if SDL_VERSION_ATLEAST(2,0,14)
10002 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
10003 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
10004 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
10008 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
10010 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
10014 // ============================================================================
10016 // ============================================================================
10018 #if defined(PLATFORM_WINDOWS)
10019 /* FILETIME of Jan 1 1970 00:00:00. */
10020 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
10023 * timezone information is stored outside the kernel so tzp isn't used anymore.
10025 * Note: this function is not for Win32 high precision timing purpose. See
10028 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
10030 FILETIME file_time;
10031 SYSTEMTIME system_time;
10032 ULARGE_INTEGER ularge;
10034 GetSystemTime(&system_time);
10035 SystemTimeToFileTime(&system_time, &file_time);
10036 ularge.LowPart = file_time.dwLowDateTime;
10037 ularge.HighPart = file_time.dwHighDateTime;
10039 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10040 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10046 static char *test_init_uuid_random_function_simple(void)
10048 static char seed_text[100];
10049 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10051 sprintf(seed_text, "%d", seed);
10056 static char *test_init_uuid_random_function_better(void)
10058 static char seed_text[100];
10059 struct timeval current_time;
10061 gettimeofday(¤t_time, NULL);
10063 prng_seed_bytes(¤t_time, sizeof(current_time));
10065 sprintf(seed_text, "%ld.%ld",
10066 (long)current_time.tv_sec,
10067 (long)current_time.tv_usec);
10072 #if defined(PLATFORM_WINDOWS)
10073 static char *test_init_uuid_random_function_better_windows(void)
10075 static char seed_text[100];
10076 struct timeval current_time;
10078 gettimeofday_windows(¤t_time, NULL);
10080 prng_seed_bytes(¤t_time, sizeof(current_time));
10082 sprintf(seed_text, "%ld.%ld",
10083 (long)current_time.tv_sec,
10084 (long)current_time.tv_usec);
10090 static unsigned int test_uuid_random_function_simple(int max)
10092 return GetSimpleRandom(max);
10095 static unsigned int test_uuid_random_function_better(int max)
10097 return (max > 0 ? prng_get_uint() % max : 0);
10100 #if defined(PLATFORM_WINDOWS)
10101 #define NUM_UUID_TESTS 3
10103 #define NUM_UUID_TESTS 2
10106 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10108 struct hashtable *hash_seeds =
10109 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10110 struct hashtable *hash_uuids =
10111 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10112 static char message[100];
10115 char *random_name = (nr == 0 ? "simple" : "better");
10116 char *random_type = (always_seed ? "always" : "only once");
10117 char *(*init_random_function)(void) =
10119 test_init_uuid_random_function_simple :
10120 test_init_uuid_random_function_better);
10121 unsigned int (*random_function)(int) =
10123 test_uuid_random_function_simple :
10124 test_uuid_random_function_better);
10127 #if defined(PLATFORM_WINDOWS)
10130 random_name = "windows";
10131 init_random_function = test_init_uuid_random_function_better_windows;
10137 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10138 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10140 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10141 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10142 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10144 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10148 // always initialize random number generator at least once
10149 init_random_function();
10151 unsigned int time_start = SDL_GetTicks();
10153 for (i = 0; i < num_uuids; i++)
10157 char *seed = getStringCopy(init_random_function());
10159 hashtable_remove(hash_seeds, seed);
10160 hashtable_insert(hash_seeds, seed, "1");
10163 char *uuid = getStringCopy(getUUIDExt(random_function));
10165 hashtable_remove(hash_uuids, uuid);
10166 hashtable_insert(hash_uuids, uuid, "1");
10169 int num_unique_seeds = hashtable_count(hash_seeds);
10170 int num_unique_uuids = hashtable_count(hash_uuids);
10172 unsigned int time_needed = SDL_GetTicks() - time_start;
10174 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10176 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10179 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10181 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10182 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10184 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10186 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10188 Request(message, REQ_CONFIRM);
10190 hashtable_destroy(hash_seeds, 0);
10191 hashtable_destroy(hash_uuids, 0);
10194 void TestGeneratingUUIDs(void)
10196 int num_uuids = 1000000;
10199 for (i = 0; i < NUM_UUID_TESTS; i++)
10200 for (j = 0; j < 2; j++)
10201 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10203 CloseAllAndExit(0);