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 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
3260 int delay_value_normal = request.step_delay;
3261 int delay_value_fast = delay_value_normal / 2;
3262 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3263 boolean no_delay = (tape.warp_forward);
3264 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3265 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3266 DelayCounter anim_delay = { anim_delay_value };
3268 int tile_size = MAX(request.step_offset, 1);
3269 int max_xsize = request.width / tile_size;
3270 int max_ysize = request.height / tile_size;
3271 int max_xsize_inner = max_xsize - 2;
3272 int max_ysize_inner = max_ysize - 2;
3274 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3275 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3276 int xend = max_xsize_inner;
3277 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3278 int xstep = (xstart < xend ? 1 : 0);
3279 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3281 int end = MAX(xend - xstart, yend - ystart);
3284 if (setup.quick_doors)
3291 for (i = start; i <= end; i++)
3293 int last_frame = end; // last frame of this "for" loop
3294 int x = xstart + i * xstep;
3295 int y = ystart + i * ystep;
3296 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3297 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3298 int xsize_size_left = (xsize - 1) * tile_size;
3299 int ysize_size_top = (ysize - 1) * tile_size;
3300 int max_xsize_pos = (max_xsize - 1) * tile_size;
3301 int max_ysize_pos = (max_ysize - 1) * tile_size;
3302 int width = xsize * tile_size;
3303 int height = ysize * tile_size;
3308 if (game_just_ended)
3309 HandleGameActions();
3311 setRequestPosition(&src_x, &src_y, FALSE);
3312 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3314 for (yy = 0; yy < 2; yy++)
3316 for (xx = 0; xx < 2; xx++)
3318 int src_xx = src_x + xx * max_xsize_pos;
3319 int src_yy = src_y + yy * max_ysize_pos;
3320 int dst_xx = dst_x + xx * xsize_size_left;
3321 int dst_yy = dst_y + yy * ysize_size_top;
3322 int xx_size = (xx ? tile_size : xsize_size_left);
3323 int yy_size = (yy ? tile_size : ysize_size_top);
3325 // draw partial (animated) envelope request to temporary bitmap
3326 BlitBitmap(bitmap_db_store_1, bitmap_db_store_2,
3327 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3331 // prepare partial (animated) envelope request from temporary bitmap
3332 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, dst_x, dst_y,
3335 redraw_mask |= REDRAW_FIELD;
3339 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3342 ClearAutoRepeatKeyEvents();
3345 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3347 int graphic = IMG_BACKGROUND_REQUEST;
3348 int sound_opening = SND_REQUEST_OPENING;
3349 int sound_closing = SND_REQUEST_CLOSING;
3350 int anim_mode_1 = request.anim_mode; // (higher priority)
3351 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3352 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3353 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3354 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3356 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3358 if (action == ACTION_OPENING)
3360 DrawEnvelopeRequest(text, req_state);
3362 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3364 if (anim_mode == ANIM_DEFAULT)
3365 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3367 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3371 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3373 if (anim_mode != ANIM_NONE)
3374 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3376 if (anim_mode == ANIM_DEFAULT)
3377 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3380 game.envelope_active = FALSE;
3383 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3385 if (IS_MM_WALL(element))
3387 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3393 int graphic = el2preimg(element);
3395 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3396 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3401 void DrawLevel(int draw_background_mask)
3405 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3406 SetDrawBackgroundMask(draw_background_mask);
3410 for (x = BX1; x <= BX2; x++)
3411 for (y = BY1; y <= BY2; y++)
3412 DrawScreenField(x, y);
3414 redraw_mask |= REDRAW_FIELD;
3417 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3422 for (x = 0; x < size_x; x++)
3423 for (y = 0; y < size_y; y++)
3424 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3426 redraw_mask |= REDRAW_FIELD;
3429 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3433 for (x = 0; x < size_x; x++)
3434 for (y = 0; y < size_y; y++)
3435 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3437 redraw_mask |= REDRAW_FIELD;
3440 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3442 boolean show_level_border = (BorderElement != EL_EMPTY);
3443 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3444 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3445 int tile_size = preview.tile_size;
3446 int preview_width = preview.xsize * tile_size;
3447 int preview_height = preview.ysize * tile_size;
3448 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3449 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3450 int real_preview_width = real_preview_xsize * tile_size;
3451 int real_preview_height = real_preview_ysize * tile_size;
3452 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3453 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3456 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3459 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3461 dst_x += (preview_width - real_preview_width) / 2;
3462 dst_y += (preview_height - real_preview_height) / 2;
3464 for (x = 0; x < real_preview_xsize; x++)
3466 for (y = 0; y < real_preview_ysize; y++)
3468 int lx = from_x + x + (show_level_border ? -1 : 0);
3469 int ly = from_y + y + (show_level_border ? -1 : 0);
3470 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3471 getBorderElement(lx, ly));
3473 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3474 element, tile_size);
3478 redraw_mask |= REDRAW_FIELD;
3481 #define MICROLABEL_EMPTY 0
3482 #define MICROLABEL_LEVEL_NAME 1
3483 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3484 #define MICROLABEL_LEVEL_AUTHOR 3
3485 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3486 #define MICROLABEL_IMPORTED_FROM 5
3487 #define MICROLABEL_IMPORTED_BY_HEAD 6
3488 #define MICROLABEL_IMPORTED_BY 7
3490 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3492 int max_text_width = SXSIZE;
3493 int font_width = getFontWidth(font_nr);
3495 if (pos->align == ALIGN_CENTER)
3496 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3497 else if (pos->align == ALIGN_RIGHT)
3498 max_text_width = pos->x;
3500 max_text_width = SXSIZE - pos->x;
3502 return max_text_width / font_width;
3505 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3507 char label_text[MAX_OUTPUT_LINESIZE + 1];
3508 int max_len_label_text;
3509 int font_nr = pos->font;
3512 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3515 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3516 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3517 mode == MICROLABEL_IMPORTED_BY_HEAD)
3518 font_nr = pos->font_alt;
3520 max_len_label_text = getMaxTextLength(pos, font_nr);
3522 if (pos->size != -1)
3523 max_len_label_text = pos->size;
3525 for (i = 0; i < max_len_label_text; i++)
3526 label_text[i] = ' ';
3527 label_text[max_len_label_text] = '\0';
3529 if (strlen(label_text) > 0)
3530 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3533 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3534 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3535 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3536 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3537 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3538 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3539 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3540 max_len_label_text);
3541 label_text[max_len_label_text] = '\0';
3543 if (strlen(label_text) > 0)
3544 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3546 redraw_mask |= REDRAW_FIELD;
3549 static void DrawPreviewLevelLabel(int mode)
3551 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3554 static void DrawPreviewLevelInfo(int mode)
3556 if (mode == MICROLABEL_LEVEL_NAME)
3557 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3558 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3559 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3562 static void DrawPreviewLevelExt(boolean restart)
3564 static DelayCounter scroll_delay = { 0 };
3565 static DelayCounter label_delay = { 0 };
3566 static int from_x, from_y, scroll_direction;
3567 static int label_state, label_counter;
3568 boolean show_level_border = (BorderElement != EL_EMPTY);
3569 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3570 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3572 scroll_delay.value = preview.step_delay;
3573 label_delay.value = MICROLEVEL_LABEL_DELAY;
3580 if (preview.anim_mode == ANIM_CENTERED)
3582 if (level_xsize > preview.xsize)
3583 from_x = (level_xsize - preview.xsize) / 2;
3584 if (level_ysize > preview.ysize)
3585 from_y = (level_ysize - preview.ysize) / 2;
3588 from_x += preview.xoffset;
3589 from_y += preview.yoffset;
3591 scroll_direction = MV_RIGHT;
3595 DrawPreviewLevelPlayfield(from_x, from_y);
3596 DrawPreviewLevelLabel(label_state);
3598 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3599 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3601 // initialize delay counters
3602 ResetDelayCounter(&scroll_delay);
3603 ResetDelayCounter(&label_delay);
3605 if (leveldir_current->name)
3607 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3608 char label_text[MAX_OUTPUT_LINESIZE + 1];
3609 int font_nr = pos->font;
3610 int max_len_label_text = getMaxTextLength(pos, font_nr);
3612 if (pos->size != -1)
3613 max_len_label_text = pos->size;
3615 strncpy(label_text, leveldir_current->name, max_len_label_text);
3616 label_text[max_len_label_text] = '\0';
3618 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3619 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3625 // scroll preview level, if needed
3626 if (preview.anim_mode != ANIM_NONE &&
3627 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3628 DelayReached(&scroll_delay))
3630 switch (scroll_direction)
3635 from_x -= preview.step_offset;
3636 from_x = (from_x < 0 ? 0 : from_x);
3639 scroll_direction = MV_UP;
3643 if (from_x < level_xsize - preview.xsize)
3645 from_x += preview.step_offset;
3646 from_x = (from_x > level_xsize - preview.xsize ?
3647 level_xsize - preview.xsize : from_x);
3650 scroll_direction = MV_DOWN;
3656 from_y -= preview.step_offset;
3657 from_y = (from_y < 0 ? 0 : from_y);
3660 scroll_direction = MV_RIGHT;
3664 if (from_y < level_ysize - preview.ysize)
3666 from_y += preview.step_offset;
3667 from_y = (from_y > level_ysize - preview.ysize ?
3668 level_ysize - preview.ysize : from_y);
3671 scroll_direction = MV_LEFT;
3678 DrawPreviewLevelPlayfield(from_x, from_y);
3681 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3682 // redraw micro level label, if needed
3683 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3684 !strEqual(level.author, ANONYMOUS_NAME) &&
3685 !strEqual(level.author, leveldir_current->name) &&
3686 DelayReached(&label_delay))
3688 int max_label_counter = 23;
3690 if (leveldir_current->imported_from != NULL &&
3691 strlen(leveldir_current->imported_from) > 0)
3692 max_label_counter += 14;
3693 if (leveldir_current->imported_by != NULL &&
3694 strlen(leveldir_current->imported_by) > 0)
3695 max_label_counter += 14;
3697 label_counter = (label_counter + 1) % max_label_counter;
3698 label_state = (label_counter >= 0 && label_counter <= 7 ?
3699 MICROLABEL_LEVEL_NAME :
3700 label_counter >= 9 && label_counter <= 12 ?
3701 MICROLABEL_LEVEL_AUTHOR_HEAD :
3702 label_counter >= 14 && label_counter <= 21 ?
3703 MICROLABEL_LEVEL_AUTHOR :
3704 label_counter >= 23 && label_counter <= 26 ?
3705 MICROLABEL_IMPORTED_FROM_HEAD :
3706 label_counter >= 28 && label_counter <= 35 ?
3707 MICROLABEL_IMPORTED_FROM :
3708 label_counter >= 37 && label_counter <= 40 ?
3709 MICROLABEL_IMPORTED_BY_HEAD :
3710 label_counter >= 42 && label_counter <= 49 ?
3711 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3713 if (leveldir_current->imported_from == NULL &&
3714 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3715 label_state == MICROLABEL_IMPORTED_FROM))
3716 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3717 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3719 DrawPreviewLevelLabel(label_state);
3723 void DrawPreviewPlayers(void)
3725 if (game_status != GAME_MODE_MAIN)
3728 // do not draw preview players if level preview redefined, but players aren't
3729 if (preview.redefined && !menu.main.preview_players.redefined)
3732 boolean player_found[MAX_PLAYERS];
3733 int num_players = 0;
3736 for (i = 0; i < MAX_PLAYERS; i++)
3737 player_found[i] = FALSE;
3739 // check which players can be found in the level (simple approach)
3740 for (x = 0; x < lev_fieldx; x++)
3742 for (y = 0; y < lev_fieldy; y++)
3744 int element = level.field[x][y];
3746 if (IS_PLAYER_ELEMENT(element))
3748 int player_nr = GET_PLAYER_NR(element);
3750 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3752 if (!player_found[player_nr])
3755 player_found[player_nr] = TRUE;
3760 struct TextPosInfo *pos = &menu.main.preview_players;
3761 int tile_size = pos->tile_size;
3762 int border_size = pos->border_size;
3763 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3764 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3765 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3766 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3767 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3768 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3769 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3770 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3771 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3772 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3773 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3774 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3776 // clear area in which the players will be drawn
3777 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3778 max_players_width, max_players_height);
3780 if (!network.enabled && !setup.team_mode)
3783 // only draw players if level is suited for team mode
3784 if (num_players < 2)
3787 // draw all players that were found in the level
3788 for (i = 0; i < MAX_PLAYERS; i++)
3790 if (player_found[i])
3792 int graphic = el2img(EL_PLAYER_1 + i);
3794 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3796 xpos += player_xoffset;
3797 ypos += player_yoffset;
3802 void DrawPreviewLevelInitial(void)
3804 DrawPreviewLevelExt(TRUE);
3805 DrawPreviewPlayers();
3808 void DrawPreviewLevelAnimation(void)
3810 DrawPreviewLevelExt(FALSE);
3813 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3814 int border_size, int font_nr)
3816 int graphic = el2img(EL_PLAYER_1 + player_nr);
3817 int font_height = getFontHeight(font_nr);
3818 int player_height = MAX(tile_size, font_height);
3819 int xoffset_text = tile_size + border_size;
3820 int yoffset_text = (player_height - font_height) / 2;
3821 int yoffset_graphic = (player_height - tile_size) / 2;
3822 char *player_name = getNetworkPlayerName(player_nr + 1);
3824 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3826 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3829 static void DrawNetworkPlayersExt(boolean force)
3831 if (game_status != GAME_MODE_MAIN)
3834 if (!network.connected && !force)
3837 // do not draw network players if level preview redefined, but players aren't
3838 if (preview.redefined && !menu.main.network_players.redefined)
3841 int num_players = 0;
3844 for (i = 0; i < MAX_PLAYERS; i++)
3845 if (stored_player[i].connected_network)
3848 struct TextPosInfo *pos = &menu.main.network_players;
3849 int tile_size = pos->tile_size;
3850 int border_size = pos->border_size;
3851 int xoffset_text = tile_size + border_size;
3852 int font_nr = pos->font;
3853 int font_width = getFontWidth(font_nr);
3854 int font_height = getFontHeight(font_nr);
3855 int player_height = MAX(tile_size, font_height);
3856 int player_yoffset = player_height + border_size;
3857 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3858 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3859 int all_players_height = num_players * player_yoffset - border_size;
3860 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3861 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3862 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3864 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3865 max_players_width, max_players_height);
3867 // first draw local network player ...
3868 for (i = 0; i < MAX_PLAYERS; i++)
3870 if (stored_player[i].connected_network &&
3871 stored_player[i].connected_locally)
3873 char *player_name = getNetworkPlayerName(i + 1);
3874 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3875 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3877 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3879 ypos += player_yoffset;
3883 // ... then draw all other network players
3884 for (i = 0; i < MAX_PLAYERS; i++)
3886 if (stored_player[i].connected_network &&
3887 !stored_player[i].connected_locally)
3889 char *player_name = getNetworkPlayerName(i + 1);
3890 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3891 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3893 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3895 ypos += player_yoffset;
3900 void DrawNetworkPlayers(void)
3902 DrawNetworkPlayersExt(FALSE);
3905 void ClearNetworkPlayers(void)
3907 DrawNetworkPlayersExt(TRUE);
3910 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3911 int graphic, int lx, int ly,
3914 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3916 if (mask_mode == USE_MASKING)
3917 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3919 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3922 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3923 int graphic, int sync_frame, int mask_mode)
3925 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3927 if (mask_mode == USE_MASKING)
3928 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3930 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3933 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3934 int graphic, int sync_frame, int tilesize,
3937 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3939 if (mask_mode == USE_MASKING)
3940 DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
3942 DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
3945 static void DrawGraphicAnimation(int x, int y, int graphic)
3947 int lx = LEVELX(x), ly = LEVELY(y);
3948 int mask_mode = NO_MASKING;
3950 if (!IN_SCR_FIELD(x, y))
3953 if (game.use_masked_elements)
3955 if (Tile[lx][ly] != EL_EMPTY)
3957 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3959 mask_mode = USE_MASKING;
3963 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3964 graphic, lx, ly, mask_mode);
3966 MarkTileDirty(x, y);
3969 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3971 int lx = LEVELX(x), ly = LEVELY(y);
3972 int mask_mode = NO_MASKING;
3974 if (!IN_SCR_FIELD(x, y))
3977 if (game.use_masked_elements)
3979 if (Tile[lx][ly] != EL_EMPTY)
3981 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3983 mask_mode = USE_MASKING;
3987 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3988 graphic, lx, ly, mask_mode);
3990 MarkTileDirty(x, y);
3993 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3995 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3998 void DrawLevelElementAnimation(int x, int y, int element)
4000 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4002 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4005 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
4007 int sx = SCREENX(x), sy = SCREENY(y);
4009 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4012 if (Tile[x][y] == EL_EMPTY)
4013 graphic = el2img(GfxElementEmpty[x][y]);
4015 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4018 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4021 DrawGraphicAnimation(sx, sy, graphic);
4024 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4025 DrawLevelFieldCrumbled(x, y);
4027 if (GFX_CRUMBLED(Tile[x][y]))
4028 DrawLevelFieldCrumbled(x, y);
4032 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4034 int sx = SCREENX(x), sy = SCREENY(y);
4037 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4040 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4042 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4045 DrawGraphicAnimation(sx, sy, graphic);
4047 if (GFX_CRUMBLED(element))
4048 DrawLevelFieldCrumbled(x, y);
4051 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4053 if (player->use_murphy)
4055 // this works only because currently only one player can be "murphy" ...
4056 static int last_horizontal_dir = MV_LEFT;
4057 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4059 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4060 last_horizontal_dir = move_dir;
4062 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4064 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4066 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4072 return el_act_dir2img(player->artwork_element, player->GfxAction, move_dir);
4075 static boolean equalGraphics(int graphic1, int graphic2)
4077 struct GraphicInfo *g1 = &graphic_info[graphic1];
4078 struct GraphicInfo *g2 = &graphic_info[graphic2];
4080 return (g1->bitmap == g2->bitmap &&
4081 g1->src_x == g2->src_x &&
4082 g1->src_y == g2->src_y &&
4083 g1->anim_frames == g2->anim_frames &&
4084 g1->anim_delay == g2->anim_delay &&
4085 g1->anim_mode == g2->anim_mode);
4088 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4092 DRAW_PLAYER_STAGE_INIT = 0,
4093 DRAW_PLAYER_STAGE_LAST_FIELD,
4094 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4095 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4096 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4097 DRAW_PLAYER_STAGE_PLAYER,
4099 DRAW_PLAYER_STAGE_PLAYER,
4100 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4102 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4103 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4105 NUM_DRAW_PLAYER_STAGES
4108 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4110 static int static_last_player_graphic[MAX_PLAYERS];
4111 static int static_last_player_frame[MAX_PLAYERS];
4112 static boolean static_player_is_opaque[MAX_PLAYERS];
4113 static boolean draw_player[MAX_PLAYERS];
4114 int pnr = player->index_nr;
4116 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4118 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4119 static_last_player_frame[pnr] = player->Frame;
4120 static_player_is_opaque[pnr] = FALSE;
4122 draw_player[pnr] = TRUE;
4125 if (!draw_player[pnr])
4129 if (!IN_LEV_FIELD(player->jx, player->jy))
4131 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4132 Debug("draw:DrawPlayerExt", "This should never happen!");
4134 draw_player[pnr] = FALSE;
4140 int last_player_graphic = static_last_player_graphic[pnr];
4141 int last_player_frame = static_last_player_frame[pnr];
4142 boolean player_is_opaque = static_player_is_opaque[pnr];
4144 int jx = player->jx;
4145 int jy = player->jy;
4146 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4147 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4148 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4149 int last_jx = (player->is_moving ? jx - dx : jx);
4150 int last_jy = (player->is_moving ? jy - dy : jy);
4151 int next_jx = jx + dx;
4152 int next_jy = jy + dy;
4153 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4154 int sx = SCREENX(jx);
4155 int sy = SCREENY(jy);
4156 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4157 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4158 int element = Tile[jx][jy];
4159 int last_element = Tile[last_jx][last_jy];
4160 int action = (player->is_pushing ? ACTION_PUSHING :
4161 player->is_digging ? ACTION_DIGGING :
4162 player->is_collecting ? ACTION_COLLECTING :
4163 player->is_moving ? ACTION_MOVING :
4164 player->is_snapping ? ACTION_SNAPPING :
4165 player->is_dropping ? ACTION_DROPPING :
4166 player->is_waiting ? player->action_waiting :
4169 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4171 // ------------------------------------------------------------------------
4172 // initialize drawing the player
4173 // ------------------------------------------------------------------------
4175 draw_player[pnr] = FALSE;
4177 // GfxElement[][] is set to the element the player is digging or collecting;
4178 // remove also for off-screen player if the player is not moving anymore
4179 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4180 GfxElement[jx][jy] = EL_UNDEFINED;
4182 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4185 if (element == EL_EXPLOSION)
4188 InitPlayerGfxAnimation(player, action, move_dir);
4190 draw_player[pnr] = TRUE;
4192 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4194 // ------------------------------------------------------------------------
4195 // draw things in the field the player is leaving, if needed
4196 // ------------------------------------------------------------------------
4198 if (!IN_SCR_FIELD(sx, sy))
4199 draw_player[pnr] = FALSE;
4201 if (!player->is_moving)
4204 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4206 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4208 if (last_element == EL_DYNAMITE_ACTIVE ||
4209 last_element == EL_EM_DYNAMITE_ACTIVE ||
4210 last_element == EL_SP_DISK_RED_ACTIVE)
4211 DrawDynamite(last_jx, last_jy);
4213 DrawLevelFieldThruMask(last_jx, last_jy);
4215 else if (last_element == EL_DYNAMITE_ACTIVE ||
4216 last_element == EL_EM_DYNAMITE_ACTIVE ||
4217 last_element == EL_SP_DISK_RED_ACTIVE)
4218 DrawDynamite(last_jx, last_jy);
4220 DrawLevelField(last_jx, last_jy);
4222 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4224 // ------------------------------------------------------------------------
4225 // draw things behind the player, if needed
4226 // ------------------------------------------------------------------------
4230 DrawLevelElement(jx, jy, Back[jx][jy]);
4235 if (IS_ACTIVE_BOMB(element))
4237 DrawLevelElement(jx, jy, EL_EMPTY);
4242 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4244 int old_element = GfxElement[jx][jy];
4245 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4246 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4248 if (GFX_CRUMBLED(old_element))
4249 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4251 DrawScreenGraphic(sx, sy, old_graphic, frame);
4253 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4254 static_player_is_opaque[pnr] = TRUE;
4258 GfxElement[jx][jy] = EL_UNDEFINED;
4260 // make sure that pushed elements are drawn with correct frame rate
4261 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4263 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4264 GfxFrame[jx][jy] = player->StepFrame;
4266 DrawLevelField(jx, jy);
4269 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4271 // ------------------------------------------------------------------------
4272 // draw things the player is pushing, if needed
4273 // ------------------------------------------------------------------------
4275 if (!player->is_pushing || !player->is_moving)
4278 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4281 int gfx_frame = GfxFrame[jx][jy];
4283 if (!IS_MOVING(jx, jy)) // push movement already finished
4285 element = Tile[next_jx][next_jy];
4286 gfx_frame = GfxFrame[next_jx][next_jy];
4289 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4290 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4291 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4293 // draw background element under pushed element (like the Sokoban field)
4294 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4296 // this allows transparent pushing animation over non-black background
4299 DrawLevelElement(jx, jy, Back[jx][jy]);
4301 DrawLevelElement(jx, jy, EL_EMPTY);
4304 if (Back[next_jx][next_jy])
4305 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4307 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4309 int px = SCREENX(jx), py = SCREENY(jy);
4310 int pxx = (TILEX - ABS(sxx)) * dx;
4311 int pyy = (TILEY - ABS(syy)) * dy;
4314 // do not draw (EM style) pushing animation when pushing is finished
4315 // (two-tile animations usually do not contain start and end frame)
4316 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4317 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4319 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4321 // masked drawing is needed for EMC style (double) movement graphics
4322 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4323 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4326 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4328 // ------------------------------------------------------------------------
4329 // draw player himself
4330 // ------------------------------------------------------------------------
4332 int graphic = getPlayerGraphic(player, move_dir);
4334 // in the case of changed player action or direction, prevent the current
4335 // animation frame from being restarted for identical animations
4336 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4337 player->Frame = last_player_frame;
4339 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4341 if (player_is_opaque)
4342 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4344 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4346 if (SHIELD_ON(player))
4348 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4349 IMG_SHIELD_NORMAL_ACTIVE);
4350 frame = getGraphicAnimationFrame(graphic, -1);
4352 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4355 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4357 // ------------------------------------------------------------------------
4358 // draw things in front of player (active dynamite or dynabombs)
4359 // ------------------------------------------------------------------------
4361 if (IS_ACTIVE_BOMB(element))
4363 int graphic = el2img(element);
4364 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4366 if (game.emulation == EMU_SUPAPLEX)
4367 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4369 DrawGraphicThruMask(sx, sy, graphic, frame);
4372 if (player_is_moving && last_element == EL_EXPLOSION)
4374 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4375 GfxElement[last_jx][last_jy] : EL_EMPTY);
4376 int graphic = el_act2img(element, ACTION_EXPLODING);
4377 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4378 int phase = ExplodePhase[last_jx][last_jy] - 1;
4379 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4382 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4385 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4387 // ------------------------------------------------------------------------
4388 // draw elements the player is just walking/passing through/under
4389 // ------------------------------------------------------------------------
4391 if (player_is_moving)
4393 // handle the field the player is leaving ...
4394 if (IS_ACCESSIBLE_INSIDE(last_element))
4395 DrawLevelField(last_jx, last_jy);
4396 else if (IS_ACCESSIBLE_UNDER(last_element))
4397 DrawLevelFieldThruMask(last_jx, last_jy);
4400 // do not redraw accessible elements if the player is just pushing them
4401 if (!player_is_moving || !player->is_pushing)
4403 // ... and the field the player is entering
4404 if (IS_ACCESSIBLE_INSIDE(element))
4405 DrawLevelField(jx, jy);
4406 else if (IS_ACCESSIBLE_UNDER(element))
4407 DrawLevelFieldThruMask(jx, jy);
4410 MarkTileDirty(sx, sy);
4414 void DrawPlayer(struct PlayerInfo *player)
4418 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4419 DrawPlayerExt(player, i);
4422 void DrawAllPlayers(void)
4426 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4427 for (j = 0; j < MAX_PLAYERS; j++)
4428 if (stored_player[j].active)
4429 DrawPlayerExt(&stored_player[j], i);
4432 void DrawPlayerField(int x, int y)
4434 if (!IS_PLAYER(x, y))
4437 DrawPlayer(PLAYERINFO(x, y));
4440 // ----------------------------------------------------------------------------
4442 void WaitForEventToContinue(void)
4444 boolean first_wait = TRUE;
4445 boolean still_wait = TRUE;
4447 if (program.headless)
4450 // simulate releasing mouse button over last gadget, if still pressed
4452 HandleGadgets(-1, -1, 0);
4454 button_status = MB_RELEASED;
4457 ClearPlayerAction();
4463 if (NextValidEvent(&event))
4467 case EVENT_BUTTONPRESS:
4468 case EVENT_FINGERPRESS:
4472 case EVENT_BUTTONRELEASE:
4473 case EVENT_FINGERRELEASE:
4474 still_wait = first_wait;
4477 case EVENT_KEYPRESS:
4478 case SDL_CONTROLLERBUTTONDOWN:
4479 case SDL_JOYBUTTONDOWN:
4484 HandleOtherEvents(&event);
4488 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4493 if (!PendingEvent())
4498 #define MAX_REQUEST_LINES 13
4499 #define MAX_REQUEST_LINE_FONT1_LEN 7
4500 #define MAX_REQUEST_LINE_FONT2_LEN 10
4502 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4504 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4506 int draw_buffer_last = GetDrawtoField();
4507 int width = request.width;
4508 int height = request.height;
4512 setRequestPosition(&sx, &sy, FALSE);
4514 button_status = MB_RELEASED;
4516 request_gadget_id = -1;
4521 if (game_just_ended)
4523 SetDrawtoField(draw_buffer_game);
4525 HandleGameActions();
4527 SetDrawtoField(DRAW_TO_BACKBUFFER);
4534 while (NextValidEvent(&event))
4538 case EVENT_BUTTONPRESS:
4539 case EVENT_BUTTONRELEASE:
4540 case EVENT_MOTIONNOTIFY:
4542 DrawBuffer *drawto_last = drawto;
4545 if (event.type == EVENT_MOTIONNOTIFY)
4550 motion_status = TRUE;
4551 mx = ((MotionEvent *) &event)->x;
4552 my = ((MotionEvent *) &event)->y;
4556 motion_status = FALSE;
4557 mx = ((ButtonEvent *) &event)->x;
4558 my = ((ButtonEvent *) &event)->y;
4559 if (event.type == EVENT_BUTTONPRESS)
4560 button_status = ((ButtonEvent *) &event)->button;
4562 button_status = MB_RELEASED;
4565 if (global.use_envelope_request)
4567 // draw changed button states to temporary bitmap
4568 drawto = bitmap_db_store_1;
4571 // this sets 'request_gadget_id'
4572 HandleGadgets(mx, my, button_status);
4574 if (global.use_envelope_request)
4576 // restore pointer to drawing buffer
4577 drawto = drawto_last;
4579 // prepare complete envelope request from temporary bitmap
4580 PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy,
4584 switch (request_gadget_id)
4586 case TOOL_CTRL_ID_YES:
4587 case TOOL_CTRL_ID_TOUCH_YES:
4590 case TOOL_CTRL_ID_NO:
4591 case TOOL_CTRL_ID_TOUCH_NO:
4594 case TOOL_CTRL_ID_CONFIRM:
4595 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4596 result = TRUE | FALSE;
4599 case TOOL_CTRL_ID_PLAYER_1:
4602 case TOOL_CTRL_ID_PLAYER_2:
4605 case TOOL_CTRL_ID_PLAYER_3:
4608 case TOOL_CTRL_ID_PLAYER_4:
4613 // only check clickable animations if no request gadget clicked
4614 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4621 case SDL_WINDOWEVENT:
4622 HandleWindowEvent((WindowEvent *) &event);
4625 case SDL_APP_WILLENTERBACKGROUND:
4626 case SDL_APP_DIDENTERBACKGROUND:
4627 case SDL_APP_WILLENTERFOREGROUND:
4628 case SDL_APP_DIDENTERFOREGROUND:
4629 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4632 case EVENT_KEYPRESS:
4634 Key key = GetEventKey((KeyEvent *)&event);
4639 if (req_state & REQ_CONFIRM)
4648 #if defined(KSYM_Rewind)
4649 case KSYM_Rewind: // for Amazon Fire TV remote
4658 #if defined(KSYM_FastForward)
4659 case KSYM_FastForward: // for Amazon Fire TV remote
4665 HandleKeysDebug(key, KEY_PRESSED);
4669 if (req_state & REQ_PLAYER)
4671 int old_player_nr = setup.network_player_nr;
4674 result = old_player_nr + 1;
4679 result = old_player_nr + 1;
4710 case EVENT_FINGERRELEASE:
4711 case EVENT_KEYRELEASE:
4712 ClearPlayerAction();
4715 case SDL_CONTROLLERBUTTONDOWN:
4716 switch (event.cbutton.button)
4718 case SDL_CONTROLLER_BUTTON_A:
4719 case SDL_CONTROLLER_BUTTON_X:
4720 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4721 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4725 case SDL_CONTROLLER_BUTTON_B:
4726 case SDL_CONTROLLER_BUTTON_Y:
4727 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4728 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4729 case SDL_CONTROLLER_BUTTON_BACK:
4734 if (req_state & REQ_PLAYER)
4736 int old_player_nr = setup.network_player_nr;
4739 result = old_player_nr + 1;
4741 switch (event.cbutton.button)
4743 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4744 case SDL_CONTROLLER_BUTTON_Y:
4748 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4749 case SDL_CONTROLLER_BUTTON_B:
4753 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4754 case SDL_CONTROLLER_BUTTON_A:
4758 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4759 case SDL_CONTROLLER_BUTTON_X:
4770 case SDL_CONTROLLERBUTTONUP:
4771 HandleJoystickEvent(&event);
4772 ClearPlayerAction();
4776 HandleOtherEvents(&event);
4781 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4783 int joy = AnyJoystick();
4785 if (joy & JOY_BUTTON_1)
4787 else if (joy & JOY_BUTTON_2)
4790 else if (AnyJoystick())
4792 int joy = AnyJoystick();
4794 if (req_state & REQ_PLAYER)
4798 else if (joy & JOY_RIGHT)
4800 else if (joy & JOY_DOWN)
4802 else if (joy & JOY_LEFT)
4810 SetDrawtoField(draw_buffer_last);
4815 static boolean RequestDoor(char *text, unsigned int req_state)
4817 int draw_buffer_last = GetDrawtoField();
4818 unsigned int old_door_state;
4819 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4820 int font_nr = FONT_TEXT_2;
4825 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4827 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4828 font_nr = FONT_TEXT_1;
4831 if (game_status == GAME_MODE_PLAYING)
4832 BlitScreenToBitmap(backbuffer);
4834 // disable deactivated drawing when quick-loading level tape recording
4835 if (tape.playing && tape.deactivate_display)
4836 TapeDeactivateDisplayOff(TRUE);
4838 SetMouseCursor(CURSOR_DEFAULT);
4840 // pause network game while waiting for request to answer
4841 if (network.enabled &&
4842 game_status == GAME_MODE_PLAYING &&
4843 !game.all_players_gone &&
4844 req_state & REQUEST_WAIT_FOR_INPUT)
4845 SendToServer_PausePlaying();
4847 old_door_state = GetDoorState();
4849 // simulate releasing mouse button over last gadget, if still pressed
4851 HandleGadgets(-1, -1, 0);
4855 // draw released gadget before proceeding
4858 if (old_door_state & DOOR_OPEN_1)
4860 CloseDoor(DOOR_CLOSE_1);
4862 // save old door content
4863 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4864 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4867 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4868 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4870 // clear door drawing field
4871 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4873 // force DOOR font inside door area
4874 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4876 // write text for request
4877 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4879 char text_line[max_request_line_len + 1];
4885 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4887 tc = *(text_ptr + tx);
4888 // if (!tc || tc == ' ')
4889 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4893 if ((tc == '?' || tc == '!') && tl == 0)
4903 strncpy(text_line, text_ptr, tl);
4906 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4907 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4908 text_line, font_nr);
4910 text_ptr += tl + (tc == ' ' ? 1 : 0);
4911 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4916 if (req_state & REQ_ASK)
4918 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4919 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4920 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4921 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4923 else if (req_state & REQ_CONFIRM)
4925 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4926 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4928 else if (req_state & REQ_PLAYER)
4930 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4931 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4932 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4933 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4936 // copy request gadgets to door backbuffer
4937 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4939 OpenDoor(DOOR_OPEN_1);
4941 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4943 if (game_status == GAME_MODE_PLAYING)
4945 SetPanelBackground();
4946 SetDrawBackgroundMask(REDRAW_DOOR_1);
4950 SetDrawBackgroundMask(REDRAW_FIELD);
4956 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4958 // ---------- handle request buttons ----------
4959 result = RequestHandleEvents(req_state, draw_buffer_last);
4963 if (!(req_state & REQ_STAY_OPEN))
4965 CloseDoor(DOOR_CLOSE_1);
4967 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4968 (req_state & REQ_REOPEN))
4969 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4974 if (game_status == GAME_MODE_PLAYING)
4976 SetPanelBackground();
4977 SetDrawBackgroundMask(REDRAW_DOOR_1);
4981 SetDrawBackgroundMask(REDRAW_FIELD);
4984 // continue network game after request
4985 if (network.enabled &&
4986 game_status == GAME_MODE_PLAYING &&
4987 !game.all_players_gone &&
4988 req_state & REQUEST_WAIT_FOR_INPUT)
4989 SendToServer_ContinuePlaying();
4991 // restore deactivated drawing when quick-loading level tape recording
4992 if (tape.playing && tape.deactivate_display)
4993 TapeDeactivateDisplayOn();
4998 static boolean RequestEnvelope(char *text, unsigned int req_state)
5000 int draw_buffer_last = GetDrawtoField();
5003 if (game_status == GAME_MODE_PLAYING)
5004 BlitScreenToBitmap(backbuffer);
5006 // disable deactivated drawing when quick-loading level tape recording
5007 if (tape.playing && tape.deactivate_display)
5008 TapeDeactivateDisplayOff(TRUE);
5010 SetMouseCursor(CURSOR_DEFAULT);
5012 // pause network game while waiting for request to answer
5013 if (network.enabled &&
5014 game_status == GAME_MODE_PLAYING &&
5015 !game.all_players_gone &&
5016 req_state & REQUEST_WAIT_FOR_INPUT)
5017 SendToServer_PausePlaying();
5019 // simulate releasing mouse button over last gadget, if still pressed
5021 HandleGadgets(-1, -1, 0);
5025 // (replace with setting corresponding request background)
5026 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5027 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5029 // clear door drawing field
5030 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5032 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5034 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5036 if (game_status == GAME_MODE_PLAYING)
5038 SetPanelBackground();
5039 SetDrawBackgroundMask(REDRAW_DOOR_1);
5043 SetDrawBackgroundMask(REDRAW_FIELD);
5049 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5051 // ---------- handle request buttons ----------
5052 result = RequestHandleEvents(req_state, draw_buffer_last);
5056 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5060 if (game_status == GAME_MODE_PLAYING)
5062 SetPanelBackground();
5063 SetDrawBackgroundMask(REDRAW_DOOR_1);
5067 SetDrawBackgroundMask(REDRAW_FIELD);
5070 // continue network game after request
5071 if (network.enabled &&
5072 game_status == GAME_MODE_PLAYING &&
5073 !game.all_players_gone &&
5074 req_state & REQUEST_WAIT_FOR_INPUT)
5075 SendToServer_ContinuePlaying();
5077 // restore deactivated drawing when quick-loading level tape recording
5078 if (tape.playing && tape.deactivate_display)
5079 TapeDeactivateDisplayOn();
5084 boolean Request(char *text, unsigned int req_state)
5086 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
5088 boolean overlay_enabled = GetOverlayEnabled();
5091 // when showing request dialog after game ended, deactivate game panel
5092 if (game_just_ended)
5093 game.panel.active = FALSE;
5095 game.request_active = TRUE;
5097 SetOverlayEnabled(FALSE);
5099 if (global.use_envelope_request)
5100 result = RequestEnvelope(text, req_state);
5102 result = RequestDoor(text, req_state);
5104 SetOverlayEnabled(overlay_enabled);
5106 game.request_active = FALSE;
5111 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5113 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5114 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5117 if (dpo1->sort_priority != dpo2->sort_priority)
5118 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5120 compare_result = dpo1->nr - dpo2->nr;
5122 return compare_result;
5125 void InitGraphicCompatibilityInfo_Doors(void)
5131 struct DoorInfo *door;
5135 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5136 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5138 { -1, -1, -1, NULL }
5140 struct Rect door_rect_list[] =
5142 { DX, DY, DXSIZE, DYSIZE },
5143 { VX, VY, VXSIZE, VYSIZE }
5147 for (i = 0; doors[i].door_token != -1; i++)
5149 int door_token = doors[i].door_token;
5150 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5151 int part_1 = doors[i].part_1;
5152 int part_8 = doors[i].part_8;
5153 int part_2 = part_1 + 1;
5154 int part_3 = part_1 + 2;
5155 struct DoorInfo *door = doors[i].door;
5156 struct Rect *door_rect = &door_rect_list[door_index];
5157 boolean door_gfx_redefined = FALSE;
5159 // check if any door part graphic definitions have been redefined
5161 for (j = 0; door_part_controls[j].door_token != -1; j++)
5163 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5164 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5166 if (dpc->door_token == door_token && fi->redefined)
5167 door_gfx_redefined = TRUE;
5170 // check for old-style door graphic/animation modifications
5172 if (!door_gfx_redefined)
5174 if (door->anim_mode & ANIM_STATIC_PANEL)
5176 door->panel.step_xoffset = 0;
5177 door->panel.step_yoffset = 0;
5180 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5182 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5183 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5184 int num_door_steps, num_panel_steps;
5186 // remove door part graphics other than the two default wings
5188 for (j = 0; door_part_controls[j].door_token != -1; j++)
5190 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5191 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5193 if (dpc->graphic >= part_3 &&
5194 dpc->graphic <= part_8)
5198 // set graphics and screen positions of the default wings
5200 g_part_1->width = door_rect->width;
5201 g_part_1->height = door_rect->height;
5202 g_part_2->width = door_rect->width;
5203 g_part_2->height = door_rect->height;
5204 g_part_2->src_x = door_rect->width;
5205 g_part_2->src_y = g_part_1->src_y;
5207 door->part_2.x = door->part_1.x;
5208 door->part_2.y = door->part_1.y;
5210 if (door->width != -1)
5212 g_part_1->width = door->width;
5213 g_part_2->width = door->width;
5215 // special treatment for graphics and screen position of right wing
5216 g_part_2->src_x += door_rect->width - door->width;
5217 door->part_2.x += door_rect->width - door->width;
5220 if (door->height != -1)
5222 g_part_1->height = door->height;
5223 g_part_2->height = door->height;
5225 // special treatment for graphics and screen position of bottom wing
5226 g_part_2->src_y += door_rect->height - door->height;
5227 door->part_2.y += door_rect->height - door->height;
5230 // set animation delays for the default wings and panels
5232 door->part_1.step_delay = door->step_delay;
5233 door->part_2.step_delay = door->step_delay;
5234 door->panel.step_delay = door->step_delay;
5236 // set animation draw order for the default wings
5238 door->part_1.sort_priority = 2; // draw left wing over ...
5239 door->part_2.sort_priority = 1; // ... right wing
5241 // set animation draw offset for the default wings
5243 if (door->anim_mode & ANIM_HORIZONTAL)
5245 door->part_1.step_xoffset = door->step_offset;
5246 door->part_1.step_yoffset = 0;
5247 door->part_2.step_xoffset = door->step_offset * -1;
5248 door->part_2.step_yoffset = 0;
5250 num_door_steps = g_part_1->width / door->step_offset;
5252 else // ANIM_VERTICAL
5254 door->part_1.step_xoffset = 0;
5255 door->part_1.step_yoffset = door->step_offset;
5256 door->part_2.step_xoffset = 0;
5257 door->part_2.step_yoffset = door->step_offset * -1;
5259 num_door_steps = g_part_1->height / door->step_offset;
5262 // set animation draw offset for the default panels
5264 if (door->step_offset > 1)
5266 num_panel_steps = 2 * door_rect->height / door->step_offset;
5267 door->panel.start_step = num_panel_steps - num_door_steps;
5268 door->panel.start_step_closing = door->panel.start_step;
5272 num_panel_steps = door_rect->height / door->step_offset;
5273 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5274 door->panel.start_step_closing = door->panel.start_step;
5275 door->panel.step_delay *= 2;
5282 void InitDoors(void)
5286 for (i = 0; door_part_controls[i].door_token != -1; i++)
5288 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5289 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5291 // initialize "start_step_opening" and "start_step_closing", if needed
5292 if (dpc->pos->start_step_opening == 0 &&
5293 dpc->pos->start_step_closing == 0)
5295 // dpc->pos->start_step_opening = dpc->pos->start_step;
5296 dpc->pos->start_step_closing = dpc->pos->start_step;
5299 // fill structure for door part draw order (sorted below)
5301 dpo->sort_priority = dpc->pos->sort_priority;
5304 // sort door part controls according to sort_priority and graphic number
5305 qsort(door_part_order, MAX_DOOR_PARTS,
5306 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5309 unsigned int OpenDoor(unsigned int door_state)
5311 if (door_state & DOOR_COPY_BACK)
5313 if (door_state & DOOR_OPEN_1)
5314 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5315 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5317 if (door_state & DOOR_OPEN_2)
5318 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5319 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5321 door_state &= ~DOOR_COPY_BACK;
5324 return MoveDoor(door_state);
5327 unsigned int CloseDoor(unsigned int door_state)
5329 unsigned int old_door_state = GetDoorState();
5331 if (!(door_state & DOOR_NO_COPY_BACK))
5333 if (old_door_state & DOOR_OPEN_1)
5334 BlitBitmap(backbuffer, bitmap_db_door_1,
5335 DX, DY, DXSIZE, DYSIZE, 0, 0);
5337 if (old_door_state & DOOR_OPEN_2)
5338 BlitBitmap(backbuffer, bitmap_db_door_2,
5339 VX, VY, VXSIZE, VYSIZE, 0, 0);
5341 door_state &= ~DOOR_NO_COPY_BACK;
5344 return MoveDoor(door_state);
5347 unsigned int GetDoorState(void)
5349 return MoveDoor(DOOR_GET_STATE);
5352 unsigned int SetDoorState(unsigned int door_state)
5354 return MoveDoor(door_state | DOOR_SET_STATE);
5357 static int euclid(int a, int b)
5359 return (b ? euclid(b, a % b) : a);
5362 unsigned int MoveDoor(unsigned int door_state)
5364 struct Rect door_rect_list[] =
5366 { DX, DY, DXSIZE, DYSIZE },
5367 { VX, VY, VXSIZE, VYSIZE }
5369 static int door1 = DOOR_CLOSE_1;
5370 static int door2 = DOOR_CLOSE_2;
5371 DelayCounter door_delay = { 0 };
5374 if (door_state == DOOR_GET_STATE)
5375 return (door1 | door2);
5377 if (door_state & DOOR_SET_STATE)
5379 if (door_state & DOOR_ACTION_1)
5380 door1 = door_state & DOOR_ACTION_1;
5381 if (door_state & DOOR_ACTION_2)
5382 door2 = door_state & DOOR_ACTION_2;
5384 return (door1 | door2);
5387 if (!(door_state & DOOR_FORCE_REDRAW))
5389 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5390 door_state &= ~DOOR_OPEN_1;
5391 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5392 door_state &= ~DOOR_CLOSE_1;
5393 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5394 door_state &= ~DOOR_OPEN_2;
5395 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5396 door_state &= ~DOOR_CLOSE_2;
5399 if (global.autoplay_leveldir)
5401 door_state |= DOOR_NO_DELAY;
5402 door_state &= ~DOOR_CLOSE_ALL;
5405 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5406 door_state |= DOOR_NO_DELAY;
5408 if (door_state & DOOR_ACTION)
5410 boolean door_panel_drawn[NUM_DOORS];
5411 boolean panel_has_doors[NUM_DOORS];
5412 boolean door_part_skip[MAX_DOOR_PARTS];
5413 boolean door_part_done[MAX_DOOR_PARTS];
5414 boolean door_part_done_all;
5415 int num_steps[MAX_DOOR_PARTS];
5416 int max_move_delay = 0; // delay for complete animations of all doors
5417 int max_step_delay = 0; // delay (ms) between two animation frames
5418 int num_move_steps = 0; // number of animation steps for all doors
5419 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5420 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5424 for (i = 0; i < NUM_DOORS; i++)
5425 panel_has_doors[i] = FALSE;
5427 for (i = 0; i < MAX_DOOR_PARTS; i++)
5429 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5430 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5431 int door_token = dpc->door_token;
5433 door_part_done[i] = FALSE;
5434 door_part_skip[i] = (!(door_state & door_token) ||
5438 for (i = 0; i < MAX_DOOR_PARTS; i++)
5440 int nr = door_part_order[i].nr;
5441 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5442 struct DoorPartPosInfo *pos = dpc->pos;
5443 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5444 int door_token = dpc->door_token;
5445 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5446 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5447 int step_xoffset = ABS(pos->step_xoffset);
5448 int step_yoffset = ABS(pos->step_yoffset);
5449 int step_delay = pos->step_delay;
5450 int current_door_state = door_state & door_token;
5451 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5452 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5453 boolean part_opening = (is_panel ? door_closing : door_opening);
5454 int start_step = (part_opening ? pos->start_step_opening :
5455 pos->start_step_closing);
5456 float move_xsize = (step_xoffset ? g->width : 0);
5457 float move_ysize = (step_yoffset ? g->height : 0);
5458 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5459 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5460 int move_steps = (move_xsteps && move_ysteps ?
5461 MIN(move_xsteps, move_ysteps) :
5462 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5463 int move_delay = move_steps * step_delay;
5465 if (door_part_skip[nr])
5468 max_move_delay = MAX(max_move_delay, move_delay);
5469 max_step_delay = (max_step_delay == 0 ? step_delay :
5470 euclid(max_step_delay, step_delay));
5471 num_steps[nr] = move_steps;
5475 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5477 panel_has_doors[door_index] = TRUE;
5481 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5483 num_move_steps = max_move_delay / max_step_delay;
5484 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5486 door_delay.value = max_step_delay;
5488 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5490 start = num_move_steps - 1;
5494 // opening door sound has priority over simultaneously closing door
5495 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5497 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5499 if (door_state & DOOR_OPEN_1)
5500 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5501 if (door_state & DOOR_OPEN_2)
5502 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5504 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5506 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5508 if (door_state & DOOR_CLOSE_1)
5509 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5510 if (door_state & DOOR_CLOSE_2)
5511 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5515 for (k = start; k < num_move_steps; k++)
5517 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5519 door_part_done_all = TRUE;
5521 for (i = 0; i < NUM_DOORS; i++)
5522 door_panel_drawn[i] = FALSE;
5524 for (i = 0; i < MAX_DOOR_PARTS; i++)
5526 int nr = door_part_order[i].nr;
5527 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5528 struct DoorPartPosInfo *pos = dpc->pos;
5529 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5530 int door_token = dpc->door_token;
5531 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5532 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5533 boolean is_panel_and_door_has_closed = FALSE;
5534 struct Rect *door_rect = &door_rect_list[door_index];
5535 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5537 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5538 int current_door_state = door_state & door_token;
5539 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5540 boolean door_closing = !door_opening;
5541 boolean part_opening = (is_panel ? door_closing : door_opening);
5542 boolean part_closing = !part_opening;
5543 int start_step = (part_opening ? pos->start_step_opening :
5544 pos->start_step_closing);
5545 int step_delay = pos->step_delay;
5546 int step_factor = step_delay / max_step_delay;
5547 int k1 = (step_factor ? k / step_factor + 1 : k);
5548 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5549 int kk = MAX(0, k2);
5552 int src_x, src_y, src_xx, src_yy;
5553 int dst_x, dst_y, dst_xx, dst_yy;
5556 if (door_part_skip[nr])
5559 if (!(door_state & door_token))
5567 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5568 int kk_door = MAX(0, k2_door);
5569 int sync_frame = kk_door * door_delay.value;
5570 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5572 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5573 &g_src_x, &g_src_y);
5578 if (!door_panel_drawn[door_index])
5580 ClearRectangle(drawto, door_rect->x, door_rect->y,
5581 door_rect->width, door_rect->height);
5583 door_panel_drawn[door_index] = TRUE;
5586 // draw opening or closing door parts
5588 if (pos->step_xoffset < 0) // door part on right side
5591 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5594 if (dst_xx + width > door_rect->width)
5595 width = door_rect->width - dst_xx;
5597 else // door part on left side
5600 dst_xx = pos->x - kk * pos->step_xoffset;
5604 src_xx = ABS(dst_xx);
5608 width = g->width - src_xx;
5610 if (width > door_rect->width)
5611 width = door_rect->width;
5613 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5616 if (pos->step_yoffset < 0) // door part on bottom side
5619 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5622 if (dst_yy + height > door_rect->height)
5623 height = door_rect->height - dst_yy;
5625 else // door part on top side
5628 dst_yy = pos->y - kk * pos->step_yoffset;
5632 src_yy = ABS(dst_yy);
5636 height = g->height - src_yy;
5639 src_x = g_src_x + src_xx;
5640 src_y = g_src_y + src_yy;
5642 dst_x = door_rect->x + dst_xx;
5643 dst_y = door_rect->y + dst_yy;
5645 is_panel_and_door_has_closed =
5648 panel_has_doors[door_index] &&
5649 k >= num_move_steps_doors_only - 1);
5651 if (width >= 0 && width <= g->width &&
5652 height >= 0 && height <= g->height &&
5653 !is_panel_and_door_has_closed)
5655 if (is_panel || !pos->draw_masked)
5656 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5659 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5663 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5665 if ((part_opening && (width < 0 || height < 0)) ||
5666 (part_closing && (width >= g->width && height >= g->height)))
5667 door_part_done[nr] = TRUE;
5669 // continue door part animations, but not panel after door has closed
5670 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5671 door_part_done_all = FALSE;
5674 if (!(door_state & DOOR_NO_DELAY))
5678 SkipUntilDelayReached(&door_delay, &k, last_frame);
5680 // prevent OS (Windows) from complaining about program not responding
5684 if (door_part_done_all)
5688 if (!(door_state & DOOR_NO_DELAY))
5690 // wait for specified door action post delay
5691 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5692 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5693 else if (door_state & DOOR_ACTION_1)
5694 door_delay.value = door_1.post_delay;
5695 else if (door_state & DOOR_ACTION_2)
5696 door_delay.value = door_2.post_delay;
5698 while (!DelayReached(&door_delay))
5703 if (door_state & DOOR_ACTION_1)
5704 door1 = door_state & DOOR_ACTION_1;
5705 if (door_state & DOOR_ACTION_2)
5706 door2 = door_state & DOOR_ACTION_2;
5708 // draw masked border over door area
5709 DrawMaskedBorder(REDRAW_DOOR_1);
5710 DrawMaskedBorder(REDRAW_DOOR_2);
5712 ClearAutoRepeatKeyEvents();
5714 return (door1 | door2);
5717 static boolean useSpecialEditorDoor(void)
5719 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5720 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5722 // do not draw special editor door if editor border defined or redefined
5723 if (graphic_info[graphic].bitmap != NULL || redefined)
5726 // do not draw special editor door if global border defined to be empty
5727 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5730 // do not draw special editor door if viewport definitions do not match
5734 EY + EYSIZE != VY + VYSIZE)
5740 void DrawSpecialEditorDoor(void)
5742 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5743 int top_border_width = gfx1->width;
5744 int top_border_height = gfx1->height;
5745 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5746 int ex = EX - outer_border;
5747 int ey = EY - outer_border;
5748 int vy = VY - outer_border;
5749 int exsize = EXSIZE + 2 * outer_border;
5751 if (!useSpecialEditorDoor())
5754 // draw bigger level editor toolbox window
5755 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5756 top_border_width, top_border_height, ex, ey - top_border_height);
5757 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5758 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5760 redraw_mask |= REDRAW_ALL;
5763 void UndrawSpecialEditorDoor(void)
5765 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5766 int top_border_width = gfx1->width;
5767 int top_border_height = gfx1->height;
5768 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5769 int ex = EX - outer_border;
5770 int ey = EY - outer_border;
5771 int ey_top = ey - top_border_height;
5772 int exsize = EXSIZE + 2 * outer_border;
5773 int eysize = EYSIZE + 2 * outer_border;
5775 if (!useSpecialEditorDoor())
5778 // draw normal tape recorder window
5779 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5781 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5782 ex, ey_top, top_border_width, top_border_height,
5784 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5785 ex, ey, exsize, eysize, ex, ey);
5789 // if screen background is set to "[NONE]", clear editor toolbox window
5790 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5791 ClearRectangle(drawto, ex, ey, exsize, eysize);
5794 redraw_mask |= REDRAW_ALL;
5798 // ---------- new tool button stuff -------------------------------------------
5803 struct TextPosInfo *pos;
5805 boolean is_touch_button;
5807 } toolbutton_info[NUM_TOOL_BUTTONS] =
5810 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5811 TOOL_CTRL_ID_YES, FALSE, "yes"
5814 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5815 TOOL_CTRL_ID_NO, FALSE, "no"
5818 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5819 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5822 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5823 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5826 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5827 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5830 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5831 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5834 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5835 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5838 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5839 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5842 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5843 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5846 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5847 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5851 void CreateToolButtons(void)
5855 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5857 int graphic = toolbutton_info[i].graphic;
5858 struct GraphicInfo *gfx = &graphic_info[graphic];
5859 struct TextPosInfo *pos = toolbutton_info[i].pos;
5860 struct GadgetInfo *gi;
5861 Bitmap *deco_bitmap = None;
5862 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5863 unsigned int event_mask = GD_EVENT_RELEASED;
5864 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5865 int base_x = (is_touch_button ? 0 : DX);
5866 int base_y = (is_touch_button ? 0 : DY);
5867 int gd_x = gfx->src_x;
5868 int gd_y = gfx->src_y;
5869 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5870 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5875 // do not use touch buttons if overlay touch buttons are disabled
5876 if (is_touch_button && !setup.touch.overlay_buttons)
5879 if (global.use_envelope_request && !is_touch_button)
5881 setRequestPosition(&base_x, &base_y, TRUE);
5883 // check if request buttons are outside of envelope and fix, if needed
5884 if (x < 0 || x + gfx->width > request.width ||
5885 y < 0 || y + gfx->height > request.height)
5887 if (id == TOOL_CTRL_ID_YES)
5890 y = request.height - 2 * request.border_size - gfx->height;
5892 else if (id == TOOL_CTRL_ID_NO)
5894 x = request.width - 2 * request.border_size - gfx->width;
5895 y = request.height - 2 * request.border_size - gfx->height;
5897 else if (id == TOOL_CTRL_ID_CONFIRM)
5899 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5900 y = request.height - 2 * request.border_size - gfx->height;
5902 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5904 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5906 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5907 y = request.height - 2 * request.border_size - gfx->height * 2;
5909 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5910 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5915 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5918 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5920 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5921 pos->size, &deco_bitmap, &deco_x, &deco_y);
5922 deco_xpos = (gfx->width - pos->size) / 2;
5923 deco_ypos = (gfx->height - pos->size) / 2;
5926 gi = CreateGadget(GDI_CUSTOM_ID, id,
5927 GDI_IMAGE_ID, graphic,
5928 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5931 GDI_WIDTH, gfx->width,
5932 GDI_HEIGHT, gfx->height,
5933 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5934 GDI_STATE, GD_BUTTON_UNPRESSED,
5935 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5936 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5937 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5938 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5939 GDI_DECORATION_SIZE, pos->size, pos->size,
5940 GDI_DECORATION_SHIFTING, 1, 1,
5941 GDI_DIRECT_DRAW, FALSE,
5942 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5943 GDI_EVENT_MASK, event_mask,
5944 GDI_CALLBACK_ACTION, HandleToolButtons,
5948 Fail("cannot create gadget");
5950 tool_gadget[id] = gi;
5954 void FreeToolButtons(void)
5958 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5959 FreeGadget(tool_gadget[i]);
5962 static void UnmapToolButtons(void)
5966 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5967 UnmapGadget(tool_gadget[i]);
5970 static void HandleToolButtons(struct GadgetInfo *gi)
5972 request_gadget_id = gi->custom_id;
5975 static struct Mapping_EM_to_RND_object
5978 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5979 boolean is_backside; // backside of moving element
5985 em_object_mapping_list[GAME_TILE_MAX + 1] =
5988 Zborder, FALSE, FALSE,
5992 Zplayer, FALSE, FALSE,
6001 Ztank, FALSE, FALSE,
6005 Zeater, FALSE, FALSE,
6009 Zdynamite, FALSE, FALSE,
6013 Zboom, FALSE, FALSE,
6018 Xchain, FALSE, FALSE,
6019 EL_DEFAULT, ACTION_EXPLODING, -1
6022 Xboom_bug, FALSE, FALSE,
6023 EL_BUG, ACTION_EXPLODING, -1
6026 Xboom_tank, FALSE, FALSE,
6027 EL_SPACESHIP, ACTION_EXPLODING, -1
6030 Xboom_android, FALSE, FALSE,
6031 EL_EMC_ANDROID, ACTION_OTHER, -1
6034 Xboom_1, FALSE, FALSE,
6035 EL_DEFAULT, ACTION_EXPLODING, -1
6038 Xboom_2, FALSE, FALSE,
6039 EL_DEFAULT, ACTION_EXPLODING, -1
6043 Xblank, TRUE, FALSE,
6048 Xsplash_e, FALSE, FALSE,
6049 EL_ACID_SPLASH_RIGHT, -1, -1
6052 Xsplash_w, FALSE, FALSE,
6053 EL_ACID_SPLASH_LEFT, -1, -1
6057 Xplant, TRUE, FALSE,
6058 EL_EMC_PLANT, -1, -1
6061 Yplant, FALSE, FALSE,
6062 EL_EMC_PLANT, -1, -1
6066 Xacid_1, TRUE, FALSE,
6070 Xacid_2, FALSE, FALSE,
6074 Xacid_3, FALSE, FALSE,
6078 Xacid_4, FALSE, FALSE,
6082 Xacid_5, FALSE, FALSE,
6086 Xacid_6, FALSE, FALSE,
6090 Xacid_7, FALSE, FALSE,
6094 Xacid_8, FALSE, FALSE,
6099 Xfake_acid_1, TRUE, FALSE,
6100 EL_EMC_FAKE_ACID, -1, -1
6103 Xfake_acid_2, FALSE, FALSE,
6104 EL_EMC_FAKE_ACID, -1, -1
6107 Xfake_acid_3, FALSE, FALSE,
6108 EL_EMC_FAKE_ACID, -1, -1
6111 Xfake_acid_4, FALSE, FALSE,
6112 EL_EMC_FAKE_ACID, -1, -1
6115 Xfake_acid_5, FALSE, FALSE,
6116 EL_EMC_FAKE_ACID, -1, -1
6119 Xfake_acid_6, FALSE, FALSE,
6120 EL_EMC_FAKE_ACID, -1, -1
6123 Xfake_acid_7, FALSE, FALSE,
6124 EL_EMC_FAKE_ACID, -1, -1
6127 Xfake_acid_8, FALSE, FALSE,
6128 EL_EMC_FAKE_ACID, -1, -1
6132 Xfake_acid_1_player, FALSE, FALSE,
6133 EL_EMC_FAKE_ACID, -1, -1
6136 Xfake_acid_2_player, FALSE, FALSE,
6137 EL_EMC_FAKE_ACID, -1, -1
6140 Xfake_acid_3_player, FALSE, FALSE,
6141 EL_EMC_FAKE_ACID, -1, -1
6144 Xfake_acid_4_player, FALSE, FALSE,
6145 EL_EMC_FAKE_ACID, -1, -1
6148 Xfake_acid_5_player, FALSE, FALSE,
6149 EL_EMC_FAKE_ACID, -1, -1
6152 Xfake_acid_6_player, FALSE, FALSE,
6153 EL_EMC_FAKE_ACID, -1, -1
6156 Xfake_acid_7_player, FALSE, FALSE,
6157 EL_EMC_FAKE_ACID, -1, -1
6160 Xfake_acid_8_player, FALSE, FALSE,
6161 EL_EMC_FAKE_ACID, -1, -1
6165 Xgrass, TRUE, FALSE,
6166 EL_EMC_GRASS, -1, -1
6169 Ygrass_nB, FALSE, FALSE,
6170 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6173 Ygrass_eB, FALSE, FALSE,
6174 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6177 Ygrass_sB, FALSE, FALSE,
6178 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6181 Ygrass_wB, FALSE, FALSE,
6182 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6190 Ydirt_nB, FALSE, FALSE,
6191 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6194 Ydirt_eB, FALSE, FALSE,
6195 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6198 Ydirt_sB, FALSE, FALSE,
6199 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6202 Ydirt_wB, FALSE, FALSE,
6203 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6207 Xandroid, TRUE, FALSE,
6208 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6211 Xandroid_1_n, FALSE, FALSE,
6212 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6215 Xandroid_2_n, FALSE, FALSE,
6216 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6219 Xandroid_1_e, FALSE, FALSE,
6220 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6223 Xandroid_2_e, FALSE, FALSE,
6224 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6227 Xandroid_1_w, FALSE, FALSE,
6228 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6231 Xandroid_2_w, FALSE, FALSE,
6232 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6235 Xandroid_1_s, FALSE, FALSE,
6236 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6239 Xandroid_2_s, FALSE, FALSE,
6240 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6243 Yandroid_n, FALSE, FALSE,
6244 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6247 Yandroid_nB, FALSE, TRUE,
6248 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6251 Yandroid_ne, FALSE, FALSE,
6252 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6255 Yandroid_neB, FALSE, TRUE,
6256 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6259 Yandroid_e, FALSE, FALSE,
6260 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6263 Yandroid_eB, FALSE, TRUE,
6264 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6267 Yandroid_se, FALSE, FALSE,
6268 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6271 Yandroid_seB, FALSE, TRUE,
6272 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6275 Yandroid_s, FALSE, FALSE,
6276 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6279 Yandroid_sB, FALSE, TRUE,
6280 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6283 Yandroid_sw, FALSE, FALSE,
6284 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6287 Yandroid_swB, FALSE, TRUE,
6288 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6291 Yandroid_w, FALSE, FALSE,
6292 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6295 Yandroid_wB, FALSE, TRUE,
6296 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6299 Yandroid_nw, FALSE, FALSE,
6300 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6303 Yandroid_nwB, FALSE, TRUE,
6304 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6308 Xeater_n, TRUE, FALSE,
6309 EL_YAMYAM_UP, -1, -1
6312 Xeater_e, TRUE, FALSE,
6313 EL_YAMYAM_RIGHT, -1, -1
6316 Xeater_w, TRUE, FALSE,
6317 EL_YAMYAM_LEFT, -1, -1
6320 Xeater_s, TRUE, FALSE,
6321 EL_YAMYAM_DOWN, -1, -1
6324 Yeater_n, FALSE, FALSE,
6325 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6328 Yeater_nB, FALSE, TRUE,
6329 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6332 Yeater_e, FALSE, FALSE,
6333 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6336 Yeater_eB, FALSE, TRUE,
6337 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6340 Yeater_s, FALSE, FALSE,
6341 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6344 Yeater_sB, FALSE, TRUE,
6345 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6348 Yeater_w, FALSE, FALSE,
6349 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6352 Yeater_wB, FALSE, TRUE,
6353 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6356 Yeater_stone, FALSE, FALSE,
6357 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6360 Yeater_spring, FALSE, FALSE,
6361 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6365 Xalien, TRUE, FALSE,
6369 Xalien_pause, FALSE, FALSE,
6373 Yalien_n, FALSE, FALSE,
6374 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6377 Yalien_nB, FALSE, TRUE,
6378 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6381 Yalien_e, FALSE, FALSE,
6382 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6385 Yalien_eB, FALSE, TRUE,
6386 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6389 Yalien_s, FALSE, FALSE,
6390 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6393 Yalien_sB, FALSE, TRUE,
6394 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6397 Yalien_w, FALSE, FALSE,
6398 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6401 Yalien_wB, FALSE, TRUE,
6402 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6405 Yalien_stone, FALSE, FALSE,
6406 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6409 Yalien_spring, FALSE, FALSE,
6410 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6414 Xbug_1_n, TRUE, FALSE,
6418 Xbug_1_e, TRUE, FALSE,
6419 EL_BUG_RIGHT, -1, -1
6422 Xbug_1_s, TRUE, FALSE,
6426 Xbug_1_w, TRUE, FALSE,
6430 Xbug_2_n, FALSE, FALSE,
6434 Xbug_2_e, FALSE, FALSE,
6435 EL_BUG_RIGHT, -1, -1
6438 Xbug_2_s, FALSE, FALSE,
6442 Xbug_2_w, FALSE, FALSE,
6446 Ybug_n, FALSE, FALSE,
6447 EL_BUG, ACTION_MOVING, MV_BIT_UP
6450 Ybug_nB, FALSE, TRUE,
6451 EL_BUG, ACTION_MOVING, MV_BIT_UP
6454 Ybug_e, FALSE, FALSE,
6455 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6458 Ybug_eB, FALSE, TRUE,
6459 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6462 Ybug_s, FALSE, FALSE,
6463 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6466 Ybug_sB, FALSE, TRUE,
6467 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6470 Ybug_w, FALSE, FALSE,
6471 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6474 Ybug_wB, FALSE, TRUE,
6475 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6478 Ybug_w_n, FALSE, FALSE,
6479 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6482 Ybug_n_e, FALSE, FALSE,
6483 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6486 Ybug_e_s, FALSE, FALSE,
6487 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6490 Ybug_s_w, FALSE, FALSE,
6491 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6494 Ybug_e_n, FALSE, FALSE,
6495 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6498 Ybug_s_e, FALSE, FALSE,
6499 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6502 Ybug_w_s, FALSE, FALSE,
6503 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6506 Ybug_n_w, FALSE, FALSE,
6507 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6510 Ybug_stone, FALSE, FALSE,
6511 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6514 Ybug_spring, FALSE, FALSE,
6515 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6519 Xtank_1_n, TRUE, FALSE,
6520 EL_SPACESHIP_UP, -1, -1
6523 Xtank_1_e, TRUE, FALSE,
6524 EL_SPACESHIP_RIGHT, -1, -1
6527 Xtank_1_s, TRUE, FALSE,
6528 EL_SPACESHIP_DOWN, -1, -1
6531 Xtank_1_w, TRUE, FALSE,
6532 EL_SPACESHIP_LEFT, -1, -1
6535 Xtank_2_n, FALSE, FALSE,
6536 EL_SPACESHIP_UP, -1, -1
6539 Xtank_2_e, FALSE, FALSE,
6540 EL_SPACESHIP_RIGHT, -1, -1
6543 Xtank_2_s, FALSE, FALSE,
6544 EL_SPACESHIP_DOWN, -1, -1
6547 Xtank_2_w, FALSE, FALSE,
6548 EL_SPACESHIP_LEFT, -1, -1
6551 Ytank_n, FALSE, FALSE,
6552 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6555 Ytank_nB, FALSE, TRUE,
6556 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6559 Ytank_e, FALSE, FALSE,
6560 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6563 Ytank_eB, FALSE, TRUE,
6564 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6567 Ytank_s, FALSE, FALSE,
6568 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6571 Ytank_sB, FALSE, TRUE,
6572 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6575 Ytank_w, FALSE, FALSE,
6576 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6579 Ytank_wB, FALSE, TRUE,
6580 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6583 Ytank_w_n, FALSE, FALSE,
6584 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6587 Ytank_n_e, FALSE, FALSE,
6588 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6591 Ytank_e_s, FALSE, FALSE,
6592 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6595 Ytank_s_w, FALSE, FALSE,
6596 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6599 Ytank_e_n, FALSE, FALSE,
6600 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6603 Ytank_s_e, FALSE, FALSE,
6604 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6607 Ytank_w_s, FALSE, FALSE,
6608 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6611 Ytank_n_w, FALSE, FALSE,
6612 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6615 Ytank_stone, FALSE, FALSE,
6616 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6619 Ytank_spring, FALSE, FALSE,
6620 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6624 Xemerald, TRUE, FALSE,
6628 Xemerald_pause, FALSE, FALSE,
6632 Xemerald_fall, FALSE, FALSE,
6636 Xemerald_shine, FALSE, FALSE,
6637 EL_EMERALD, ACTION_TWINKLING, -1
6640 Yemerald_s, FALSE, FALSE,
6641 EL_EMERALD, ACTION_FALLING, -1
6644 Yemerald_sB, FALSE, TRUE,
6645 EL_EMERALD, ACTION_FALLING, -1
6648 Yemerald_e, FALSE, FALSE,
6649 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6652 Yemerald_eB, FALSE, TRUE,
6653 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6656 Yemerald_w, FALSE, FALSE,
6657 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6660 Yemerald_wB, FALSE, TRUE,
6661 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6664 Yemerald_blank, FALSE, FALSE,
6665 EL_EMERALD, ACTION_COLLECTING, -1
6669 Xdiamond, TRUE, FALSE,
6673 Xdiamond_pause, FALSE, FALSE,
6677 Xdiamond_fall, FALSE, FALSE,
6681 Xdiamond_shine, FALSE, FALSE,
6682 EL_DIAMOND, ACTION_TWINKLING, -1
6685 Ydiamond_s, FALSE, FALSE,
6686 EL_DIAMOND, ACTION_FALLING, -1
6689 Ydiamond_sB, FALSE, TRUE,
6690 EL_DIAMOND, ACTION_FALLING, -1
6693 Ydiamond_e, FALSE, FALSE,
6694 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6697 Ydiamond_eB, FALSE, TRUE,
6698 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6701 Ydiamond_w, FALSE, FALSE,
6702 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6705 Ydiamond_wB, FALSE, TRUE,
6706 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6709 Ydiamond_blank, FALSE, FALSE,
6710 EL_DIAMOND, ACTION_COLLECTING, -1
6713 Ydiamond_stone, FALSE, FALSE,
6714 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6718 Xstone, TRUE, FALSE,
6722 Xstone_pause, FALSE, FALSE,
6726 Xstone_fall, FALSE, FALSE,
6730 Ystone_s, FALSE, FALSE,
6731 EL_ROCK, ACTION_FALLING, -1
6734 Ystone_sB, FALSE, TRUE,
6735 EL_ROCK, ACTION_FALLING, -1
6738 Ystone_e, FALSE, FALSE,
6739 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6742 Ystone_eB, FALSE, TRUE,
6743 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6746 Ystone_w, FALSE, FALSE,
6747 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6750 Ystone_wB, FALSE, TRUE,
6751 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6759 Xbomb_pause, FALSE, FALSE,
6763 Xbomb_fall, FALSE, FALSE,
6767 Ybomb_s, FALSE, FALSE,
6768 EL_BOMB, ACTION_FALLING, -1
6771 Ybomb_sB, FALSE, TRUE,
6772 EL_BOMB, ACTION_FALLING, -1
6775 Ybomb_e, FALSE, FALSE,
6776 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6779 Ybomb_eB, FALSE, TRUE,
6780 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6783 Ybomb_w, FALSE, FALSE,
6784 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6787 Ybomb_wB, FALSE, TRUE,
6788 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6791 Ybomb_blank, FALSE, FALSE,
6792 EL_BOMB, ACTION_ACTIVATING, -1
6800 Xnut_pause, FALSE, FALSE,
6804 Xnut_fall, FALSE, FALSE,
6808 Ynut_s, FALSE, FALSE,
6809 EL_NUT, ACTION_FALLING, -1
6812 Ynut_sB, FALSE, TRUE,
6813 EL_NUT, ACTION_FALLING, -1
6816 Ynut_e, FALSE, FALSE,
6817 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6820 Ynut_eB, FALSE, TRUE,
6821 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6824 Ynut_w, FALSE, FALSE,
6825 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6828 Ynut_wB, FALSE, TRUE,
6829 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6832 Ynut_stone, FALSE, FALSE,
6833 EL_NUT, ACTION_BREAKING, -1
6837 Xspring, TRUE, FALSE,
6841 Xspring_pause, FALSE, FALSE,
6845 Xspring_e, TRUE, FALSE,
6846 EL_SPRING_RIGHT, -1, -1
6849 Xspring_w, TRUE, FALSE,
6850 EL_SPRING_LEFT, -1, -1
6853 Xspring_fall, FALSE, FALSE,
6857 Yspring_s, FALSE, FALSE,
6858 EL_SPRING, ACTION_FALLING, -1
6861 Yspring_sB, FALSE, TRUE,
6862 EL_SPRING, ACTION_FALLING, -1
6865 Yspring_e, FALSE, FALSE,
6866 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6869 Yspring_eB, FALSE, TRUE,
6870 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6873 Yspring_w, FALSE, FALSE,
6874 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6877 Yspring_wB, FALSE, TRUE,
6878 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6881 Yspring_alien_e, FALSE, FALSE,
6882 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6885 Yspring_alien_eB, FALSE, TRUE,
6886 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6889 Yspring_alien_w, FALSE, FALSE,
6890 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6893 Yspring_alien_wB, FALSE, TRUE,
6894 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6898 Xpush_emerald_e, FALSE, FALSE,
6899 EL_EMERALD, -1, MV_BIT_RIGHT
6902 Xpush_emerald_w, FALSE, FALSE,
6903 EL_EMERALD, -1, MV_BIT_LEFT
6906 Xpush_diamond_e, FALSE, FALSE,
6907 EL_DIAMOND, -1, MV_BIT_RIGHT
6910 Xpush_diamond_w, FALSE, FALSE,
6911 EL_DIAMOND, -1, MV_BIT_LEFT
6914 Xpush_stone_e, FALSE, FALSE,
6915 EL_ROCK, -1, MV_BIT_RIGHT
6918 Xpush_stone_w, FALSE, FALSE,
6919 EL_ROCK, -1, MV_BIT_LEFT
6922 Xpush_bomb_e, FALSE, FALSE,
6923 EL_BOMB, -1, MV_BIT_RIGHT
6926 Xpush_bomb_w, FALSE, FALSE,
6927 EL_BOMB, -1, MV_BIT_LEFT
6930 Xpush_nut_e, FALSE, FALSE,
6931 EL_NUT, -1, MV_BIT_RIGHT
6934 Xpush_nut_w, FALSE, FALSE,
6935 EL_NUT, -1, MV_BIT_LEFT
6938 Xpush_spring_e, FALSE, FALSE,
6939 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6942 Xpush_spring_w, FALSE, FALSE,
6943 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6947 Xdynamite, TRUE, FALSE,
6948 EL_EM_DYNAMITE, -1, -1
6951 Ydynamite_blank, FALSE, FALSE,
6952 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6955 Xdynamite_1, TRUE, FALSE,
6956 EL_EM_DYNAMITE_ACTIVE, -1, -1
6959 Xdynamite_2, FALSE, FALSE,
6960 EL_EM_DYNAMITE_ACTIVE, -1, -1
6963 Xdynamite_3, FALSE, FALSE,
6964 EL_EM_DYNAMITE_ACTIVE, -1, -1
6967 Xdynamite_4, FALSE, FALSE,
6968 EL_EM_DYNAMITE_ACTIVE, -1, -1
6972 Xkey_1, TRUE, FALSE,
6976 Xkey_2, TRUE, FALSE,
6980 Xkey_3, TRUE, FALSE,
6984 Xkey_4, TRUE, FALSE,
6988 Xkey_5, TRUE, FALSE,
6989 EL_EMC_KEY_5, -1, -1
6992 Xkey_6, TRUE, FALSE,
6993 EL_EMC_KEY_6, -1, -1
6996 Xkey_7, TRUE, FALSE,
6997 EL_EMC_KEY_7, -1, -1
7000 Xkey_8, TRUE, FALSE,
7001 EL_EMC_KEY_8, -1, -1
7005 Xdoor_1, TRUE, FALSE,
7006 EL_EM_GATE_1, -1, -1
7009 Xdoor_2, TRUE, FALSE,
7010 EL_EM_GATE_2, -1, -1
7013 Xdoor_3, TRUE, FALSE,
7014 EL_EM_GATE_3, -1, -1
7017 Xdoor_4, TRUE, FALSE,
7018 EL_EM_GATE_4, -1, -1
7021 Xdoor_5, TRUE, FALSE,
7022 EL_EMC_GATE_5, -1, -1
7025 Xdoor_6, TRUE, FALSE,
7026 EL_EMC_GATE_6, -1, -1
7029 Xdoor_7, TRUE, FALSE,
7030 EL_EMC_GATE_7, -1, -1
7033 Xdoor_8, TRUE, FALSE,
7034 EL_EMC_GATE_8, -1, -1
7038 Xfake_door_1, TRUE, FALSE,
7039 EL_EM_GATE_1_GRAY, -1, -1
7042 Xfake_door_2, TRUE, FALSE,
7043 EL_EM_GATE_2_GRAY, -1, -1
7046 Xfake_door_3, TRUE, FALSE,
7047 EL_EM_GATE_3_GRAY, -1, -1
7050 Xfake_door_4, TRUE, FALSE,
7051 EL_EM_GATE_4_GRAY, -1, -1
7054 Xfake_door_5, TRUE, FALSE,
7055 EL_EMC_GATE_5_GRAY, -1, -1
7058 Xfake_door_6, TRUE, FALSE,
7059 EL_EMC_GATE_6_GRAY, -1, -1
7062 Xfake_door_7, TRUE, FALSE,
7063 EL_EMC_GATE_7_GRAY, -1, -1
7066 Xfake_door_8, TRUE, FALSE,
7067 EL_EMC_GATE_8_GRAY, -1, -1
7071 Xballoon, TRUE, FALSE,
7075 Yballoon_n, FALSE, FALSE,
7076 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7079 Yballoon_nB, FALSE, TRUE,
7080 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7083 Yballoon_e, FALSE, FALSE,
7084 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7087 Yballoon_eB, FALSE, TRUE,
7088 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7091 Yballoon_s, FALSE, FALSE,
7092 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7095 Yballoon_sB, FALSE, TRUE,
7096 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7099 Yballoon_w, FALSE, FALSE,
7100 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7103 Yballoon_wB, FALSE, TRUE,
7104 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7108 Xball_1, TRUE, FALSE,
7109 EL_EMC_MAGIC_BALL, -1, -1
7112 Yball_1, FALSE, FALSE,
7113 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7116 Xball_2, FALSE, FALSE,
7117 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7120 Yball_2, FALSE, FALSE,
7121 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7124 Yball_blank, FALSE, FALSE,
7125 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7129 Xamoeba_1, TRUE, FALSE,
7130 EL_AMOEBA_DRY, ACTION_OTHER, -1
7133 Xamoeba_2, FALSE, FALSE,
7134 EL_AMOEBA_DRY, ACTION_OTHER, -1
7137 Xamoeba_3, FALSE, FALSE,
7138 EL_AMOEBA_DRY, ACTION_OTHER, -1
7141 Xamoeba_4, FALSE, FALSE,
7142 EL_AMOEBA_DRY, ACTION_OTHER, -1
7145 Xamoeba_5, TRUE, FALSE,
7146 EL_AMOEBA_WET, ACTION_OTHER, -1
7149 Xamoeba_6, FALSE, FALSE,
7150 EL_AMOEBA_WET, ACTION_OTHER, -1
7153 Xamoeba_7, FALSE, FALSE,
7154 EL_AMOEBA_WET, ACTION_OTHER, -1
7157 Xamoeba_8, FALSE, FALSE,
7158 EL_AMOEBA_WET, ACTION_OTHER, -1
7163 EL_AMOEBA_DROP, ACTION_GROWING, -1
7166 Xdrip_fall, FALSE, FALSE,
7167 EL_AMOEBA_DROP, -1, -1
7170 Xdrip_stretch, FALSE, FALSE,
7171 EL_AMOEBA_DROP, ACTION_FALLING, -1
7174 Xdrip_stretchB, FALSE, TRUE,
7175 EL_AMOEBA_DROP, ACTION_FALLING, -1
7178 Ydrip_1_s, FALSE, FALSE,
7179 EL_AMOEBA_DROP, ACTION_FALLING, -1
7182 Ydrip_1_sB, FALSE, TRUE,
7183 EL_AMOEBA_DROP, ACTION_FALLING, -1
7186 Ydrip_2_s, FALSE, FALSE,
7187 EL_AMOEBA_DROP, ACTION_FALLING, -1
7190 Ydrip_2_sB, FALSE, TRUE,
7191 EL_AMOEBA_DROP, ACTION_FALLING, -1
7195 Xwonderwall, TRUE, FALSE,
7196 EL_MAGIC_WALL, -1, -1
7199 Ywonderwall, FALSE, FALSE,
7200 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7204 Xwheel, TRUE, FALSE,
7205 EL_ROBOT_WHEEL, -1, -1
7208 Ywheel, FALSE, FALSE,
7209 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7213 Xswitch, TRUE, FALSE,
7214 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7217 Yswitch, FALSE, FALSE,
7218 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7222 Xbumper, TRUE, FALSE,
7223 EL_EMC_SPRING_BUMPER, -1, -1
7226 Ybumper, FALSE, FALSE,
7227 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7231 Xacid_nw, TRUE, FALSE,
7232 EL_ACID_POOL_TOPLEFT, -1, -1
7235 Xacid_ne, TRUE, FALSE,
7236 EL_ACID_POOL_TOPRIGHT, -1, -1
7239 Xacid_sw, TRUE, FALSE,
7240 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7243 Xacid_s, TRUE, FALSE,
7244 EL_ACID_POOL_BOTTOM, -1, -1
7247 Xacid_se, TRUE, FALSE,
7248 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7252 Xfake_blank, TRUE, FALSE,
7253 EL_INVISIBLE_WALL, -1, -1
7256 Yfake_blank, FALSE, FALSE,
7257 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7261 Xfake_grass, TRUE, FALSE,
7262 EL_EMC_FAKE_GRASS, -1, -1
7265 Yfake_grass, FALSE, FALSE,
7266 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7270 Xfake_amoeba, TRUE, FALSE,
7271 EL_EMC_DRIPPER, -1, -1
7274 Yfake_amoeba, FALSE, FALSE,
7275 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7279 Xlenses, TRUE, FALSE,
7280 EL_EMC_LENSES, -1, -1
7284 Xmagnify, TRUE, FALSE,
7285 EL_EMC_MAGNIFIER, -1, -1
7290 EL_QUICKSAND_EMPTY, -1, -1
7293 Xsand_stone, TRUE, FALSE,
7294 EL_QUICKSAND_FULL, -1, -1
7297 Xsand_stonein_1, FALSE, TRUE,
7298 EL_ROCK, ACTION_FILLING, -1
7301 Xsand_stonein_2, FALSE, TRUE,
7302 EL_ROCK, ACTION_FILLING, -1
7305 Xsand_stonein_3, FALSE, TRUE,
7306 EL_ROCK, ACTION_FILLING, -1
7309 Xsand_stonein_4, FALSE, TRUE,
7310 EL_ROCK, ACTION_FILLING, -1
7313 Xsand_sandstone_1, FALSE, FALSE,
7314 EL_QUICKSAND_FILLING, -1, -1
7317 Xsand_sandstone_2, FALSE, FALSE,
7318 EL_QUICKSAND_FILLING, -1, -1
7321 Xsand_sandstone_3, FALSE, FALSE,
7322 EL_QUICKSAND_FILLING, -1, -1
7325 Xsand_sandstone_4, FALSE, FALSE,
7326 EL_QUICKSAND_FILLING, -1, -1
7329 Xsand_stonesand_1, FALSE, FALSE,
7330 EL_QUICKSAND_EMPTYING, -1, -1
7333 Xsand_stonesand_2, FALSE, FALSE,
7334 EL_QUICKSAND_EMPTYING, -1, -1
7337 Xsand_stonesand_3, FALSE, FALSE,
7338 EL_QUICKSAND_EMPTYING, -1, -1
7341 Xsand_stonesand_4, FALSE, FALSE,
7342 EL_QUICKSAND_EMPTYING, -1, -1
7345 Xsand_stoneout_1, FALSE, FALSE,
7346 EL_ROCK, ACTION_EMPTYING, -1
7349 Xsand_stoneout_2, FALSE, FALSE,
7350 EL_ROCK, ACTION_EMPTYING, -1
7353 Xsand_stonesand_quickout_1, FALSE, FALSE,
7354 EL_QUICKSAND_EMPTYING, -1, -1
7357 Xsand_stonesand_quickout_2, FALSE, FALSE,
7358 EL_QUICKSAND_EMPTYING, -1, -1
7362 Xslide_ns, TRUE, FALSE,
7363 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7366 Yslide_ns_blank, FALSE, FALSE,
7367 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7370 Xslide_ew, TRUE, FALSE,
7371 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7374 Yslide_ew_blank, FALSE, FALSE,
7375 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7379 Xwind_n, TRUE, FALSE,
7380 EL_BALLOON_SWITCH_UP, -1, -1
7383 Xwind_e, TRUE, FALSE,
7384 EL_BALLOON_SWITCH_RIGHT, -1, -1
7387 Xwind_s, TRUE, FALSE,
7388 EL_BALLOON_SWITCH_DOWN, -1, -1
7391 Xwind_w, TRUE, FALSE,
7392 EL_BALLOON_SWITCH_LEFT, -1, -1
7395 Xwind_any, TRUE, FALSE,
7396 EL_BALLOON_SWITCH_ANY, -1, -1
7399 Xwind_stop, TRUE, FALSE,
7400 EL_BALLOON_SWITCH_NONE, -1, -1
7405 EL_EM_EXIT_CLOSED, -1, -1
7408 Xexit_1, TRUE, FALSE,
7409 EL_EM_EXIT_OPEN, -1, -1
7412 Xexit_2, FALSE, FALSE,
7413 EL_EM_EXIT_OPEN, -1, -1
7416 Xexit_3, FALSE, FALSE,
7417 EL_EM_EXIT_OPEN, -1, -1
7421 Xpause, FALSE, FALSE,
7426 Xwall_1, TRUE, FALSE,
7430 Xwall_2, TRUE, FALSE,
7431 EL_EMC_WALL_14, -1, -1
7434 Xwall_3, TRUE, FALSE,
7435 EL_EMC_WALL_15, -1, -1
7438 Xwall_4, TRUE, FALSE,
7439 EL_EMC_WALL_16, -1, -1
7443 Xroundwall_1, TRUE, FALSE,
7444 EL_WALL_SLIPPERY, -1, -1
7447 Xroundwall_2, TRUE, FALSE,
7448 EL_EMC_WALL_SLIPPERY_2, -1, -1
7451 Xroundwall_3, TRUE, FALSE,
7452 EL_EMC_WALL_SLIPPERY_3, -1, -1
7455 Xroundwall_4, TRUE, FALSE,
7456 EL_EMC_WALL_SLIPPERY_4, -1, -1
7460 Xsteel_1, TRUE, FALSE,
7461 EL_STEELWALL, -1, -1
7464 Xsteel_2, TRUE, FALSE,
7465 EL_EMC_STEELWALL_2, -1, -1
7468 Xsteel_3, TRUE, FALSE,
7469 EL_EMC_STEELWALL_3, -1, -1
7472 Xsteel_4, TRUE, FALSE,
7473 EL_EMC_STEELWALL_4, -1, -1
7477 Xdecor_1, TRUE, FALSE,
7478 EL_EMC_WALL_8, -1, -1
7481 Xdecor_2, TRUE, FALSE,
7482 EL_EMC_WALL_6, -1, -1
7485 Xdecor_3, TRUE, FALSE,
7486 EL_EMC_WALL_4, -1, -1
7489 Xdecor_4, TRUE, FALSE,
7490 EL_EMC_WALL_7, -1, -1
7493 Xdecor_5, TRUE, FALSE,
7494 EL_EMC_WALL_5, -1, -1
7497 Xdecor_6, TRUE, FALSE,
7498 EL_EMC_WALL_9, -1, -1
7501 Xdecor_7, TRUE, FALSE,
7502 EL_EMC_WALL_10, -1, -1
7505 Xdecor_8, TRUE, FALSE,
7506 EL_EMC_WALL_1, -1, -1
7509 Xdecor_9, TRUE, FALSE,
7510 EL_EMC_WALL_2, -1, -1
7513 Xdecor_10, TRUE, FALSE,
7514 EL_EMC_WALL_3, -1, -1
7517 Xdecor_11, TRUE, FALSE,
7518 EL_EMC_WALL_11, -1, -1
7521 Xdecor_12, TRUE, FALSE,
7522 EL_EMC_WALL_12, -1, -1
7526 Xalpha_0, TRUE, FALSE,
7527 EL_CHAR('0'), -1, -1
7530 Xalpha_1, TRUE, FALSE,
7531 EL_CHAR('1'), -1, -1
7534 Xalpha_2, TRUE, FALSE,
7535 EL_CHAR('2'), -1, -1
7538 Xalpha_3, TRUE, FALSE,
7539 EL_CHAR('3'), -1, -1
7542 Xalpha_4, TRUE, FALSE,
7543 EL_CHAR('4'), -1, -1
7546 Xalpha_5, TRUE, FALSE,
7547 EL_CHAR('5'), -1, -1
7550 Xalpha_6, TRUE, FALSE,
7551 EL_CHAR('6'), -1, -1
7554 Xalpha_7, TRUE, FALSE,
7555 EL_CHAR('7'), -1, -1
7558 Xalpha_8, TRUE, FALSE,
7559 EL_CHAR('8'), -1, -1
7562 Xalpha_9, TRUE, FALSE,
7563 EL_CHAR('9'), -1, -1
7566 Xalpha_excla, TRUE, FALSE,
7567 EL_CHAR('!'), -1, -1
7570 Xalpha_apost, TRUE, FALSE,
7571 EL_CHAR('\''), -1, -1
7574 Xalpha_comma, TRUE, FALSE,
7575 EL_CHAR(','), -1, -1
7578 Xalpha_minus, TRUE, FALSE,
7579 EL_CHAR('-'), -1, -1
7582 Xalpha_perio, TRUE, FALSE,
7583 EL_CHAR('.'), -1, -1
7586 Xalpha_colon, TRUE, FALSE,
7587 EL_CHAR(':'), -1, -1
7590 Xalpha_quest, TRUE, FALSE,
7591 EL_CHAR('?'), -1, -1
7594 Xalpha_a, TRUE, FALSE,
7595 EL_CHAR('A'), -1, -1
7598 Xalpha_b, TRUE, FALSE,
7599 EL_CHAR('B'), -1, -1
7602 Xalpha_c, TRUE, FALSE,
7603 EL_CHAR('C'), -1, -1
7606 Xalpha_d, TRUE, FALSE,
7607 EL_CHAR('D'), -1, -1
7610 Xalpha_e, TRUE, FALSE,
7611 EL_CHAR('E'), -1, -1
7614 Xalpha_f, TRUE, FALSE,
7615 EL_CHAR('F'), -1, -1
7618 Xalpha_g, TRUE, FALSE,
7619 EL_CHAR('G'), -1, -1
7622 Xalpha_h, TRUE, FALSE,
7623 EL_CHAR('H'), -1, -1
7626 Xalpha_i, TRUE, FALSE,
7627 EL_CHAR('I'), -1, -1
7630 Xalpha_j, TRUE, FALSE,
7631 EL_CHAR('J'), -1, -1
7634 Xalpha_k, TRUE, FALSE,
7635 EL_CHAR('K'), -1, -1
7638 Xalpha_l, TRUE, FALSE,
7639 EL_CHAR('L'), -1, -1
7642 Xalpha_m, TRUE, FALSE,
7643 EL_CHAR('M'), -1, -1
7646 Xalpha_n, TRUE, FALSE,
7647 EL_CHAR('N'), -1, -1
7650 Xalpha_o, TRUE, FALSE,
7651 EL_CHAR('O'), -1, -1
7654 Xalpha_p, TRUE, FALSE,
7655 EL_CHAR('P'), -1, -1
7658 Xalpha_q, TRUE, FALSE,
7659 EL_CHAR('Q'), -1, -1
7662 Xalpha_r, TRUE, FALSE,
7663 EL_CHAR('R'), -1, -1
7666 Xalpha_s, TRUE, FALSE,
7667 EL_CHAR('S'), -1, -1
7670 Xalpha_t, TRUE, FALSE,
7671 EL_CHAR('T'), -1, -1
7674 Xalpha_u, TRUE, FALSE,
7675 EL_CHAR('U'), -1, -1
7678 Xalpha_v, TRUE, FALSE,
7679 EL_CHAR('V'), -1, -1
7682 Xalpha_w, TRUE, FALSE,
7683 EL_CHAR('W'), -1, -1
7686 Xalpha_x, TRUE, FALSE,
7687 EL_CHAR('X'), -1, -1
7690 Xalpha_y, TRUE, FALSE,
7691 EL_CHAR('Y'), -1, -1
7694 Xalpha_z, TRUE, FALSE,
7695 EL_CHAR('Z'), -1, -1
7698 Xalpha_arrow_e, TRUE, FALSE,
7699 EL_CHAR('>'), -1, -1
7702 Xalpha_arrow_w, TRUE, FALSE,
7703 EL_CHAR('<'), -1, -1
7706 Xalpha_copyr, TRUE, FALSE,
7707 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7711 Ykey_1_blank, FALSE, FALSE,
7712 EL_EM_KEY_1, ACTION_COLLECTING, -1
7715 Ykey_2_blank, FALSE, FALSE,
7716 EL_EM_KEY_2, ACTION_COLLECTING, -1
7719 Ykey_3_blank, FALSE, FALSE,
7720 EL_EM_KEY_3, ACTION_COLLECTING, -1
7723 Ykey_4_blank, FALSE, FALSE,
7724 EL_EM_KEY_4, ACTION_COLLECTING, -1
7727 Ykey_5_blank, FALSE, FALSE,
7728 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7731 Ykey_6_blank, FALSE, FALSE,
7732 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7735 Ykey_7_blank, FALSE, FALSE,
7736 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7739 Ykey_8_blank, FALSE, FALSE,
7740 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7743 Ylenses_blank, FALSE, FALSE,
7744 EL_EMC_LENSES, ACTION_COLLECTING, -1
7747 Ymagnify_blank, FALSE, FALSE,
7748 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7751 Ygrass_blank, FALSE, FALSE,
7752 EL_EMC_GRASS, ACTION_SNAPPING, -1
7755 Ydirt_blank, FALSE, FALSE,
7756 EL_SAND, ACTION_SNAPPING, -1
7765 static struct Mapping_EM_to_RND_player
7774 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7778 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7782 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7786 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7790 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7794 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7798 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7802 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7806 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7810 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7814 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7818 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7822 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7826 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7830 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7834 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7838 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7842 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7846 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7850 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7854 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7858 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7862 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7866 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7870 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7874 EL_PLAYER_1, ACTION_DEFAULT, -1,
7878 EL_PLAYER_2, ACTION_DEFAULT, -1,
7882 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7886 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7890 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7894 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7898 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7902 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7906 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7910 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7914 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7918 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7922 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7926 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7930 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7934 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7938 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7942 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7946 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7950 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7954 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7958 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7962 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7966 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7970 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7974 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7978 EL_PLAYER_3, ACTION_DEFAULT, -1,
7982 EL_PLAYER_4, ACTION_DEFAULT, -1,
7991 int map_element_RND_to_EM_cave(int element_rnd)
7993 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7994 static boolean mapping_initialized = FALSE;
7996 if (!mapping_initialized)
8000 // return "Xalpha_quest" for all undefined elements in mapping array
8001 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8002 mapping_RND_to_EM[i] = Xalpha_quest;
8004 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8005 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
8006 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
8007 em_object_mapping_list[i].element_em;
8009 mapping_initialized = TRUE;
8012 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
8014 Warn("invalid RND level element %d", element_rnd);
8019 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
8022 int map_element_EM_to_RND_cave(int element_em_cave)
8024 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8025 static boolean mapping_initialized = FALSE;
8027 if (!mapping_initialized)
8031 // return "EL_UNKNOWN" for all undefined elements in mapping array
8032 for (i = 0; i < GAME_TILE_MAX; i++)
8033 mapping_EM_to_RND[i] = EL_UNKNOWN;
8035 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8036 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8037 em_object_mapping_list[i].element_rnd;
8039 mapping_initialized = TRUE;
8042 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8044 Warn("invalid EM cave element %d", element_em_cave);
8049 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8052 int map_element_EM_to_RND_game(int element_em_game)
8054 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8055 static boolean mapping_initialized = FALSE;
8057 if (!mapping_initialized)
8061 // return "EL_UNKNOWN" for all undefined elements in mapping array
8062 for (i = 0; i < GAME_TILE_MAX; i++)
8063 mapping_EM_to_RND[i] = EL_UNKNOWN;
8065 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8066 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8067 em_object_mapping_list[i].element_rnd;
8069 mapping_initialized = TRUE;
8072 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8074 Warn("invalid EM game element %d", element_em_game);
8079 return mapping_EM_to_RND[element_em_game];
8082 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8084 struct LevelInfo_EM *level_em = level->native_em_level;
8085 struct CAVE *cav = level_em->cav;
8088 for (i = 0; i < GAME_TILE_MAX; i++)
8089 cav->android_array[i] = Cblank;
8091 for (i = 0; i < level->num_android_clone_elements; i++)
8093 int element_rnd = level->android_clone_element[i];
8094 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8096 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8097 if (em_object_mapping_list[j].element_rnd == element_rnd)
8098 cav->android_array[em_object_mapping_list[j].element_em] =
8103 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8105 struct LevelInfo_EM *level_em = level->native_em_level;
8106 struct CAVE *cav = level_em->cav;
8109 level->num_android_clone_elements = 0;
8111 for (i = 0; i < GAME_TILE_MAX; i++)
8113 int element_em_cave = cav->android_array[i];
8115 boolean element_found = FALSE;
8117 if (element_em_cave == Cblank)
8120 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8122 for (j = 0; j < level->num_android_clone_elements; j++)
8123 if (level->android_clone_element[j] == element_rnd)
8124 element_found = TRUE;
8128 level->android_clone_element[level->num_android_clone_elements++] =
8131 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8136 if (level->num_android_clone_elements == 0)
8138 level->num_android_clone_elements = 1;
8139 level->android_clone_element[0] = EL_EMPTY;
8143 int map_direction_RND_to_EM(int direction)
8145 return (direction == MV_UP ? 0 :
8146 direction == MV_RIGHT ? 1 :
8147 direction == MV_DOWN ? 2 :
8148 direction == MV_LEFT ? 3 :
8152 int map_direction_EM_to_RND(int direction)
8154 return (direction == 0 ? MV_UP :
8155 direction == 1 ? MV_RIGHT :
8156 direction == 2 ? MV_DOWN :
8157 direction == 3 ? MV_LEFT :
8161 int map_element_RND_to_SP(int element_rnd)
8163 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8165 if (element_rnd >= EL_SP_START &&
8166 element_rnd <= EL_SP_END)
8167 element_sp = element_rnd - EL_SP_START;
8168 else if (element_rnd == EL_EMPTY_SPACE)
8170 else if (element_rnd == EL_INVISIBLE_WALL)
8176 int map_element_SP_to_RND(int element_sp)
8178 int element_rnd = EL_UNKNOWN;
8180 if (element_sp >= 0x00 &&
8182 element_rnd = EL_SP_START + element_sp;
8183 else if (element_sp == 0x28)
8184 element_rnd = EL_INVISIBLE_WALL;
8189 int map_action_SP_to_RND(int action_sp)
8193 case actActive: return ACTION_ACTIVE;
8194 case actImpact: return ACTION_IMPACT;
8195 case actExploding: return ACTION_EXPLODING;
8196 case actDigging: return ACTION_DIGGING;
8197 case actSnapping: return ACTION_SNAPPING;
8198 case actCollecting: return ACTION_COLLECTING;
8199 case actPassing: return ACTION_PASSING;
8200 case actPushing: return ACTION_PUSHING;
8201 case actDropping: return ACTION_DROPPING;
8203 default: return ACTION_DEFAULT;
8207 int map_element_RND_to_MM(int element_rnd)
8209 return (element_rnd >= EL_MM_START_1 &&
8210 element_rnd <= EL_MM_END_1 ?
8211 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8213 element_rnd >= EL_MM_START_2 &&
8214 element_rnd <= EL_MM_END_2 ?
8215 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8217 element_rnd >= EL_MM_START_3 &&
8218 element_rnd <= EL_MM_END_3 ?
8219 EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
8221 element_rnd >= EL_CHAR_START &&
8222 element_rnd <= EL_CHAR_END ?
8223 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8225 element_rnd >= EL_MM_RUNTIME_START &&
8226 element_rnd <= EL_MM_RUNTIME_END ?
8227 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8229 EL_MM_EMPTY_NATIVE);
8232 int map_element_MM_to_RND(int element_mm)
8234 return (element_mm == EL_MM_EMPTY_NATIVE ||
8235 element_mm == EL_DF_EMPTY_NATIVE ?
8238 element_mm >= EL_MM_START_1_NATIVE &&
8239 element_mm <= EL_MM_END_1_NATIVE ?
8240 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8242 element_mm >= EL_MM_START_2_NATIVE &&
8243 element_mm <= EL_MM_END_2_NATIVE ?
8244 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8246 element_mm >= EL_MM_START_3_NATIVE &&
8247 element_mm <= EL_MM_END_3_NATIVE ?
8248 EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
8250 element_mm >= EL_MM_CHAR_START_NATIVE &&
8251 element_mm <= EL_MM_CHAR_END_NATIVE ?
8252 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8254 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8255 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8256 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8261 int map_action_MM_to_RND(int action_mm)
8263 // all MM actions are defined to exactly match their RND counterparts
8267 int map_sound_MM_to_RND(int sound_mm)
8271 case SND_MM_GAME_LEVELTIME_CHARGING:
8272 return SND_GAME_LEVELTIME_CHARGING;
8274 case SND_MM_GAME_HEALTH_CHARGING:
8275 return SND_GAME_HEALTH_CHARGING;
8278 return SND_UNDEFINED;
8282 int map_mm_wall_element(int element)
8284 return (element >= EL_MM_STEEL_WALL_START &&
8285 element <= EL_MM_STEEL_WALL_END ?
8288 element >= EL_MM_WOODEN_WALL_START &&
8289 element <= EL_MM_WOODEN_WALL_END ?
8292 element >= EL_MM_ICE_WALL_START &&
8293 element <= EL_MM_ICE_WALL_END ?
8296 element >= EL_MM_AMOEBA_WALL_START &&
8297 element <= EL_MM_AMOEBA_WALL_END ?
8300 element >= EL_DF_STEEL_WALL_START &&
8301 element <= EL_DF_STEEL_WALL_END ?
8304 element >= EL_DF_WOODEN_WALL_START &&
8305 element <= EL_DF_WOODEN_WALL_END ?
8311 int map_mm_wall_element_editor(int element)
8315 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8316 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8317 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8318 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8319 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8320 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8322 default: return element;
8326 int get_next_element(int element)
8330 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8331 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8332 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8333 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8334 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8335 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8336 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8337 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8338 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8339 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8340 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8342 default: return element;
8346 int el2img_mm(int element_mm)
8348 return el2img(map_element_MM_to_RND(element_mm));
8351 int el_act2img_mm(int element_mm, int action)
8353 return el_act2img(map_element_MM_to_RND(element_mm), action);
8356 int el_act_dir2img(int element, int action, int direction)
8358 element = GFX_ELEMENT(element);
8359 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8361 // direction_graphic[][] == graphic[] for undefined direction graphics
8362 return element_info[element].direction_graphic[action][direction];
8365 static int el_act_dir2crm(int element, int action, int direction)
8367 element = GFX_ELEMENT(element);
8368 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8370 // direction_graphic[][] == graphic[] for undefined direction graphics
8371 return element_info[element].direction_crumbled[action][direction];
8374 int el_act2img(int element, int action)
8376 element = GFX_ELEMENT(element);
8378 return element_info[element].graphic[action];
8381 int el_act2crm(int element, int action)
8383 element = GFX_ELEMENT(element);
8385 return element_info[element].crumbled[action];
8388 int el_dir2img(int element, int direction)
8390 element = GFX_ELEMENT(element);
8392 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8395 int el2baseimg(int element)
8397 return element_info[element].graphic[ACTION_DEFAULT];
8400 int el2img(int element)
8402 element = GFX_ELEMENT(element);
8404 return element_info[element].graphic[ACTION_DEFAULT];
8407 int el2edimg(int element)
8409 element = GFX_ELEMENT(element);
8411 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8414 int el2preimg(int element)
8416 element = GFX_ELEMENT(element);
8418 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8421 int el2panelimg(int element)
8423 element = GFX_ELEMENT(element);
8425 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8428 int font2baseimg(int font_nr)
8430 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8433 int getBeltNrFromBeltElement(int element)
8435 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8436 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8437 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8440 int getBeltNrFromBeltActiveElement(int element)
8442 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8443 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8444 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8447 int getBeltNrFromBeltSwitchElement(int element)
8449 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8450 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8451 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8454 int getBeltDirNrFromBeltElement(int element)
8456 static int belt_base_element[4] =
8458 EL_CONVEYOR_BELT_1_LEFT,
8459 EL_CONVEYOR_BELT_2_LEFT,
8460 EL_CONVEYOR_BELT_3_LEFT,
8461 EL_CONVEYOR_BELT_4_LEFT
8464 int belt_nr = getBeltNrFromBeltElement(element);
8465 int belt_dir_nr = element - belt_base_element[belt_nr];
8467 return (belt_dir_nr % 3);
8470 int getBeltDirNrFromBeltSwitchElement(int element)
8472 static int belt_base_element[4] =
8474 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8475 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8476 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8477 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8480 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8481 int belt_dir_nr = element - belt_base_element[belt_nr];
8483 return (belt_dir_nr % 3);
8486 int getBeltDirFromBeltElement(int element)
8488 static int belt_move_dir[3] =
8495 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8497 return belt_move_dir[belt_dir_nr];
8500 int getBeltDirFromBeltSwitchElement(int element)
8502 static int belt_move_dir[3] =
8509 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8511 return belt_move_dir[belt_dir_nr];
8514 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8516 static int belt_base_element[4] =
8518 EL_CONVEYOR_BELT_1_LEFT,
8519 EL_CONVEYOR_BELT_2_LEFT,
8520 EL_CONVEYOR_BELT_3_LEFT,
8521 EL_CONVEYOR_BELT_4_LEFT
8524 return belt_base_element[belt_nr] + belt_dir_nr;
8527 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8529 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8531 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8534 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8536 static int belt_base_element[4] =
8538 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8539 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8540 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8541 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8544 return belt_base_element[belt_nr] + belt_dir_nr;
8547 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8549 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8551 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8554 boolean swapTiles_EM(boolean is_pre_emc_cave)
8556 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8559 boolean getTeamMode_EM(void)
8561 return game.team_mode || network_playing;
8564 boolean isActivePlayer_EM(int player_nr)
8566 return stored_player[player_nr].active;
8569 unsigned int InitRND(int seed)
8571 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8572 return InitEngineRandom_EM(seed);
8573 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8574 return InitEngineRandom_SP(seed);
8575 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8576 return InitEngineRandom_MM(seed);
8578 return InitEngineRandom_RND(seed);
8581 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8582 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8584 static int get_effective_element_EM(int tile, int frame_em)
8586 int element = object_mapping[tile].element_rnd;
8587 int action = object_mapping[tile].action;
8588 boolean is_backside = object_mapping[tile].is_backside;
8589 boolean action_removing = (action == ACTION_DIGGING ||
8590 action == ACTION_SNAPPING ||
8591 action == ACTION_COLLECTING);
8599 return (frame_em > 5 ? EL_EMPTY : element);
8605 else // frame_em == 7
8616 case Ydiamond_stone:
8620 case Xdrip_stretchB:
8636 case Ymagnify_blank:
8639 case Xsand_stonein_1:
8640 case Xsand_stonein_2:
8641 case Xsand_stonein_3:
8642 case Xsand_stonein_4:
8646 return (is_backside || action_removing ? EL_EMPTY : element);
8651 static boolean check_linear_animation_EM(int tile)
8655 case Xsand_stonesand_1:
8656 case Xsand_stonesand_quickout_1:
8657 case Xsand_sandstone_1:
8658 case Xsand_stonein_1:
8659 case Xsand_stoneout_1:
8687 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8688 boolean has_crumbled_graphics,
8689 int crumbled, int sync_frame)
8691 // if element can be crumbled, but certain action graphics are just empty
8692 // space (like instantly snapping sand to empty space in 1 frame), do not
8693 // treat these empty space graphics as crumbled graphics in EMC engine
8694 if (crumbled == IMG_EMPTY_SPACE)
8695 has_crumbled_graphics = FALSE;
8697 if (has_crumbled_graphics)
8699 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8700 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8701 g_crumbled->anim_delay,
8702 g_crumbled->anim_mode,
8703 g_crumbled->anim_start_frame,
8706 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8707 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8709 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8710 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8712 g_em->has_crumbled_graphics = TRUE;
8716 g_em->crumbled_bitmap = NULL;
8717 g_em->crumbled_src_x = 0;
8718 g_em->crumbled_src_y = 0;
8719 g_em->crumbled_border_size = 0;
8720 g_em->crumbled_tile_size = 0;
8722 g_em->has_crumbled_graphics = FALSE;
8727 void ResetGfxAnimation_EM(int x, int y, int tile)
8733 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8734 int tile, int frame_em, int x, int y)
8736 int action = object_mapping[tile].action;
8737 int direction = object_mapping[tile].direction;
8738 int effective_element = get_effective_element_EM(tile, frame_em);
8739 int graphic = (direction == MV_NONE ?
8740 el_act2img(effective_element, action) :
8741 el_act_dir2img(effective_element, action, direction));
8742 struct GraphicInfo *g = &graphic_info[graphic];
8744 boolean action_removing = (action == ACTION_DIGGING ||
8745 action == ACTION_SNAPPING ||
8746 action == ACTION_COLLECTING);
8747 boolean action_moving = (action == ACTION_FALLING ||
8748 action == ACTION_MOVING ||
8749 action == ACTION_PUSHING ||
8750 action == ACTION_EATING ||
8751 action == ACTION_FILLING ||
8752 action == ACTION_EMPTYING);
8753 boolean action_falling = (action == ACTION_FALLING ||
8754 action == ACTION_FILLING ||
8755 action == ACTION_EMPTYING);
8757 // special case: graphic uses "2nd movement tile" and has defined
8758 // 7 frames for movement animation (or less) => use default graphic
8759 // for last (8th) frame which ends the movement animation
8760 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8762 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8763 graphic = (direction == MV_NONE ?
8764 el_act2img(effective_element, action) :
8765 el_act_dir2img(effective_element, action, direction));
8767 g = &graphic_info[graphic];
8770 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8774 else if (action_moving)
8776 boolean is_backside = object_mapping[tile].is_backside;
8780 int direction = object_mapping[tile].direction;
8781 int move_dir = (action_falling ? MV_DOWN : direction);
8786 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8787 if (g->double_movement && frame_em == 0)
8791 if (move_dir == MV_LEFT)
8792 GfxFrame[x - 1][y] = GfxFrame[x][y];
8793 else if (move_dir == MV_RIGHT)
8794 GfxFrame[x + 1][y] = GfxFrame[x][y];
8795 else if (move_dir == MV_UP)
8796 GfxFrame[x][y - 1] = GfxFrame[x][y];
8797 else if (move_dir == MV_DOWN)
8798 GfxFrame[x][y + 1] = GfxFrame[x][y];
8805 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8806 if (tile == Xsand_stonesand_quickout_1 ||
8807 tile == Xsand_stonesand_quickout_2)
8811 if (graphic_info[graphic].anim_global_sync)
8812 sync_frame = FrameCounter;
8813 else if (graphic_info[graphic].anim_global_anim_sync)
8814 sync_frame = getGlobalAnimSyncFrame();
8815 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8816 sync_frame = GfxFrame[x][y];
8818 sync_frame = 0; // playfield border (pseudo steel)
8820 SetRandomAnimationValue(x, y);
8822 int frame = getAnimationFrame(g->anim_frames,
8825 g->anim_start_frame,
8828 g_em->unique_identifier =
8829 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8832 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8833 int tile, int frame_em, int x, int y)
8835 int action = object_mapping[tile].action;
8836 int direction = object_mapping[tile].direction;
8837 boolean is_backside = object_mapping[tile].is_backside;
8838 int effective_element = get_effective_element_EM(tile, frame_em);
8839 int effective_action = action;
8840 int graphic = (direction == MV_NONE ?
8841 el_act2img(effective_element, effective_action) :
8842 el_act_dir2img(effective_element, effective_action,
8844 int crumbled = (direction == MV_NONE ?
8845 el_act2crm(effective_element, effective_action) :
8846 el_act_dir2crm(effective_element, effective_action,
8848 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8849 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8850 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8851 struct GraphicInfo *g = &graphic_info[graphic];
8854 // special case: graphic uses "2nd movement tile" and has defined
8855 // 7 frames for movement animation (or less) => use default graphic
8856 // for last (8th) frame which ends the movement animation
8857 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8859 effective_action = ACTION_DEFAULT;
8860 graphic = (direction == MV_NONE ?
8861 el_act2img(effective_element, effective_action) :
8862 el_act_dir2img(effective_element, effective_action,
8864 crumbled = (direction == MV_NONE ?
8865 el_act2crm(effective_element, effective_action) :
8866 el_act_dir2crm(effective_element, effective_action,
8869 g = &graphic_info[graphic];
8872 if (graphic_info[graphic].anim_global_sync)
8873 sync_frame = FrameCounter;
8874 else if (graphic_info[graphic].anim_global_anim_sync)
8875 sync_frame = getGlobalAnimSyncFrame();
8876 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8877 sync_frame = GfxFrame[x][y];
8879 sync_frame = 0; // playfield border (pseudo steel)
8881 SetRandomAnimationValue(x, y);
8883 int frame = getAnimationFrame(g->anim_frames,
8886 g->anim_start_frame,
8889 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8890 g->double_movement && is_backside);
8892 // (updating the "crumbled" graphic definitions is probably not really needed,
8893 // as animations for crumbled graphics can't be longer than one EMC cycle)
8894 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8898 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8899 int player_nr, int anim, int frame_em)
8901 int element = player_mapping[player_nr][anim].element_rnd;
8902 int action = player_mapping[player_nr][anim].action;
8903 int direction = player_mapping[player_nr][anim].direction;
8904 int graphic = (direction == MV_NONE ?
8905 el_act2img(element, action) :
8906 el_act_dir2img(element, action, direction));
8907 struct GraphicInfo *g = &graphic_info[graphic];
8910 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8912 stored_player[player_nr].StepFrame = frame_em;
8914 sync_frame = stored_player[player_nr].Frame;
8916 int frame = getAnimationFrame(g->anim_frames,
8919 g->anim_start_frame,
8922 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8923 &g_em->src_x, &g_em->src_y, FALSE);
8926 void InitGraphicInfo_EM(void)
8930 // always start with reliable default values
8931 for (i = 0; i < GAME_TILE_MAX; i++)
8933 object_mapping[i].element_rnd = EL_UNKNOWN;
8934 object_mapping[i].is_backside = FALSE;
8935 object_mapping[i].action = ACTION_DEFAULT;
8936 object_mapping[i].direction = MV_NONE;
8939 // always start with reliable default values
8940 for (p = 0; p < MAX_PLAYERS; p++)
8942 for (i = 0; i < PLY_MAX; i++)
8944 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8945 player_mapping[p][i].action = ACTION_DEFAULT;
8946 player_mapping[p][i].direction = MV_NONE;
8950 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8952 int e = em_object_mapping_list[i].element_em;
8954 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8955 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8957 if (em_object_mapping_list[i].action != -1)
8958 object_mapping[e].action = em_object_mapping_list[i].action;
8960 if (em_object_mapping_list[i].direction != -1)
8961 object_mapping[e].direction =
8962 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8965 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8967 int a = em_player_mapping_list[i].action_em;
8968 int p = em_player_mapping_list[i].player_nr;
8970 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8972 if (em_player_mapping_list[i].action != -1)
8973 player_mapping[p][a].action = em_player_mapping_list[i].action;
8975 if (em_player_mapping_list[i].direction != -1)
8976 player_mapping[p][a].direction =
8977 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8980 for (i = 0; i < GAME_TILE_MAX; i++)
8982 int element = object_mapping[i].element_rnd;
8983 int action = object_mapping[i].action;
8984 int direction = object_mapping[i].direction;
8985 boolean is_backside = object_mapping[i].is_backside;
8986 boolean action_exploding = ((action == ACTION_EXPLODING ||
8987 action == ACTION_SMASHED_BY_ROCK ||
8988 action == ACTION_SMASHED_BY_SPRING) &&
8989 element != EL_DIAMOND);
8990 boolean action_active = (action == ACTION_ACTIVE);
8991 boolean action_other = (action == ACTION_OTHER);
8993 for (j = 0; j < 8; j++)
8995 int effective_element = get_effective_element_EM(i, j);
8996 int effective_action = (j < 7 ? action :
8997 i == Xdrip_stretch ? action :
8998 i == Xdrip_stretchB ? action :
8999 i == Ydrip_1_s ? action :
9000 i == Ydrip_1_sB ? action :
9001 i == Yball_1 ? action :
9002 i == Xball_2 ? action :
9003 i == Yball_2 ? action :
9004 i == Yball_blank ? action :
9005 i == Ykey_1_blank ? action :
9006 i == Ykey_2_blank ? action :
9007 i == Ykey_3_blank ? action :
9008 i == Ykey_4_blank ? action :
9009 i == Ykey_5_blank ? action :
9010 i == Ykey_6_blank ? action :
9011 i == Ykey_7_blank ? action :
9012 i == Ykey_8_blank ? action :
9013 i == Ylenses_blank ? action :
9014 i == Ymagnify_blank ? action :
9015 i == Ygrass_blank ? action :
9016 i == Ydirt_blank ? action :
9017 i == Xsand_stonein_1 ? action :
9018 i == Xsand_stonein_2 ? action :
9019 i == Xsand_stonein_3 ? action :
9020 i == Xsand_stonein_4 ? action :
9021 i == Xsand_stoneout_1 ? action :
9022 i == Xsand_stoneout_2 ? action :
9023 i == Xboom_android ? ACTION_EXPLODING :
9024 action_exploding ? ACTION_EXPLODING :
9025 action_active ? action :
9026 action_other ? action :
9028 int graphic = (el_act_dir2img(effective_element, effective_action,
9030 int crumbled = (el_act_dir2crm(effective_element, effective_action,
9032 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
9033 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
9034 boolean has_action_graphics = (graphic != base_graphic);
9035 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9036 struct GraphicInfo *g = &graphic_info[graphic];
9037 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9040 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9041 boolean special_animation = (action != ACTION_DEFAULT &&
9042 g->anim_frames == 3 &&
9043 g->anim_delay == 2 &&
9044 g->anim_mode & ANIM_LINEAR);
9045 int sync_frame = (i == Xdrip_stretch ? 7 :
9046 i == Xdrip_stretchB ? 7 :
9047 i == Ydrip_2_s ? j + 8 :
9048 i == Ydrip_2_sB ? j + 8 :
9057 i == Xfake_acid_1 ? 0 :
9058 i == Xfake_acid_2 ? 10 :
9059 i == Xfake_acid_3 ? 20 :
9060 i == Xfake_acid_4 ? 30 :
9061 i == Xfake_acid_5 ? 40 :
9062 i == Xfake_acid_6 ? 50 :
9063 i == Xfake_acid_7 ? 60 :
9064 i == Xfake_acid_8 ? 70 :
9065 i == Xfake_acid_1_player ? 0 :
9066 i == Xfake_acid_2_player ? 10 :
9067 i == Xfake_acid_3_player ? 20 :
9068 i == Xfake_acid_4_player ? 30 :
9069 i == Xfake_acid_5_player ? 40 :
9070 i == Xfake_acid_6_player ? 50 :
9071 i == Xfake_acid_7_player ? 60 :
9072 i == Xfake_acid_8_player ? 70 :
9074 i == Yball_2 ? j + 8 :
9075 i == Yball_blank ? j + 1 :
9076 i == Ykey_1_blank ? j + 1 :
9077 i == Ykey_2_blank ? j + 1 :
9078 i == Ykey_3_blank ? j + 1 :
9079 i == Ykey_4_blank ? j + 1 :
9080 i == Ykey_5_blank ? j + 1 :
9081 i == Ykey_6_blank ? j + 1 :
9082 i == Ykey_7_blank ? j + 1 :
9083 i == Ykey_8_blank ? j + 1 :
9084 i == Ylenses_blank ? j + 1 :
9085 i == Ymagnify_blank ? j + 1 :
9086 i == Ygrass_blank ? j + 1 :
9087 i == Ydirt_blank ? j + 1 :
9088 i == Xamoeba_1 ? 0 :
9089 i == Xamoeba_2 ? 1 :
9090 i == Xamoeba_3 ? 2 :
9091 i == Xamoeba_4 ? 3 :
9092 i == Xamoeba_5 ? 0 :
9093 i == Xamoeba_6 ? 1 :
9094 i == Xamoeba_7 ? 2 :
9095 i == Xamoeba_8 ? 3 :
9096 i == Xexit_2 ? j + 8 :
9097 i == Xexit_3 ? j + 16 :
9098 i == Xdynamite_1 ? 0 :
9099 i == Xdynamite_2 ? 8 :
9100 i == Xdynamite_3 ? 16 :
9101 i == Xdynamite_4 ? 24 :
9102 i == Xsand_stonein_1 ? j + 1 :
9103 i == Xsand_stonein_2 ? j + 9 :
9104 i == Xsand_stonein_3 ? j + 17 :
9105 i == Xsand_stonein_4 ? j + 25 :
9106 i == Xsand_stoneout_1 && j == 0 ? 0 :
9107 i == Xsand_stoneout_1 && j == 1 ? 0 :
9108 i == Xsand_stoneout_1 && j == 2 ? 1 :
9109 i == Xsand_stoneout_1 && j == 3 ? 2 :
9110 i == Xsand_stoneout_1 && j == 4 ? 2 :
9111 i == Xsand_stoneout_1 && j == 5 ? 3 :
9112 i == Xsand_stoneout_1 && j == 6 ? 4 :
9113 i == Xsand_stoneout_1 && j == 7 ? 4 :
9114 i == Xsand_stoneout_2 && j == 0 ? 5 :
9115 i == Xsand_stoneout_2 && j == 1 ? 6 :
9116 i == Xsand_stoneout_2 && j == 2 ? 7 :
9117 i == Xsand_stoneout_2 && j == 3 ? 8 :
9118 i == Xsand_stoneout_2 && j == 4 ? 9 :
9119 i == Xsand_stoneout_2 && j == 5 ? 11 :
9120 i == Xsand_stoneout_2 && j == 6 ? 13 :
9121 i == Xsand_stoneout_2 && j == 7 ? 15 :
9122 i == Xboom_bug && j == 1 ? 2 :
9123 i == Xboom_bug && j == 2 ? 2 :
9124 i == Xboom_bug && j == 3 ? 4 :
9125 i == Xboom_bug && j == 4 ? 4 :
9126 i == Xboom_bug && j == 5 ? 2 :
9127 i == Xboom_bug && j == 6 ? 2 :
9128 i == Xboom_bug && j == 7 ? 0 :
9129 i == Xboom_tank && j == 1 ? 2 :
9130 i == Xboom_tank && j == 2 ? 2 :
9131 i == Xboom_tank && j == 3 ? 4 :
9132 i == Xboom_tank && j == 4 ? 4 :
9133 i == Xboom_tank && j == 5 ? 2 :
9134 i == Xboom_tank && j == 6 ? 2 :
9135 i == Xboom_tank && j == 7 ? 0 :
9136 i == Xboom_android && j == 7 ? 6 :
9137 i == Xboom_1 && j == 1 ? 2 :
9138 i == Xboom_1 && j == 2 ? 2 :
9139 i == Xboom_1 && j == 3 ? 4 :
9140 i == Xboom_1 && j == 4 ? 4 :
9141 i == Xboom_1 && j == 5 ? 6 :
9142 i == Xboom_1 && j == 6 ? 6 :
9143 i == Xboom_1 && j == 7 ? 8 :
9144 i == Xboom_2 && j == 0 ? 8 :
9145 i == Xboom_2 && j == 1 ? 8 :
9146 i == Xboom_2 && j == 2 ? 10 :
9147 i == Xboom_2 && j == 3 ? 10 :
9148 i == Xboom_2 && j == 4 ? 10 :
9149 i == Xboom_2 && j == 5 ? 12 :
9150 i == Xboom_2 && j == 6 ? 12 :
9151 i == Xboom_2 && j == 7 ? 12 :
9152 special_animation && j == 4 ? 3 :
9153 effective_action != action ? 0 :
9155 int frame = getAnimationFrame(g->anim_frames,
9158 g->anim_start_frame,
9161 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9162 g->double_movement && is_backside);
9164 g_em->bitmap = src_bitmap;
9165 g_em->src_x = src_x;
9166 g_em->src_y = src_y;
9167 g_em->src_offset_x = 0;
9168 g_em->src_offset_y = 0;
9169 g_em->dst_offset_x = 0;
9170 g_em->dst_offset_y = 0;
9171 g_em->width = TILEX;
9172 g_em->height = TILEY;
9174 g_em->preserve_background = FALSE;
9176 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9179 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9180 effective_action == ACTION_MOVING ||
9181 effective_action == ACTION_PUSHING ||
9182 effective_action == ACTION_EATING)) ||
9183 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9184 effective_action == ACTION_EMPTYING)))
9187 (effective_action == ACTION_FALLING ||
9188 effective_action == ACTION_FILLING ||
9189 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9190 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9191 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9192 int num_steps = (i == Ydrip_1_s ? 16 :
9193 i == Ydrip_1_sB ? 16 :
9194 i == Ydrip_2_s ? 16 :
9195 i == Ydrip_2_sB ? 16 :
9196 i == Xsand_stonein_1 ? 32 :
9197 i == Xsand_stonein_2 ? 32 :
9198 i == Xsand_stonein_3 ? 32 :
9199 i == Xsand_stonein_4 ? 32 :
9200 i == Xsand_stoneout_1 ? 16 :
9201 i == Xsand_stoneout_2 ? 16 : 8);
9202 int cx = ABS(dx) * (TILEX / num_steps);
9203 int cy = ABS(dy) * (TILEY / num_steps);
9204 int step_frame = (i == Ydrip_2_s ? j + 8 :
9205 i == Ydrip_2_sB ? j + 8 :
9206 i == Xsand_stonein_2 ? j + 8 :
9207 i == Xsand_stonein_3 ? j + 16 :
9208 i == Xsand_stonein_4 ? j + 24 :
9209 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9210 int step = (is_backside ? step_frame : num_steps - step_frame);
9212 if (is_backside) // tile where movement starts
9214 if (dx < 0 || dy < 0)
9216 g_em->src_offset_x = cx * step;
9217 g_em->src_offset_y = cy * step;
9221 g_em->dst_offset_x = cx * step;
9222 g_em->dst_offset_y = cy * step;
9225 else // tile where movement ends
9227 if (dx < 0 || dy < 0)
9229 g_em->dst_offset_x = cx * step;
9230 g_em->dst_offset_y = cy * step;
9234 g_em->src_offset_x = cx * step;
9235 g_em->src_offset_y = cy * step;
9239 g_em->width = TILEX - cx * step;
9240 g_em->height = TILEY - cy * step;
9243 // create unique graphic identifier to decide if tile must be redrawn
9244 /* bit 31 - 16 (16 bit): EM style graphic
9245 bit 15 - 12 ( 4 bit): EM style frame
9246 bit 11 - 6 ( 6 bit): graphic width
9247 bit 5 - 0 ( 6 bit): graphic height */
9248 g_em->unique_identifier =
9249 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9253 for (i = 0; i < GAME_TILE_MAX; i++)
9255 for (j = 0; j < 8; j++)
9257 int element = object_mapping[i].element_rnd;
9258 int action = object_mapping[i].action;
9259 int direction = object_mapping[i].direction;
9260 boolean is_backside = object_mapping[i].is_backside;
9261 int graphic_action = el_act_dir2img(element, action, direction);
9262 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9264 if ((action == ACTION_SMASHED_BY_ROCK ||
9265 action == ACTION_SMASHED_BY_SPRING ||
9266 action == ACTION_EATING) &&
9267 graphic_action == graphic_default)
9269 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9270 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9271 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9272 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9275 // no separate animation for "smashed by rock" -- use rock instead
9276 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9277 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9279 g_em->bitmap = g_xx->bitmap;
9280 g_em->src_x = g_xx->src_x;
9281 g_em->src_y = g_xx->src_y;
9282 g_em->src_offset_x = g_xx->src_offset_x;
9283 g_em->src_offset_y = g_xx->src_offset_y;
9284 g_em->dst_offset_x = g_xx->dst_offset_x;
9285 g_em->dst_offset_y = g_xx->dst_offset_y;
9286 g_em->width = g_xx->width;
9287 g_em->height = g_xx->height;
9288 g_em->unique_identifier = g_xx->unique_identifier;
9291 g_em->preserve_background = TRUE;
9296 for (p = 0; p < MAX_PLAYERS; p++)
9298 for (i = 0; i < PLY_MAX; i++)
9300 int element = player_mapping[p][i].element_rnd;
9301 int action = player_mapping[p][i].action;
9302 int direction = player_mapping[p][i].direction;
9304 for (j = 0; j < 8; j++)
9306 int effective_element = element;
9307 int effective_action = action;
9308 int graphic = (direction == MV_NONE ?
9309 el_act2img(effective_element, effective_action) :
9310 el_act_dir2img(effective_element, effective_action,
9312 struct GraphicInfo *g = &graphic_info[graphic];
9313 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9317 int frame = getAnimationFrame(g->anim_frames,
9320 g->anim_start_frame,
9323 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9325 g_em->bitmap = src_bitmap;
9326 g_em->src_x = src_x;
9327 g_em->src_y = src_y;
9328 g_em->src_offset_x = 0;
9329 g_em->src_offset_y = 0;
9330 g_em->dst_offset_x = 0;
9331 g_em->dst_offset_y = 0;
9332 g_em->width = TILEX;
9333 g_em->height = TILEY;
9339 static void CheckSaveEngineSnapshot_EM(int frame,
9340 boolean any_player_moving,
9341 boolean any_player_snapping,
9342 boolean any_player_dropping)
9344 if (frame == 7 && !any_player_dropping)
9346 if (!local_player->was_waiting)
9348 if (!CheckSaveEngineSnapshotToList())
9351 local_player->was_waiting = TRUE;
9354 else if (any_player_moving || any_player_snapping || any_player_dropping)
9356 local_player->was_waiting = FALSE;
9360 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9361 boolean murphy_is_dropping)
9363 if (murphy_is_waiting)
9365 if (!local_player->was_waiting)
9367 if (!CheckSaveEngineSnapshotToList())
9370 local_player->was_waiting = TRUE;
9375 local_player->was_waiting = FALSE;
9379 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9380 boolean button_released)
9382 if (button_released)
9384 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9385 CheckSaveEngineSnapshotToList();
9387 else if (element_clicked)
9389 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9390 CheckSaveEngineSnapshotToList();
9392 game.snapshot.changed_action = TRUE;
9396 boolean CheckSingleStepMode_EM(int frame,
9397 boolean any_player_moving,
9398 boolean any_player_snapping,
9399 boolean any_player_dropping)
9401 if (tape.single_step && tape.recording && !tape.pausing)
9402 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9403 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9405 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9406 any_player_snapping, any_player_dropping);
9408 return tape.pausing;
9411 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9412 boolean murphy_is_dropping)
9414 boolean murphy_starts_dropping = FALSE;
9417 for (i = 0; i < MAX_PLAYERS; i++)
9418 if (stored_player[i].force_dropping)
9419 murphy_starts_dropping = TRUE;
9421 if (tape.single_step && tape.recording && !tape.pausing)
9422 if (murphy_is_waiting && !murphy_starts_dropping)
9423 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9425 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9428 void CheckSingleStepMode_MM(boolean element_clicked,
9429 boolean button_released)
9431 if (tape.single_step && tape.recording && !tape.pausing)
9432 if (button_released)
9433 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9435 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9438 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9439 int graphic, int sync_frame)
9441 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9443 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9446 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9448 return (IS_NEXT_FRAME(sync_frame, graphic));
9451 int getGraphicInfo_Delay(int graphic)
9453 return graphic_info[graphic].anim_delay;
9456 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9458 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9461 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9467 void PlayMenuSoundExt(int sound)
9469 if (sound == SND_UNDEFINED)
9472 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9473 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9476 if (IS_LOOP_SOUND(sound))
9477 PlaySoundLoop(sound);
9482 void PlayMenuSound(void)
9484 PlayMenuSoundExt(menu.sound[game_status]);
9487 void PlayMenuSoundStereo(int sound, int stereo_position)
9489 if (sound == SND_UNDEFINED)
9492 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9493 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9496 if (IS_LOOP_SOUND(sound))
9497 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9499 PlaySoundStereo(sound, stereo_position);
9502 void PlayMenuSoundIfLoopExt(int sound)
9504 if (sound == SND_UNDEFINED)
9507 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9508 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9511 if (IS_LOOP_SOUND(sound))
9512 PlaySoundLoop(sound);
9515 void PlayMenuSoundIfLoop(void)
9517 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9520 void PlayMenuMusicExt(int music)
9522 if (music == MUS_UNDEFINED)
9525 if (!setup.sound_music)
9528 if (IS_LOOP_MUSIC(music))
9529 PlayMusicLoop(music);
9534 void PlayMenuMusic(void)
9536 char *curr_music = getCurrentlyPlayingMusicFilename();
9537 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9539 if (!strEqual(curr_music, next_music))
9540 PlayMenuMusicExt(menu.music[game_status]);
9543 void PlayMenuSoundsAndMusic(void)
9549 static void FadeMenuSounds(void)
9554 static void FadeMenuMusic(void)
9556 char *curr_music = getCurrentlyPlayingMusicFilename();
9557 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9559 if (!strEqual(curr_music, next_music))
9563 void FadeMenuSoundsAndMusic(void)
9569 void PlaySoundActivating(void)
9572 PlaySound(SND_MENU_ITEM_ACTIVATING);
9576 void PlaySoundSelecting(void)
9579 PlaySound(SND_MENU_ITEM_SELECTING);
9583 void ToggleFullscreenIfNeeded(void)
9585 // if setup and video fullscreen state are already matching, nothing do do
9586 if (setup.fullscreen == video.fullscreen_enabled ||
9587 !video.fullscreen_available)
9590 SDLSetWindowFullscreen(setup.fullscreen);
9592 // set setup value according to successfully changed fullscreen mode
9593 setup.fullscreen = video.fullscreen_enabled;
9596 void ChangeWindowScalingIfNeeded(void)
9598 // if setup and video window scaling are already matching, nothing do do
9599 if (setup.window_scaling_percent == video.window_scaling_percent ||
9600 video.fullscreen_enabled)
9603 SDLSetWindowScaling(setup.window_scaling_percent);
9605 // set setup value according to successfully changed window scaling
9606 setup.window_scaling_percent = video.window_scaling_percent;
9609 void ChangeVsyncModeIfNeeded(void)
9611 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9612 int video_vsync_mode = video.vsync_mode;
9614 // if setup and video vsync mode are already matching, nothing do do
9615 if (setup_vsync_mode == video_vsync_mode)
9618 // if renderer is using OpenGL, vsync mode can directly be changed
9619 SDLSetScreenVsyncMode(setup.vsync_mode);
9621 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9622 if (video.vsync_mode == video_vsync_mode)
9624 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9626 // save backbuffer content which gets lost when re-creating screen
9627 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9629 // force re-creating screen and renderer to set new vsync mode
9630 video.fullscreen_enabled = !setup.fullscreen;
9632 // when creating new renderer, destroy textures linked to old renderer
9633 FreeAllImageTextures(); // needs old renderer to free the textures
9635 // re-create screen and renderer (including change of vsync mode)
9636 ChangeVideoModeIfNeeded(setup.fullscreen);
9638 // set setup value according to successfully changed fullscreen mode
9639 setup.fullscreen = video.fullscreen_enabled;
9641 // restore backbuffer content from temporary backbuffer backup bitmap
9642 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9643 FreeBitmap(tmp_backbuffer);
9645 // update visible window/screen
9646 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9648 // when changing vsync mode, re-create textures for new renderer
9649 InitImageTextures();
9652 // set setup value according to successfully changed vsync mode
9653 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9656 static void JoinRectangles(int *x, int *y, int *width, int *height,
9657 int x2, int y2, int width2, int height2)
9659 // do not join with "off-screen" rectangle
9660 if (x2 == -1 || y2 == -1)
9665 *width = MAX(*width, width2);
9666 *height = MAX(*height, height2);
9669 void SetAnimStatus(int anim_status_new)
9671 if (anim_status_new == GAME_MODE_MAIN)
9672 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9673 else if (anim_status_new == GAME_MODE_NAMES)
9674 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9675 else if (anim_status_new == GAME_MODE_SCORES)
9676 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9678 global.anim_status_next = anim_status_new;
9680 // directly set screen modes that are entered without fading
9681 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9682 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9683 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9684 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9685 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9686 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9687 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9688 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9689 global.anim_status = global.anim_status_next;
9692 void SetGameStatus(int game_status_new)
9694 if (game_status_new != game_status)
9695 game_status_last_screen = game_status;
9697 game_status = game_status_new;
9699 SetAnimStatus(game_status_new);
9702 void SetFontStatus(int game_status_new)
9704 static int last_game_status = -1;
9706 if (game_status_new != -1)
9708 // set game status for font use after storing last game status
9709 last_game_status = game_status;
9710 game_status = game_status_new;
9714 // reset game status after font use from last stored game status
9715 game_status = last_game_status;
9719 void ResetFontStatus(void)
9724 void SetLevelSetInfo(char *identifier, int level_nr)
9726 setString(&levelset.identifier, identifier);
9728 levelset.level_nr = level_nr;
9731 boolean CheckIfAllViewportsHaveChanged(void)
9733 // if game status has not changed, viewports have not changed either
9734 if (game_status == game_status_last)
9737 // check if all viewports have changed with current game status
9739 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9740 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9741 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9742 int new_real_sx = vp_playfield->x;
9743 int new_real_sy = vp_playfield->y;
9744 int new_full_sxsize = vp_playfield->width;
9745 int new_full_sysize = vp_playfield->height;
9746 int new_dx = vp_door_1->x;
9747 int new_dy = vp_door_1->y;
9748 int new_dxsize = vp_door_1->width;
9749 int new_dysize = vp_door_1->height;
9750 int new_vx = vp_door_2->x;
9751 int new_vy = vp_door_2->y;
9752 int new_vxsize = vp_door_2->width;
9753 int new_vysize = vp_door_2->height;
9755 boolean playfield_viewport_has_changed =
9756 (new_real_sx != REAL_SX ||
9757 new_real_sy != REAL_SY ||
9758 new_full_sxsize != FULL_SXSIZE ||
9759 new_full_sysize != FULL_SYSIZE);
9761 boolean door_1_viewport_has_changed =
9764 new_dxsize != DXSIZE ||
9765 new_dysize != DYSIZE);
9767 boolean door_2_viewport_has_changed =
9770 new_vxsize != VXSIZE ||
9771 new_vysize != VYSIZE ||
9772 game_status_last == GAME_MODE_EDITOR);
9774 return (playfield_viewport_has_changed &&
9775 door_1_viewport_has_changed &&
9776 door_2_viewport_has_changed);
9779 boolean CheckFadeAll(void)
9781 return (CheckIfGlobalBorderHasChanged() ||
9782 CheckIfAllViewportsHaveChanged());
9785 void ChangeViewportPropertiesIfNeeded(void)
9787 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9788 FALSE : setup.small_game_graphics);
9789 int gfx_game_mode = getGlobalGameStatus(game_status);
9790 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9792 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9793 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9794 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9795 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9796 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9797 int new_win_xsize = vp_window->width;
9798 int new_win_ysize = vp_window->height;
9799 int border_left = vp_playfield->border_left;
9800 int border_right = vp_playfield->border_right;
9801 int border_top = vp_playfield->border_top;
9802 int border_bottom = vp_playfield->border_bottom;
9803 int new_sx = vp_playfield->x + border_left;
9804 int new_sy = vp_playfield->y + border_top;
9805 int new_sxsize = vp_playfield->width - border_left - border_right;
9806 int new_sysize = vp_playfield->height - border_top - border_bottom;
9807 int new_real_sx = vp_playfield->x;
9808 int new_real_sy = vp_playfield->y;
9809 int new_full_sxsize = vp_playfield->width;
9810 int new_full_sysize = vp_playfield->height;
9811 int new_dx = vp_door_1->x;
9812 int new_dy = vp_door_1->y;
9813 int new_dxsize = vp_door_1->width;
9814 int new_dysize = vp_door_1->height;
9815 int new_vx = vp_door_2->x;
9816 int new_vy = vp_door_2->y;
9817 int new_vxsize = vp_door_2->width;
9818 int new_vysize = vp_door_2->height;
9819 int new_ex = vp_door_3->x;
9820 int new_ey = vp_door_3->y;
9821 int new_exsize = vp_door_3->width;
9822 int new_eysize = vp_door_3->height;
9823 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9824 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9825 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9826 int new_scr_fieldx = new_sxsize / tilesize;
9827 int new_scr_fieldy = new_sysize / tilesize;
9828 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9829 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9830 boolean init_gfx_buffers = FALSE;
9831 boolean init_video_buffer = FALSE;
9832 boolean init_gadgets_and_anims = FALSE;
9833 boolean init_em_graphics = FALSE;
9835 if (new_win_xsize != WIN_XSIZE ||
9836 new_win_ysize != WIN_YSIZE)
9838 WIN_XSIZE = new_win_xsize;
9839 WIN_YSIZE = new_win_ysize;
9841 init_video_buffer = TRUE;
9842 init_gfx_buffers = TRUE;
9843 init_gadgets_and_anims = TRUE;
9845 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9848 if (new_scr_fieldx != SCR_FIELDX ||
9849 new_scr_fieldy != SCR_FIELDY)
9851 // this always toggles between MAIN and GAME when using small tile size
9853 SCR_FIELDX = new_scr_fieldx;
9854 SCR_FIELDY = new_scr_fieldy;
9856 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9867 new_sxsize != SXSIZE ||
9868 new_sysize != SYSIZE ||
9869 new_dxsize != DXSIZE ||
9870 new_dysize != DYSIZE ||
9871 new_vxsize != VXSIZE ||
9872 new_vysize != VYSIZE ||
9873 new_exsize != EXSIZE ||
9874 new_eysize != EYSIZE ||
9875 new_real_sx != REAL_SX ||
9876 new_real_sy != REAL_SY ||
9877 new_full_sxsize != FULL_SXSIZE ||
9878 new_full_sysize != FULL_SYSIZE ||
9879 new_tilesize_var != TILESIZE_VAR
9882 // ------------------------------------------------------------------------
9883 // determine next fading area for changed viewport definitions
9884 // ------------------------------------------------------------------------
9886 // start with current playfield area (default fading area)
9889 FADE_SXSIZE = FULL_SXSIZE;
9890 FADE_SYSIZE = FULL_SYSIZE;
9892 // add new playfield area if position or size has changed
9893 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9894 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9896 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9897 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9900 // add current and new door 1 area if position or size has changed
9901 if (new_dx != DX || new_dy != DY ||
9902 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9904 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9905 DX, DY, DXSIZE, DYSIZE);
9906 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9907 new_dx, new_dy, new_dxsize, new_dysize);
9910 // add current and new door 2 area if position or size has changed
9911 if (new_vx != VX || new_vy != VY ||
9912 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9914 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9915 VX, VY, VXSIZE, VYSIZE);
9916 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9917 new_vx, new_vy, new_vxsize, new_vysize);
9920 // ------------------------------------------------------------------------
9921 // handle changed tile size
9922 // ------------------------------------------------------------------------
9924 if (new_tilesize_var != TILESIZE_VAR)
9926 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9928 // changing tile size invalidates scroll values of engine snapshots
9929 FreeEngineSnapshotSingle();
9931 // changing tile size requires update of graphic mapping for EM engine
9932 init_em_graphics = TRUE;
9943 SXSIZE = new_sxsize;
9944 SYSIZE = new_sysize;
9945 DXSIZE = new_dxsize;
9946 DYSIZE = new_dysize;
9947 VXSIZE = new_vxsize;
9948 VYSIZE = new_vysize;
9949 EXSIZE = new_exsize;
9950 EYSIZE = new_eysize;
9951 REAL_SX = new_real_sx;
9952 REAL_SY = new_real_sy;
9953 FULL_SXSIZE = new_full_sxsize;
9954 FULL_SYSIZE = new_full_sysize;
9955 TILESIZE_VAR = new_tilesize_var;
9957 init_gfx_buffers = TRUE;
9958 init_gadgets_and_anims = TRUE;
9960 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9961 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9964 if (init_gfx_buffers)
9966 // Debug("tools:viewport", "init_gfx_buffers");
9968 SCR_FIELDX = new_scr_fieldx_buffers;
9969 SCR_FIELDY = new_scr_fieldy_buffers;
9973 SCR_FIELDX = new_scr_fieldx;
9974 SCR_FIELDY = new_scr_fieldy;
9976 SetDrawDeactivationMask(REDRAW_NONE);
9977 SetDrawBackgroundMask(REDRAW_FIELD);
9980 if (init_video_buffer)
9982 // Debug("tools:viewport", "init_video_buffer");
9984 FreeAllImageTextures(); // needs old renderer to free the textures
9986 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9987 InitImageTextures();
9990 if (init_gadgets_and_anims)
9992 // Debug("tools:viewport", "init_gadgets_and_anims");
9995 InitGlobalAnimations();
9998 if (init_em_graphics)
10000 InitGraphicInfo_EM();
10004 void OpenURL(char *url)
10006 #if SDL_VERSION_ATLEAST(2,0,14)
10009 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
10010 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
10011 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
10015 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
10017 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
10021 // ============================================================================
10023 // ============================================================================
10025 #if defined(PLATFORM_WINDOWS)
10026 /* FILETIME of Jan 1 1970 00:00:00. */
10027 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
10030 * timezone information is stored outside the kernel so tzp isn't used anymore.
10032 * Note: this function is not for Win32 high precision timing purpose. See
10035 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
10037 FILETIME file_time;
10038 SYSTEMTIME system_time;
10039 ULARGE_INTEGER ularge;
10041 GetSystemTime(&system_time);
10042 SystemTimeToFileTime(&system_time, &file_time);
10043 ularge.LowPart = file_time.dwLowDateTime;
10044 ularge.HighPart = file_time.dwHighDateTime;
10046 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10047 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10053 static char *test_init_uuid_random_function_simple(void)
10055 static char seed_text[100];
10056 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10058 sprintf(seed_text, "%d", seed);
10063 static char *test_init_uuid_random_function_better(void)
10065 static char seed_text[100];
10066 struct timeval current_time;
10068 gettimeofday(¤t_time, NULL);
10070 prng_seed_bytes(¤t_time, sizeof(current_time));
10072 sprintf(seed_text, "%ld.%ld",
10073 (long)current_time.tv_sec,
10074 (long)current_time.tv_usec);
10079 #if defined(PLATFORM_WINDOWS)
10080 static char *test_init_uuid_random_function_better_windows(void)
10082 static char seed_text[100];
10083 struct timeval current_time;
10085 gettimeofday_windows(¤t_time, NULL);
10087 prng_seed_bytes(¤t_time, sizeof(current_time));
10089 sprintf(seed_text, "%ld.%ld",
10090 (long)current_time.tv_sec,
10091 (long)current_time.tv_usec);
10097 static unsigned int test_uuid_random_function_simple(int max)
10099 return GetSimpleRandom(max);
10102 static unsigned int test_uuid_random_function_better(int max)
10104 return (max > 0 ? prng_get_uint() % max : 0);
10107 #if defined(PLATFORM_WINDOWS)
10108 #define NUM_UUID_TESTS 3
10110 #define NUM_UUID_TESTS 2
10113 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10115 struct hashtable *hash_seeds =
10116 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10117 struct hashtable *hash_uuids =
10118 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10119 static char message[100];
10122 char *random_name = (nr == 0 ? "simple" : "better");
10123 char *random_type = (always_seed ? "always" : "only once");
10124 char *(*init_random_function)(void) =
10126 test_init_uuid_random_function_simple :
10127 test_init_uuid_random_function_better);
10128 unsigned int (*random_function)(int) =
10130 test_uuid_random_function_simple :
10131 test_uuid_random_function_better);
10134 #if defined(PLATFORM_WINDOWS)
10137 random_name = "windows";
10138 init_random_function = test_init_uuid_random_function_better_windows;
10144 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10145 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10147 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10148 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10149 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10151 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10155 // always initialize random number generator at least once
10156 init_random_function();
10158 unsigned int time_start = SDL_GetTicks();
10160 for (i = 0; i < num_uuids; i++)
10164 char *seed = getStringCopy(init_random_function());
10166 hashtable_remove(hash_seeds, seed);
10167 hashtable_insert(hash_seeds, seed, "1");
10170 char *uuid = getStringCopy(getUUIDExt(random_function));
10172 hashtable_remove(hash_uuids, uuid);
10173 hashtable_insert(hash_uuids, uuid, "1");
10176 int num_unique_seeds = hashtable_count(hash_seeds);
10177 int num_unique_uuids = hashtable_count(hash_uuids);
10179 unsigned int time_needed = SDL_GetTicks() - time_start;
10181 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10183 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10186 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10188 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10189 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10191 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10193 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10195 Request(message, REQ_CONFIRM);
10197 hashtable_destroy(hash_seeds, 0);
10198 hashtable_destroy(hash_uuids, 0);
10201 void TestGeneratingUUIDs(void)
10203 int num_uuids = 1000000;
10206 for (i = 0; i < NUM_UUID_TESTS; i++)
10207 for (j = 0; j < 2; j++)
10208 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10210 CloseAllAndExit(0);