1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 #define DEBUG_FRAME_TIME FALSE
28 // tool button identifiers
29 #define TOOL_CTRL_ID_YES 0
30 #define TOOL_CTRL_ID_NO 1
31 #define TOOL_CTRL_ID_CONFIRM 2
32 #define TOOL_CTRL_ID_PLAYER_1 3
33 #define TOOL_CTRL_ID_PLAYER_2 4
34 #define TOOL_CTRL_ID_PLAYER_3 5
35 #define TOOL_CTRL_ID_PLAYER_4 6
36 #define TOOL_CTRL_ID_TOUCH_YES 7
37 #define TOOL_CTRL_ID_TOUCH_NO 8
38 #define TOOL_CTRL_ID_TOUCH_CONFIRM 9
40 #define NUM_TOOL_BUTTONS 10
42 // constants for number of doors and door parts
44 #define NUM_PANELS NUM_DOORS
45 // #define NUM_PANELS 0
46 #define MAX_PARTS_PER_DOOR 8
47 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
48 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
51 struct DoorPartOrderInfo
57 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
59 struct DoorPartControlInfo
63 struct DoorPartPosInfo *pos;
66 static struct DoorPartControlInfo door_part_controls[] =
70 IMG_GFX_DOOR_1_PART_1,
75 IMG_GFX_DOOR_1_PART_2,
80 IMG_GFX_DOOR_1_PART_3,
85 IMG_GFX_DOOR_1_PART_4,
90 IMG_GFX_DOOR_1_PART_5,
95 IMG_GFX_DOOR_1_PART_6,
100 IMG_GFX_DOOR_1_PART_7,
105 IMG_GFX_DOOR_1_PART_8,
111 IMG_GFX_DOOR_2_PART_1,
116 IMG_GFX_DOOR_2_PART_2,
121 IMG_GFX_DOOR_2_PART_3,
126 IMG_GFX_DOOR_2_PART_4,
131 IMG_GFX_DOOR_2_PART_5,
136 IMG_GFX_DOOR_2_PART_6,
141 IMG_GFX_DOOR_2_PART_7,
146 IMG_GFX_DOOR_2_PART_8,
152 IMG_BACKGROUND_PANEL,
168 static struct XY xy_topdown[] =
177 // forward declaration for internal use
178 static void UnmapToolButtons(void);
179 static void HandleToolButtons(struct GadgetInfo *);
180 static int el_act_dir2crm(int, int, int);
181 static int el_act2crm(int, int);
183 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
184 static int request_gadget_id = -1;
186 static char *print_if_not_empty(int element)
188 static char *s = NULL;
189 char *token_name = element_info[element].token_name;
194 s = checked_malloc(strlen(token_name) + 10 + 1);
196 if (element != EL_EMPTY)
197 sprintf(s, "%d\t['%s']", element, token_name);
199 sprintf(s, "%d", element);
204 int getFieldbufferOffsetX_RND(int dir, int pos)
206 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
207 int dx = (dir & MV_HORIZONTAL ? pos : 0);
208 int dx_var = dx * TILESIZE_VAR / TILESIZE;
211 if (EVEN(SCR_FIELDX))
213 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
214 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
216 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
217 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
219 fx += (dx_var > 0 ? TILEX_VAR : 0);
226 if (full_lev_fieldx <= SCR_FIELDX)
228 if (EVEN(SCR_FIELDX))
229 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
231 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
237 int getFieldbufferOffsetY_RND(int dir, int pos)
239 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
240 int dy = (dir & MV_VERTICAL ? pos : 0);
241 int dy_var = dy * TILESIZE_VAR / TILESIZE;
244 if (EVEN(SCR_FIELDY))
246 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
247 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
249 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
250 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
252 fy += (dy_var > 0 ? TILEY_VAR : 0);
259 if (full_lev_fieldy <= SCR_FIELDY)
261 if (EVEN(SCR_FIELDY))
262 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
264 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
270 static int getLevelFromScreenX_RND(int sx)
272 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
275 int lx = LEVELX((px + dx) / TILESIZE_VAR);
280 static int getLevelFromScreenY_RND(int sy)
282 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
285 int ly = LEVELY((py + dy) / TILESIZE_VAR);
290 static int getLevelFromScreenX_EM(int sx)
292 int level_xsize = level.native_em_level->cav->width;
293 int full_xsize = level_xsize * TILESIZE_VAR;
295 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
297 int fx = getFieldbufferOffsetX_EM();
300 int lx = LEVELX((px + dx) / TILESIZE_VAR);
305 static int getLevelFromScreenY_EM(int sy)
307 int level_ysize = level.native_em_level->cav->height;
308 int full_ysize = level_ysize * TILESIZE_VAR;
310 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
312 int fy = getFieldbufferOffsetY_EM();
315 int ly = LEVELY((py + dy) / TILESIZE_VAR);
320 static int getLevelFromScreenX_SP(int sx)
322 int menBorder = setup.sp_show_border_elements;
323 int level_xsize = level.native_sp_level->width;
324 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
326 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
328 int fx = getFieldbufferOffsetX_SP();
331 int lx = LEVELX((px + dx) / TILESIZE_VAR);
336 static int getLevelFromScreenY_SP(int sy)
338 int menBorder = setup.sp_show_border_elements;
339 int level_ysize = level.native_sp_level->height;
340 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
342 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
344 int fy = getFieldbufferOffsetY_SP();
347 int ly = LEVELY((py + dy) / TILESIZE_VAR);
352 static int getLevelFromScreenX_MM(int sx)
354 int level_xsize = level.native_mm_level->fieldx;
355 int full_xsize = level_xsize * TILESIZE_VAR;
357 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
360 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
365 static int getLevelFromScreenY_MM(int sy)
367 int level_ysize = level.native_mm_level->fieldy;
368 int full_ysize = level_ysize * TILESIZE_VAR;
370 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
373 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
378 int getLevelFromScreenX(int x)
380 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
381 return getLevelFromScreenX_EM(x);
382 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
383 return getLevelFromScreenX_SP(x);
384 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
385 return getLevelFromScreenX_MM(x);
387 return getLevelFromScreenX_RND(x);
390 int getLevelFromScreenY(int y)
392 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
393 return getLevelFromScreenY_EM(y);
394 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
395 return getLevelFromScreenY_SP(y);
396 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
397 return getLevelFromScreenY_MM(y);
399 return getLevelFromScreenY_RND(y);
402 int getScreenFieldSizeX(void)
404 return (tape.playing ? tape.scr_fieldx : SCR_FIELDX);
407 int getScreenFieldSizeY(void)
409 return (tape.playing ? tape.scr_fieldy : SCR_FIELDY);
412 void DumpTile(int x, int y)
419 Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
422 if (!IN_LEV_FIELD(x, y))
424 Info("(not in level field)");
430 token_name = element_info[Tile[x][y]].token_name;
432 Info("Tile: %d\t['%s']", Tile[x][y], token_name);
433 Info("Back: %s", print_if_not_empty(Back[x][y]));
434 Info("Store: %s", print_if_not_empty(Store[x][y]));
435 Info("Store2: %s", print_if_not_empty(Store2[x][y]));
436 Info("StorePlayer: %s", print_if_not_empty(StorePlayer[x][y]));
437 Info("MovPos: %d", MovPos[x][y]);
438 Info("MovDir: %d", MovDir[x][y]);
439 Info("MovDelay: %d", MovDelay[x][y]);
440 Info("ChangeDelay: %d", ChangeDelay[x][y]);
441 Info("CustomValue: %d", CustomValue[x][y]);
442 Info("GfxElement: %d", GfxElement[x][y]);
443 Info("GfxAction: %d", GfxAction[x][y]);
444 Info("GfxFrame: %d [%d]", GfxFrame[x][y], FrameCounter);
445 Info("Player x/y: %d, %d", local_player->jx, local_player->jy);
449 void DumpTileFromScreen(int sx, int sy)
451 int lx = getLevelFromScreenX(sx);
452 int ly = getLevelFromScreenY(sy);
457 void SetDrawtoField(int mode)
459 if (mode == DRAW_TO_FIELDBUFFER)
465 BX2 = SCR_FIELDX + 1;
466 BY2 = SCR_FIELDY + 1;
468 drawto_field = fieldbuffer;
470 else // DRAW_TO_BACKBUFFER
476 BX2 = SCR_FIELDX - 1;
477 BY2 = SCR_FIELDY - 1;
479 drawto_field = backbuffer;
483 int GetDrawtoField(void)
485 return (drawto_field == fieldbuffer ? DRAW_TO_FIELDBUFFER : DRAW_TO_BACKBUFFER);
488 static void RedrawPlayfield_RND(void)
490 if (game.envelope_active)
493 DrawLevel(REDRAW_ALL);
497 void RedrawPlayfield(void)
499 if (game_status != GAME_MODE_PLAYING)
502 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
503 RedrawPlayfield_EM(TRUE);
504 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
505 RedrawPlayfield_SP(TRUE);
506 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
507 RedrawPlayfield_MM();
508 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
509 RedrawPlayfield_RND();
511 BlitScreenToBitmap(backbuffer);
513 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
517 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
520 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
521 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
523 // may happen for "border.draw_masked.*" with undefined "global.border.*"
524 if (src_bitmap == NULL)
527 if (x == -1 && y == -1)
530 if (draw_target == DRAW_TO_SCREEN)
531 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
533 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
536 static void DrawMaskedBorderExt_FIELD(int draw_target)
538 if (global.border_status >= GAME_MODE_MAIN &&
539 global.border_status <= GAME_MODE_PLAYING &&
540 border.draw_masked[global.border_status])
541 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
545 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
547 // when drawing to backbuffer, never draw border over open doors
548 if (draw_target == DRAW_TO_BACKBUFFER &&
549 (GetDoorState() & DOOR_OPEN_1))
552 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
553 (global.border_status != GAME_MODE_EDITOR ||
554 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
555 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
558 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
560 // when drawing to backbuffer, never draw border over open doors
561 if (draw_target == DRAW_TO_BACKBUFFER &&
562 (GetDoorState() & DOOR_OPEN_2))
565 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
566 global.border_status != GAME_MODE_EDITOR)
567 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
570 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
572 // currently not available
575 static void DrawMaskedBorderExt_ALL(int draw_target)
577 DrawMaskedBorderExt_FIELD(draw_target);
578 DrawMaskedBorderExt_DOOR_1(draw_target);
579 DrawMaskedBorderExt_DOOR_2(draw_target);
580 DrawMaskedBorderExt_DOOR_3(draw_target);
583 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
585 // never draw masked screen borders on borderless screens
586 if (global.border_status == GAME_MODE_LOADING ||
587 global.border_status == GAME_MODE_TITLE)
590 if (redraw_mask & REDRAW_ALL)
591 DrawMaskedBorderExt_ALL(draw_target);
594 if (redraw_mask & REDRAW_FIELD)
595 DrawMaskedBorderExt_FIELD(draw_target);
596 if (redraw_mask & REDRAW_DOOR_1)
597 DrawMaskedBorderExt_DOOR_1(draw_target);
598 if (redraw_mask & REDRAW_DOOR_2)
599 DrawMaskedBorderExt_DOOR_2(draw_target);
600 if (redraw_mask & REDRAW_DOOR_3)
601 DrawMaskedBorderExt_DOOR_3(draw_target);
605 void DrawMaskedBorder_FIELD(void)
607 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
610 void DrawMaskedBorder(int redraw_mask)
612 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
615 void DrawMaskedBorderToTarget(int draw_target)
617 if (draw_target == DRAW_TO_BACKBUFFER ||
618 draw_target == DRAW_TO_SCREEN)
620 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
624 int last_border_status = global.border_status;
626 if (draw_target == DRAW_TO_FADE_SOURCE)
628 global.border_status = gfx.fade_border_source_status;
629 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
631 else if (draw_target == DRAW_TO_FADE_TARGET)
633 global.border_status = gfx.fade_border_target_status;
634 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
637 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
639 global.border_status = last_border_status;
640 gfx.masked_border_bitmap_ptr = backbuffer;
644 void DrawTileCursor(int draw_target)
646 DrawTileCursor_MM(draw_target, game_status == GAME_MODE_PLAYING);
649 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
651 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
654 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
656 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
657 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
659 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
662 void BlitScreenToBitmap(Bitmap *target_bitmap)
664 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
665 BlitScreenToBitmap_EM(target_bitmap);
666 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
667 BlitScreenToBitmap_SP(target_bitmap);
668 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
669 BlitScreenToBitmap_MM(target_bitmap);
670 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
671 BlitScreenToBitmap_RND(target_bitmap);
673 redraw_mask |= REDRAW_FIELD;
676 static void DrawFramesPerSecond(void)
679 int font_nr = FONT_TEXT_2;
680 int font_width = getFontWidth(font_nr);
681 int draw_deactivation_mask = GetDrawDeactivationMask();
682 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
684 // draw FPS with leading space (needed if field buffer deactivated)
685 sprintf(text, " %04.1f fps", global.frames_per_second);
687 // override draw deactivation mask (required for invisible warp mode)
688 SetDrawDeactivationMask(REDRAW_NONE);
690 // draw opaque FPS if field buffer deactivated, else draw masked FPS
691 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
692 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
694 // set draw deactivation mask to previous value
695 SetDrawDeactivationMask(draw_deactivation_mask);
697 // force full-screen redraw in this frame
698 redraw_mask = REDRAW_ALL;
702 static void PrintFrameTimeDebugging(void)
704 static unsigned int last_counter = 0;
705 unsigned int counter = Counter();
706 int diff_1 = counter - last_counter;
707 int diff_2 = diff_1 - GAME_FRAME_DELAY;
709 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
710 char diff_bar[2 * diff_2_max + 5];
714 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
716 for (i = 0; i < diff_2_max; i++)
717 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
718 i >= diff_2_max - diff_2_cut ? '-' : ' ');
720 diff_bar[pos++] = '|';
722 for (i = 0; i < diff_2_max; i++)
723 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
725 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
727 diff_bar[pos++] = '\0';
729 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
732 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
735 last_counter = counter;
739 static int unifiedRedrawMask(int mask)
741 if (mask & REDRAW_ALL)
744 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
750 static boolean equalRedrawMasks(int mask_1, int mask_2)
752 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
755 void BackToFront(void)
757 static int last_redraw_mask = REDRAW_NONE;
759 // force screen redraw in every frame to continue drawing global animations
760 // (but always use the last redraw mask to prevent unwanted side effects)
761 if (redraw_mask == REDRAW_NONE)
762 redraw_mask = last_redraw_mask;
764 last_redraw_mask = redraw_mask;
767 // masked border now drawn immediately when blitting backbuffer to window
769 // draw masked border to all viewports, if defined
770 DrawMaskedBorder(redraw_mask);
773 // draw frames per second (only if debug mode is enabled)
774 if (redraw_mask & REDRAW_FPS)
775 DrawFramesPerSecond();
777 // remove playfield redraw before potentially merging with doors redraw
778 if (DrawingDeactivated(REAL_SX, REAL_SY))
779 redraw_mask &= ~REDRAW_FIELD;
781 // redraw complete window if both playfield and (some) doors need redraw
782 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
783 redraw_mask = REDRAW_ALL;
785 /* although redrawing the whole window would be fine for normal gameplay,
786 being able to only redraw the playfield is required for deactivating
787 certain drawing areas (mainly playfield) to work, which is needed for
788 warp-forward to be fast enough (by skipping redraw of most frames) */
790 if (redraw_mask & REDRAW_ALL)
792 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
794 else if (redraw_mask & REDRAW_FIELD)
796 BlitBitmap(backbuffer, window,
797 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
799 else if (redraw_mask & REDRAW_DOORS)
801 // merge door areas to prevent calling screen redraw more than once
807 if (redraw_mask & REDRAW_DOOR_1)
811 x2 = MAX(x2, DX + DXSIZE);
812 y2 = MAX(y2, DY + DYSIZE);
815 if (redraw_mask & REDRAW_DOOR_2)
819 x2 = MAX(x2, VX + VXSIZE);
820 y2 = MAX(y2, VY + VYSIZE);
823 if (redraw_mask & REDRAW_DOOR_3)
827 x2 = MAX(x2, EX + EXSIZE);
828 y2 = MAX(y2, EY + EYSIZE);
831 // make sure that at least one pixel is blitted, and inside the screen
832 // (else nothing is blitted, causing the animations not to be updated)
833 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
834 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
835 x2 = MIN(MAX(1, x2), WIN_XSIZE);
836 y2 = MIN(MAX(1, y2), WIN_YSIZE);
838 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
841 redraw_mask = REDRAW_NONE;
844 PrintFrameTimeDebugging();
848 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
850 unsigned int frame_delay_value_old = GetVideoFrameDelay();
852 SetVideoFrameDelay(frame_delay_value);
856 SetVideoFrameDelay(frame_delay_value_old);
859 static int fade_type_skip = FADE_TYPE_NONE;
861 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
863 void (*draw_border_function)(void) = NULL;
864 int x, y, width, height;
865 int fade_delay, post_delay;
867 if (fade_type == FADE_TYPE_FADE_OUT)
869 if (fade_type_skip != FADE_TYPE_NONE)
871 // skip all fade operations until specified fade operation
872 if (fade_type & fade_type_skip)
873 fade_type_skip = FADE_TYPE_NONE;
878 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
882 redraw_mask |= fade_mask;
884 if (fade_type == FADE_TYPE_SKIP)
886 fade_type_skip = fade_mode;
891 fade_delay = fading.fade_delay;
892 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
894 if (fade_type_skip != FADE_TYPE_NONE)
896 // skip all fade operations until specified fade operation
897 if (fade_type & fade_type_skip)
898 fade_type_skip = FADE_TYPE_NONE;
903 if (global.autoplay_leveldir)
908 if (fade_mask == REDRAW_FIELD)
913 height = FADE_SYSIZE;
915 if (border.draw_masked_when_fading)
916 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
918 DrawMaskedBorder_FIELD(); // draw once
928 // when switching screens without fading, set fade delay to zero
929 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
932 // do not display black frame when fading out without fade delay
933 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
936 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
937 draw_border_function);
939 redraw_mask &= ~fade_mask;
941 ClearAutoRepeatKeyEvents();
944 static void SetScreenStates_BeforeFadingIn(void)
946 // temporarily set screen mode for animations to screen after fading in
947 global.anim_status = global.anim_status_next;
949 // store backbuffer with all animations that will be started after fading in
950 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
952 // set screen mode for animations back to fading
953 global.anim_status = GAME_MODE_PSEUDO_FADING;
956 static void SetScreenStates_AfterFadingIn(void)
958 // store new source screen (to use correct masked border for fading)
959 gfx.fade_border_source_status = global.border_status;
961 global.anim_status = global.anim_status_next;
964 static void SetScreenStates_BeforeFadingOut(void)
966 // store new target screen (to use correct masked border for fading)
967 gfx.fade_border_target_status = game_status;
969 // set screen mode for animations to fading
970 global.anim_status = GAME_MODE_PSEUDO_FADING;
972 // store backbuffer with all animations that will be stopped for fading out
973 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
976 static void SetScreenStates_AfterFadingOut(void)
978 global.border_status = game_status;
981 void FadeIn(int fade_mask)
983 SetScreenStates_BeforeFadingIn();
986 DrawMaskedBorder(REDRAW_ALL);
989 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
990 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
992 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
996 FADE_SXSIZE = FULL_SXSIZE;
997 FADE_SYSIZE = FULL_SYSIZE;
999 // activate virtual buttons depending on upcoming game status
1000 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1001 game_status == GAME_MODE_PLAYING && !tape.playing)
1002 SetOverlayActive(TRUE);
1004 SetScreenStates_AfterFadingIn();
1006 // force update of global animation status in case of rapid screen changes
1007 redraw_mask = REDRAW_ALL;
1011 void FadeOut(int fade_mask)
1013 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1014 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1015 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1018 SetScreenStates_BeforeFadingOut();
1020 SetTileCursorActive(FALSE);
1021 SetOverlayActive(FALSE);
1024 DrawMaskedBorder(REDRAW_ALL);
1027 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1028 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1030 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1032 SetScreenStates_AfterFadingOut();
1035 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1037 static struct TitleFadingInfo fading_leave_stored;
1040 fading_leave_stored = fading_leave;
1042 fading = fading_leave_stored;
1045 void FadeSetEnterMenu(void)
1047 fading = menu.enter_menu;
1049 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1052 void FadeSetLeaveMenu(void)
1054 fading = menu.leave_menu;
1056 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1059 void FadeSetEnterScreen(void)
1061 fading = menu.enter_screen[game_status];
1063 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1066 void FadeSetNextScreen(void)
1068 fading = menu.next_screen[game_status];
1070 // (do not overwrite fade mode set by FadeSetEnterScreen)
1071 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1074 void FadeSetLeaveScreen(void)
1076 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1079 void FadeSetFromType(int type)
1081 if (type & TYPE_ENTER_SCREEN)
1082 FadeSetEnterScreen();
1083 else if (type & TYPE_ENTER)
1085 else if (type & TYPE_LEAVE)
1089 void FadeSetDisabled(void)
1091 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1093 fading = fading_none;
1096 void FadeSkipNextFadeIn(void)
1098 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1101 void FadeSkipNextFadeOut(void)
1103 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1106 static int getGlobalGameStatus(int status)
1108 return (status == GAME_MODE_PSEUDO_TYPENAME ? GAME_MODE_MAIN :
1109 status == GAME_MODE_SCOREINFO ? GAME_MODE_SCORES :
1113 int getImageFromGraphicOrDefault(int graphic, int default_graphic)
1115 if (graphic == IMG_UNDEFINED)
1116 return IMG_UNDEFINED;
1118 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1120 return (graphic_info[graphic].bitmap != NULL || redefined ?
1121 graphic : default_graphic);
1124 static int getBackgroundImage(int graphic)
1126 return getImageFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1129 static int getGlobalBorderImage(int graphic)
1131 return getImageFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1134 Bitmap *getGlobalBorderBitmapFromStatus(int status_raw)
1136 int status = getGlobalGameStatus(status_raw);
1138 (status == GAME_MODE_MAIN ? IMG_GLOBAL_BORDER_MAIN :
1139 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1140 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1141 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1143 int graphic_final = getGlobalBorderImage(graphic);
1145 return graphic_info[graphic_final].bitmap;
1148 void SetBackgroundImage(int graphic, int redraw_mask)
1150 struct GraphicInfo *g = &graphic_info[graphic];
1151 struct GraphicInfo g_undefined = { 0 };
1153 if (graphic == IMG_UNDEFINED)
1156 // always use original size bitmap for backgrounds, if existing
1157 Bitmap *bitmap = (g->bitmaps != NULL &&
1158 g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] != NULL ?
1159 g->bitmaps[IMG_BITMAP_PTR_ORIGINAL] : g->bitmap);
1161 // remove every mask before setting mask for window, and
1162 // remove window area mask before setting mask for main or door area
1163 int remove_mask = (redraw_mask == REDRAW_ALL ? 0xffff : REDRAW_ALL);
1165 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
1166 SetBackgroundBitmap(NULL, remove_mask, 0, 0, 0, 0); // !!! FIX THIS !!!
1167 SetBackgroundBitmap(bitmap, redraw_mask,
1169 g->width, g->height);
1172 void SetWindowBackgroundImageIfDefined(int graphic)
1174 if (graphic_info[graphic].bitmap)
1175 SetBackgroundImage(graphic, REDRAW_ALL);
1178 void SetMainBackgroundImageIfDefined(int graphic)
1180 if (graphic_info[graphic].bitmap)
1181 SetBackgroundImage(graphic, REDRAW_FIELD);
1184 void SetDoorBackgroundImageIfDefined(int graphic)
1186 if (graphic_info[graphic].bitmap)
1187 SetBackgroundImage(graphic, REDRAW_DOOR_1);
1190 void SetWindowBackgroundImage(int graphic)
1192 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_ALL);
1195 void SetMainBackgroundImage(int graphic)
1197 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_FIELD);
1200 void SetDoorBackgroundImage(int graphic)
1202 SetBackgroundImage(getBackgroundImage(graphic), REDRAW_DOOR_1);
1205 void SetPanelBackground(void)
1207 SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
1210 void DrawBackground(int x, int y, int width, int height)
1212 // "drawto" might still point to playfield buffer here (hall of fame)
1213 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1215 if (IN_GFX_FIELD_FULL(x, y))
1216 redraw_mask |= REDRAW_FIELD;
1217 else if (IN_GFX_DOOR_1(x, y))
1218 redraw_mask |= REDRAW_DOOR_1;
1219 else if (IN_GFX_DOOR_2(x, y))
1220 redraw_mask |= REDRAW_DOOR_2;
1221 else if (IN_GFX_DOOR_3(x, y))
1222 redraw_mask |= REDRAW_DOOR_3;
1225 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1227 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1229 if (font->bitmap == NULL)
1232 DrawBackground(x, y, width, height);
1235 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1237 struct GraphicInfo *g = &graphic_info[graphic];
1239 if (g->bitmap == NULL)
1242 DrawBackground(x, y, width, height);
1245 static int game_status_last = -1;
1246 static Bitmap *global_border_bitmap_last = NULL;
1247 static Bitmap *global_border_bitmap = NULL;
1248 static int real_sx_last = -1, real_sy_last = -1;
1249 static int full_sxsize_last = -1, full_sysize_last = -1;
1250 static int dx_last = -1, dy_last = -1;
1251 static int dxsize_last = -1, dysize_last = -1;
1252 static int vx_last = -1, vy_last = -1;
1253 static int vxsize_last = -1, vysize_last = -1;
1254 static int ex_last = -1, ey_last = -1;
1255 static int exsize_last = -1, eysize_last = -1;
1257 boolean CheckIfGlobalBorderHasChanged(void)
1259 // if game status has not changed, global border has not changed either
1260 if (game_status == game_status_last)
1263 // determine and store new global border bitmap for current game status
1264 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1266 return (global_border_bitmap_last != global_border_bitmap);
1269 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1271 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1272 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1274 // if game status has not changed, nothing has to be redrawn
1275 if (game_status == game_status_last)
1278 // redraw if last screen was title screen
1279 if (game_status_last == GAME_MODE_TITLE)
1282 // redraw if global screen border has changed
1283 if (CheckIfGlobalBorderHasChanged())
1286 // redraw if position or size of playfield area has changed
1287 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1288 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1291 // redraw if position or size of door area has changed
1292 if (dx_last != DX || dy_last != DY ||
1293 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1296 // redraw if position or size of tape area has changed
1297 if (vx_last != VX || vy_last != VY ||
1298 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1301 // redraw if position or size of editor area has changed
1302 if (ex_last != EX || ey_last != EY ||
1303 exsize_last != EXSIZE || eysize_last != EYSIZE)
1310 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1313 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1315 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1318 void RedrawGlobalBorder(void)
1320 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1322 RedrawGlobalBorderFromBitmap(bitmap);
1324 redraw_mask = REDRAW_ALL;
1327 static void RedrawGlobalBorderIfNeeded(void)
1329 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1330 if (game_status == game_status_last)
1334 // copy current draw buffer to later copy back areas that have not changed
1335 if (game_status_last != GAME_MODE_TITLE)
1336 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1338 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1339 if (CheckIfGlobalBorderRedrawIsNeeded())
1341 // determine and store new global border bitmap for current game status
1342 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1345 // redraw global screen border (or clear, if defined to be empty)
1346 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1348 if (game_status == GAME_MODE_EDITOR)
1349 DrawSpecialEditorDoor();
1351 // copy previous playfield and door areas, if they are defined on both
1352 // previous and current screen and if they still have the same size
1354 if (real_sx_last != -1 && real_sy_last != -1 &&
1355 REAL_SX != -1 && REAL_SY != -1 &&
1356 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1357 BlitBitmap(bitmap_db_store_1, backbuffer,
1358 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1361 if (dx_last != -1 && dy_last != -1 &&
1362 DX != -1 && DY != -1 &&
1363 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1364 BlitBitmap(bitmap_db_store_1, backbuffer,
1365 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1367 if (game_status != GAME_MODE_EDITOR)
1369 if (vx_last != -1 && vy_last != -1 &&
1370 VX != -1 && VY != -1 &&
1371 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1372 BlitBitmap(bitmap_db_store_1, backbuffer,
1373 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1377 if (ex_last != -1 && ey_last != -1 &&
1378 EX != -1 && EY != -1 &&
1379 exsize_last == EXSIZE && eysize_last == EYSIZE)
1380 BlitBitmap(bitmap_db_store_1, backbuffer,
1381 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1384 redraw_mask = REDRAW_ALL;
1387 game_status_last = game_status;
1389 global_border_bitmap_last = global_border_bitmap;
1391 real_sx_last = REAL_SX;
1392 real_sy_last = REAL_SY;
1393 full_sxsize_last = FULL_SXSIZE;
1394 full_sysize_last = FULL_SYSIZE;
1397 dxsize_last = DXSIZE;
1398 dysize_last = DYSIZE;
1401 vxsize_last = VXSIZE;
1402 vysize_last = VYSIZE;
1405 exsize_last = EXSIZE;
1406 eysize_last = EYSIZE;
1409 void ClearField(void)
1411 RedrawGlobalBorderIfNeeded();
1413 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1414 // (when entering hall of fame after playing)
1415 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1417 // !!! maybe this should be done before clearing the background !!!
1418 if (game_status == GAME_MODE_PLAYING)
1420 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1421 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1425 SetDrawtoField(DRAW_TO_BACKBUFFER);
1429 void MarkTileDirty(int x, int y)
1431 redraw_mask |= REDRAW_FIELD;
1434 void SetBorderElement(void)
1438 BorderElement = EL_EMPTY;
1440 // only the R'n'D game engine may use an additional steelwall border
1441 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1444 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1446 for (x = 0; x < lev_fieldx; x++)
1448 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1449 BorderElement = EL_STEELWALL;
1451 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1457 void FloodFillLevelExt(int start_x, int start_y, int fill_element,
1458 int max_array_fieldx, int max_array_fieldy,
1459 short field[max_array_fieldx][max_array_fieldy],
1460 int max_fieldx, int max_fieldy)
1462 static struct XY stack_buffer[MAX_LEV_FIELDX * MAX_LEV_FIELDY];
1463 struct XY *check = xy_topdown;
1464 int old_element = field[start_x][start_y];
1467 // do nothing if start field already has the desired content
1468 if (old_element == fill_element)
1471 stack_buffer[stack_pos++] = (struct XY){ start_x, start_y };
1473 while (stack_pos > 0)
1475 struct XY current = stack_buffer[--stack_pos];
1478 field[current.x][current.y] = fill_element;
1480 for (i = 0; i < 4; i++)
1482 int x = current.x + check[i].x;
1483 int y = current.y + check[i].y;
1485 // check for stack buffer overflow (should not happen)
1486 if (stack_pos >= MAX_LEV_FIELDX * MAX_LEV_FIELDY)
1487 Fail("Stack buffer overflow in 'FloodFillLevelExt()'. Please debug.");
1489 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1490 stack_buffer[stack_pos++] = (struct XY){ x, y };
1495 void FloodFillLevel(int from_x, int from_y, int fill_element,
1496 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1497 int max_fieldx, int max_fieldy)
1499 FloodFillLevelExt(from_x, from_y, fill_element,
1500 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1501 max_fieldx, max_fieldy);
1504 void SetRandomAnimationValue(int x, int y)
1506 gfx.anim_random_frame = GfxRandom[x][y];
1509 int getGraphicAnimationFrame(int graphic, int sync_frame)
1511 // animation synchronized with global frame counter, not move position
1512 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1513 sync_frame = FrameCounter;
1514 else if (graphic_info[graphic].anim_global_anim_sync)
1515 sync_frame = getGlobalAnimSyncFrame();
1517 return getAnimationFrame(graphic_info[graphic].anim_frames,
1518 graphic_info[graphic].anim_delay,
1519 graphic_info[graphic].anim_mode,
1520 graphic_info[graphic].anim_start_frame,
1524 int getGraphicAnimationFrameXY(int graphic, int lx, int ly)
1526 if (graphic_info[graphic].anim_mode & ANIM_TILED)
1528 struct GraphicInfo *g = &graphic_info[graphic];
1529 int xsize = MAX(1, g->anim_frames_per_line);
1530 int ysize = MAX(1, g->anim_frames / xsize);
1531 int xoffset = g->anim_start_frame % xsize;
1532 int yoffset = g->anim_start_frame % ysize;
1533 // may be needed if screen field is significantly larger than playfield
1534 int x = (lx + xoffset + SCR_FIELDX * xsize) % xsize;
1535 int y = (ly + yoffset + SCR_FIELDY * ysize) % ysize;
1536 int sync_frame = y * xsize + x;
1538 return sync_frame % g->anim_frames;
1540 else if (graphic_info[graphic].anim_mode & ANIM_RANDOM_STATIC)
1542 struct GraphicInfo *g = &graphic_info[graphic];
1543 // may be needed if screen field is significantly larger than playfield
1544 int x = (lx + SCR_FIELDX * lev_fieldx) % lev_fieldx;
1545 int y = (ly + SCR_FIELDY * lev_fieldy) % lev_fieldy;
1546 int sync_frame = GfxRandomStatic[x][y];
1548 return sync_frame % g->anim_frames;
1552 int sync_frame = (IN_LEV_FIELD(lx, ly) ? GfxFrame[lx][ly] : -1);
1554 return getGraphicAnimationFrame(graphic, sync_frame);
1558 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1560 struct GraphicInfo *g = &graphic_info[graphic];
1561 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1563 if (tilesize == gfx.standard_tile_size)
1564 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1565 else if (tilesize == game.tile_size)
1566 *bitmap = g->bitmaps[IMG_BITMAP_PTR_GAME];
1568 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1571 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1572 boolean get_backside)
1574 struct GraphicInfo *g = &graphic_info[graphic];
1575 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1576 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1578 if (g->offset_y == 0) // frames are ordered horizontally
1580 int max_width = g->anim_frames_per_line * g->width;
1581 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1583 *x = pos % max_width;
1584 *y = src_y % g->height + pos / max_width * g->height;
1586 else if (g->offset_x == 0) // frames are ordered vertically
1588 int max_height = g->anim_frames_per_line * g->height;
1589 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1591 *x = src_x % g->width + pos / max_height * g->width;
1592 *y = pos % max_height;
1594 else // frames are ordered diagonally
1596 *x = src_x + frame * g->offset_x;
1597 *y = src_y + frame * g->offset_y;
1601 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1602 Bitmap **bitmap, int *x, int *y,
1603 boolean get_backside)
1605 struct GraphicInfo *g = &graphic_info[graphic];
1607 // if no graphics defined at all, use fallback graphics
1608 if (g->bitmaps == NULL)
1609 *g = graphic_info[IMG_CHAR_EXCLAM];
1611 // if no in-game graphics defined, always use standard graphic size
1612 if (g->bitmaps[IMG_BITMAP_PTR_GAME] == NULL)
1613 tilesize = TILESIZE;
1615 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1616 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1618 *x = *x * tilesize / g->tile_size;
1619 *y = *y * tilesize / g->tile_size;
1622 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1623 Bitmap **bitmap, int *x, int *y)
1625 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1628 void getFixedGraphicSource(int graphic, int frame,
1629 Bitmap **bitmap, int *x, int *y)
1631 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1634 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1636 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1639 void getGlobalAnimGraphicSource(int graphic, int frame,
1640 Bitmap **bitmap, int *x, int *y)
1642 struct GraphicInfo *g = &graphic_info[graphic];
1644 // if no graphics defined at all, use fallback graphics
1645 if (g->bitmaps == NULL)
1646 *g = graphic_info[IMG_CHAR_EXCLAM];
1648 // use original size graphics, if existing, else use standard size graphics
1649 if (g->bitmaps[IMG_BITMAP_PTR_ORIGINAL])
1650 *bitmap = g->bitmaps[IMG_BITMAP_PTR_ORIGINAL];
1652 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1654 getGraphicSourceXY(graphic, frame, x, y, FALSE);
1657 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1658 int *x, int *y, boolean get_backside)
1660 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1664 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1666 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1669 void DrawGraphic(int x, int y, int graphic, int frame)
1672 if (!IN_SCR_FIELD(x, y))
1674 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1675 Debug("draw:DrawGraphic", "This should never happen!");
1681 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1684 MarkTileDirty(x, y);
1687 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1690 if (!IN_SCR_FIELD(x, y))
1692 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1694 Debug("draw:DrawFixedGraphic", "This should never happen!");
1700 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1702 MarkTileDirty(x, y);
1705 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1711 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1713 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1716 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1722 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1723 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1726 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1729 if (!IN_SCR_FIELD(x, y))
1731 Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1733 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1739 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1742 MarkTileDirty(x, y);
1745 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1748 if (!IN_SCR_FIELD(x, y))
1750 Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1752 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1758 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1760 MarkTileDirty(x, y);
1763 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1769 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1771 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1775 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1776 int graphic, int frame)
1781 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1783 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1787 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1789 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1791 MarkTileDirty(x / tilesize, y / tilesize);
1794 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1797 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1798 graphic, frame, tilesize);
1799 MarkTileDirty(x / tilesize, y / tilesize);
1802 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1808 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1809 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1812 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1813 int frame, int tilesize)
1818 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1819 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1822 void DrawMiniGraphic(int x, int y, int graphic)
1824 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1825 MarkTileDirty(x / 2, y / 2);
1828 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1833 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1834 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1837 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1838 int graphic, int frame,
1839 int cut_mode, int mask_mode)
1844 int width = TILEX, height = TILEY;
1847 if (dx || dy) // shifted graphic
1849 if (x < BX1) // object enters playfield from the left
1856 else if (x > BX2) // object enters playfield from the right
1862 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1868 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1870 else if (dx) // general horizontal movement
1871 MarkTileDirty(x + SIGN(dx), y);
1873 if (y < BY1) // object enters playfield from the top
1875 if (cut_mode == CUT_BELOW) // object completely above top border
1883 else if (y > BY2) // object enters playfield from the bottom
1889 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1895 else if (dy > 0 && cut_mode == CUT_ABOVE)
1897 if (y == BY2) // object completely above bottom border
1903 MarkTileDirty(x, y + 1);
1904 } // object leaves playfield to the bottom
1905 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1907 else if (dy) // general vertical movement
1908 MarkTileDirty(x, y + SIGN(dy));
1912 if (!IN_SCR_FIELD(x, y))
1914 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1916 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1922 width = width * TILESIZE_VAR / TILESIZE;
1923 height = height * TILESIZE_VAR / TILESIZE;
1924 cx = cx * TILESIZE_VAR / TILESIZE;
1925 cy = cy * TILESIZE_VAR / TILESIZE;
1926 dx = dx * TILESIZE_VAR / TILESIZE;
1927 dy = dy * TILESIZE_VAR / TILESIZE;
1929 if (width > 0 && height > 0)
1931 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1936 dst_x = FX + x * TILEX_VAR + dx;
1937 dst_y = FY + y * TILEY_VAR + dy;
1939 if (mask_mode == USE_MASKING)
1940 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1943 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1946 MarkTileDirty(x, y);
1950 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1951 int graphic, int frame,
1952 int cut_mode, int mask_mode)
1957 int width = TILEX_VAR, height = TILEY_VAR;
1960 int x2 = x + SIGN(dx);
1961 int y2 = y + SIGN(dy);
1963 // movement with two-tile animations must be sync'ed with movement position,
1964 // not with current GfxFrame (which can be higher when using slow movement)
1965 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1966 int anim_frames = graphic_info[graphic].anim_frames;
1968 // (we also need anim_delay here for movement animations with less frames)
1969 int anim_delay = graphic_info[graphic].anim_delay;
1970 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1972 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1973 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1975 // re-calculate animation frame for two-tile movement animation
1976 frame = getGraphicAnimationFrame(graphic, sync_frame);
1978 // check if movement start graphic inside screen area and should be drawn
1979 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1981 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1983 dst_x = FX + x1 * TILEX_VAR;
1984 dst_y = FY + y1 * TILEY_VAR;
1986 if (mask_mode == USE_MASKING)
1987 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1990 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1993 MarkTileDirty(x1, y1);
1996 // check if movement end graphic inside screen area and should be drawn
1997 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1999 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
2001 dst_x = FX + x2 * TILEX_VAR;
2002 dst_y = FY + y2 * TILEY_VAR;
2004 if (mask_mode == USE_MASKING)
2005 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
2008 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
2011 MarkTileDirty(x2, y2);
2015 static void DrawGraphicShifted(int x, int y, int dx, int dy,
2016 int graphic, int frame,
2017 int cut_mode, int mask_mode)
2021 DrawGraphic(x, y, graphic, frame);
2026 if (graphic_info[graphic].double_movement) // EM style movement images
2027 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
2029 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
2032 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
2033 int graphic, int frame, int cut_mode)
2035 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
2038 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
2039 int cut_mode, int mask_mode)
2041 int lx = LEVELX(x), ly = LEVELY(y);
2045 if (IN_LEV_FIELD(lx, ly))
2047 if (element == EL_EMPTY)
2048 element = GfxElementEmpty[lx][ly];
2050 SetRandomAnimationValue(lx, ly);
2052 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2053 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2055 // do not use double (EM style) movement graphic when not moving
2056 if (graphic_info[graphic].double_movement && !dx && !dy)
2058 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2059 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2062 if (game.use_masked_elements && (dx || dy))
2063 mask_mode = USE_MASKING;
2065 else // border element
2067 graphic = el2img(element);
2068 frame = getGraphicAnimationFrameXY(graphic, lx, ly);
2071 if (element == EL_EXPANDABLE_WALL)
2073 boolean left_stopped = FALSE, right_stopped = FALSE;
2075 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
2076 left_stopped = TRUE;
2077 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
2078 right_stopped = TRUE;
2080 if (left_stopped && right_stopped)
2082 else if (left_stopped)
2084 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2085 frame = graphic_info[graphic].anim_frames - 1;
2087 else if (right_stopped)
2089 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2090 frame = graphic_info[graphic].anim_frames - 1;
2095 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2096 else if (mask_mode == USE_MASKING)
2097 DrawGraphicThruMask(x, y, graphic, frame);
2099 DrawGraphic(x, y, graphic, frame);
2102 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2103 int cut_mode, int mask_mode)
2105 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2106 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2107 cut_mode, mask_mode);
2110 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2113 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2116 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2119 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2122 void DrawLevelElementThruMask(int x, int y, int element)
2124 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2127 void DrawLevelFieldThruMask(int x, int y)
2129 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2132 // !!! implementation of quicksand is totally broken !!!
2133 #define IS_CRUMBLED_TILE(x, y, e) \
2134 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2135 !IS_MOVING(x, y) || \
2136 (e) == EL_QUICKSAND_EMPTYING || \
2137 (e) == EL_QUICKSAND_FAST_EMPTYING))
2139 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2144 int width, height, cx, cy;
2145 int sx = SCREENX(x), sy = SCREENY(y);
2146 int crumbled_border_size = graphic_info[graphic].border_size;
2147 int crumbled_tile_size = graphic_info[graphic].tile_size;
2148 int crumbled_border_size_var =
2149 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2152 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2154 for (i = 1; i < 4; i++)
2156 int dxx = (i & 1 ? dx : 0);
2157 int dyy = (i & 2 ? dy : 0);
2160 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2163 // check if neighbour field is of same crumble type
2164 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2165 graphic_info[graphic].class ==
2166 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2168 // return if check prevents inner corner
2169 if (same == (dxx == dx && dyy == dy))
2173 // if we reach this point, we have an inner corner
2175 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2177 width = crumbled_border_size_var;
2178 height = crumbled_border_size_var;
2179 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2180 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2182 if (game.use_masked_elements)
2184 int graphic0 = el2img(EL_EMPTY);
2185 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2186 Bitmap *src_bitmap0;
2189 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2191 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2193 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2195 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2197 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2200 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2202 FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2205 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2210 int width, height, bx, by, cx, cy;
2211 int sx = SCREENX(x), sy = SCREENY(y);
2212 int crumbled_border_size = graphic_info[graphic].border_size;
2213 int crumbled_tile_size = graphic_info[graphic].tile_size;
2214 int crumbled_border_size_var =
2215 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2216 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2219 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2221 // only needed when using masked elements
2222 int graphic0 = el2img(EL_EMPTY);
2223 int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
2224 Bitmap *src_bitmap0;
2227 if (game.use_masked_elements)
2228 getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
2230 // draw simple, sloppy, non-corner-accurate crumbled border
2232 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2233 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2234 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2235 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2237 if (game.use_masked_elements)
2239 BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
2241 FX + sx * TILEX_VAR + cx,
2242 FY + sy * TILEY_VAR + cy);
2244 BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2246 FX + sx * TILEX_VAR + cx,
2247 FY + sy * TILEY_VAR + cy);
2250 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2252 FX + sx * TILEX_VAR + cx,
2253 FY + sy * TILEY_VAR + cy);
2255 // (remaining middle border part must be at least as big as corner part)
2256 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2257 crumbled_border_size_var >= TILESIZE_VAR / 3)
2260 // correct corners of crumbled border, if needed
2262 for (i = -1; i <= 1; i += 2)
2264 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2265 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2266 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2269 // check if neighbour field is of same crumble type
2270 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2271 graphic_info[graphic].class ==
2272 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2274 // no crumbled corner, but continued crumbled border
2276 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2277 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2278 int b1 = (i == 1 ? crumbled_border_size_var :
2279 TILESIZE_VAR - 2 * crumbled_border_size_var);
2281 width = crumbled_border_size_var;
2282 height = crumbled_border_size_var;
2284 if (dir == 1 || dir == 2)
2299 if (game.use_masked_elements)
2301 BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
2303 FX + sx * TILEX_VAR + cx,
2304 FY + sy * TILEY_VAR + cy);
2306 BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
2308 FX + sx * TILEX_VAR + cx,
2309 FY + sy * TILEY_VAR + cy);
2312 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2314 FX + sx * TILEX_VAR + cx,
2315 FY + sy * TILEY_VAR + cy);
2320 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2322 int sx = SCREENX(x), sy = SCREENY(y);
2325 struct XY *xy = xy_topdown;
2327 if (!IN_LEV_FIELD(x, y))
2330 element = TILE_GFX_ELEMENT(x, y);
2332 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2334 if (!IN_SCR_FIELD(sx, sy))
2337 // crumble field borders towards direct neighbour fields
2338 for (i = 0; i < 4; i++)
2340 int xx = x + xy[i].x;
2341 int yy = y + xy[i].y;
2343 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2346 // check if neighbour field is of same crumble type
2347 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2348 graphic_info[graphic].class ==
2349 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2352 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2355 // crumble inner field corners towards corner neighbour fields
2356 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2357 graphic_info[graphic].anim_frames == 2)
2359 for (i = 0; i < 4; i++)
2361 int dx = (i & 1 ? +1 : -1);
2362 int dy = (i & 2 ? +1 : -1);
2364 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2368 MarkTileDirty(sx, sy);
2370 else // center field is not crumbled -- crumble neighbour fields
2372 // crumble field borders of direct neighbour fields
2373 for (i = 0; i < 4; i++)
2375 int xx = x + xy[i].x;
2376 int yy = y + xy[i].y;
2377 int sxx = sx + xy[i].x;
2378 int syy = sy + xy[i].y;
2380 if (!IN_LEV_FIELD(xx, yy) ||
2381 !IN_SCR_FIELD(sxx, syy))
2384 // do not crumble fields that are being digged or snapped
2385 if (Tile[xx][yy] == EL_EMPTY ||
2386 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2389 element = TILE_GFX_ELEMENT(xx, yy);
2391 if (!IS_CRUMBLED_TILE(xx, yy, element))
2394 graphic = el_act2crm(element, ACTION_DEFAULT);
2396 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2398 MarkTileDirty(sxx, syy);
2401 // crumble inner field corners of corner neighbour fields
2402 for (i = 0; i < 4; i++)
2404 int dx = (i & 1 ? +1 : -1);
2405 int dy = (i & 2 ? +1 : -1);
2411 if (!IN_LEV_FIELD(xx, yy) ||
2412 !IN_SCR_FIELD(sxx, syy))
2415 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2418 element = TILE_GFX_ELEMENT(xx, yy);
2420 if (!IS_CRUMBLED_TILE(xx, yy, element))
2423 graphic = el_act2crm(element, ACTION_DEFAULT);
2425 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2426 graphic_info[graphic].anim_frames == 2)
2427 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2429 MarkTileDirty(sxx, syy);
2434 void DrawLevelFieldCrumbled(int x, int y)
2438 if (!IN_LEV_FIELD(x, y))
2441 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2442 GfxElement[x][y] != EL_UNDEFINED &&
2443 GFX_CRUMBLED(GfxElement[x][y]))
2445 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2450 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2452 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2455 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2458 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2459 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2460 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2461 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2462 int sx = SCREENX(x), sy = SCREENY(y);
2464 DrawScreenGraphic(sx, sy, graphic1, frame1);
2465 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2468 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2470 int sx = SCREENX(x), sy = SCREENY(y);
2471 struct XY *xy = xy_topdown;
2474 // crumble direct neighbour fields (required for field borders)
2475 for (i = 0; i < 4; i++)
2477 int xx = x + xy[i].x;
2478 int yy = y + xy[i].y;
2479 int sxx = sx + xy[i].x;
2480 int syy = sy + xy[i].y;
2482 if (!IN_LEV_FIELD(xx, yy) ||
2483 !IN_SCR_FIELD(sxx, syy) ||
2484 !GFX_CRUMBLED(Tile[xx][yy]) ||
2488 DrawLevelField(xx, yy);
2491 // crumble corner neighbour fields (required for inner field corners)
2492 for (i = 0; i < 4; i++)
2494 int dx = (i & 1 ? +1 : -1);
2495 int dy = (i & 2 ? +1 : -1);
2501 if (!IN_LEV_FIELD(xx, yy) ||
2502 !IN_SCR_FIELD(sxx, syy) ||
2503 !GFX_CRUMBLED(Tile[xx][yy]) ||
2507 int element = TILE_GFX_ELEMENT(xx, yy);
2508 int graphic = el_act2crm(element, ACTION_DEFAULT);
2510 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2511 graphic_info[graphic].anim_frames == 2)
2512 DrawLevelField(xx, yy);
2516 static int getBorderElement(int x, int y)
2520 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2521 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2522 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2523 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2524 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2525 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2526 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2528 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2529 int steel_position = (x == -1 && y == -1 ? 0 :
2530 x == lev_fieldx && y == -1 ? 1 :
2531 x == -1 && y == lev_fieldy ? 2 :
2532 x == lev_fieldx && y == lev_fieldy ? 3 :
2533 x == -1 || x == lev_fieldx ? 4 :
2534 y == -1 || y == lev_fieldy ? 5 : 6);
2536 return border[steel_position][steel_type];
2539 void DrawScreenGraphic(int x, int y, int graphic, int frame)
2541 if (game.use_masked_elements)
2543 if (graphic != el2img(EL_EMPTY))
2544 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2546 DrawGraphicThruMask(x, y, graphic, frame);
2550 DrawGraphic(x, y, graphic, frame);
2554 void DrawLevelGraphic(int x, int y, int graphic, int frame)
2556 DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2559 void DrawScreenElement(int x, int y, int element)
2561 int mask_mode = NO_MASKING;
2563 if (game.use_masked_elements)
2565 int lx = LEVELX(x), ly = LEVELY(y);
2567 if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
2569 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
2571 mask_mode = USE_MASKING;
2575 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
2576 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2579 void DrawLevelElement(int x, int y, int element)
2581 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2582 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2585 void DrawScreenField(int x, int y)
2587 int lx = LEVELX(x), ly = LEVELY(y);
2588 int element, content;
2590 if (!IN_LEV_FIELD(lx, ly))
2592 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2595 element = getBorderElement(lx, ly);
2597 DrawScreenElement(x, y, element);
2602 element = Tile[lx][ly];
2603 content = Store[lx][ly];
2605 if (IS_MOVING(lx, ly))
2607 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2608 boolean cut_mode = NO_CUTTING;
2610 if (element == EL_QUICKSAND_EMPTYING ||
2611 element == EL_QUICKSAND_FAST_EMPTYING ||
2612 element == EL_MAGIC_WALL_EMPTYING ||
2613 element == EL_BD_MAGIC_WALL_EMPTYING ||
2614 element == EL_DC_MAGIC_WALL_EMPTYING ||
2615 element == EL_AMOEBA_DROPPING)
2616 cut_mode = CUT_ABOVE;
2617 else if (element == EL_QUICKSAND_FILLING ||
2618 element == EL_QUICKSAND_FAST_FILLING ||
2619 element == EL_MAGIC_WALL_FILLING ||
2620 element == EL_BD_MAGIC_WALL_FILLING ||
2621 element == EL_DC_MAGIC_WALL_FILLING)
2622 cut_mode = CUT_BELOW;
2624 if (cut_mode == CUT_ABOVE)
2625 DrawScreenElement(x, y, element);
2627 DrawScreenElement(x, y, EL_EMPTY);
2629 if (cut_mode != CUT_BELOW && game.use_masked_elements)
2631 int dir = MovDir[lx][ly];
2632 int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2633 int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2635 if (IN_SCR_FIELD(newx, newy))
2636 DrawScreenElement(newx, newy, EL_EMPTY);
2640 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2641 else if (cut_mode == NO_CUTTING)
2642 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2645 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2647 if (cut_mode == CUT_BELOW &&
2648 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2649 DrawLevelElement(lx, ly + 1, element);
2652 if (content == EL_ACID)
2654 int dir = MovDir[lx][ly];
2655 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2656 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2658 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2660 // prevent target field from being drawn again (but without masking)
2661 // (this would happen if target field is scanned after moving element)
2662 Stop[newlx][newly] = TRUE;
2665 else if (IS_BLOCKED(lx, ly))
2670 boolean cut_mode = NO_CUTTING;
2671 int element_old, content_old;
2673 Blocked2Moving(lx, ly, &oldx, &oldy);
2676 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2677 MovDir[oldx][oldy] == MV_RIGHT);
2679 element_old = Tile[oldx][oldy];
2680 content_old = Store[oldx][oldy];
2682 if (element_old == EL_QUICKSAND_EMPTYING ||
2683 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2684 element_old == EL_MAGIC_WALL_EMPTYING ||
2685 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2686 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2687 element_old == EL_AMOEBA_DROPPING)
2688 cut_mode = CUT_ABOVE;
2690 DrawScreenElement(x, y, EL_EMPTY);
2693 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2695 else if (cut_mode == NO_CUTTING)
2696 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2699 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2702 else if (IS_DRAWABLE(element))
2703 DrawScreenElement(x, y, element);
2705 DrawScreenElement(x, y, EL_EMPTY);
2708 void DrawLevelField(int x, int y)
2710 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2711 DrawScreenField(SCREENX(x), SCREENY(y));
2712 else if (IS_MOVING(x, y))
2716 Moving2Blocked(x, y, &newx, &newy);
2717 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2718 DrawScreenField(SCREENX(newx), SCREENY(newy));
2720 else if (IS_BLOCKED(x, y))
2724 Blocked2Moving(x, y, &oldx, &oldy);
2725 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2726 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2730 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2731 int (*el2img_function)(int), boolean masked,
2732 int element_bits_draw)
2734 int element_base = map_mm_wall_element(element);
2735 int element_bits = (IS_DF_WALL(element) ?
2736 element - EL_DF_WALL_START :
2737 IS_MM_WALL(element) ?
2738 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2739 int graphic = el2img_function(element_base);
2740 int tilesize_draw = tilesize / 2;
2745 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2747 for (i = 0; i < 4; i++)
2749 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2750 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2752 if (!(element_bits_draw & (1 << i)))
2755 if (element_bits & (1 << i))
2758 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2759 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2761 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2762 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2767 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2768 tilesize_draw, tilesize_draw);
2773 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2774 boolean masked, int element_bits_draw)
2776 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2777 element, tilesize, el2edimg, masked, element_bits_draw);
2780 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2781 int (*el2img_function)(int))
2783 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2787 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2790 if (IS_MM_WALL(element))
2792 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2793 element, tilesize, el2edimg, masked, 0x000f);
2797 int graphic = el2edimg(element);
2800 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2802 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2806 void DrawSizedElement(int x, int y, int element, int tilesize)
2808 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2811 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2813 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2816 void DrawMiniElement(int x, int y, int element)
2820 graphic = el2edimg(element);
2821 DrawMiniGraphic(x, y, graphic);
2824 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2827 int x = sx + scroll_x, y = sy + scroll_y;
2829 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2830 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2831 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2832 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2834 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2837 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2839 int x = sx + scroll_x, y = sy + scroll_y;
2841 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2842 DrawMiniElement(sx, sy, EL_EMPTY);
2843 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2844 DrawMiniElement(sx, sy, Tile[x][y]);
2846 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2849 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2850 int x, int y, int xsize, int ysize,
2851 int tile_width, int tile_height)
2855 int dst_x = startx + x * tile_width;
2856 int dst_y = starty + y * tile_height;
2857 int width = graphic_info[graphic].width;
2858 int height = graphic_info[graphic].height;
2859 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2860 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2861 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2862 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2863 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2864 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2865 boolean draw_masked = graphic_info[graphic].draw_masked;
2867 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2869 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2871 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2875 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2876 inner_sx + (x - 1) * tile_width % inner_width);
2877 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2878 inner_sy + (y - 1) * tile_height % inner_height);
2881 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2884 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2888 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2889 int x, int y, int xsize, int ysize,
2892 int font_width = getFontWidth(font_nr);
2893 int font_height = getFontHeight(font_nr);
2895 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2896 font_width, font_height);
2899 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2901 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2902 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2903 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2904 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2905 boolean no_delay = (tape.warp_forward);
2906 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2907 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2908 DelayCounter anim_delay = { anim_delay_value };
2909 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2910 int font_width = getFontWidth(font_nr);
2911 int font_height = getFontHeight(font_nr);
2912 int max_xsize = level.envelope[envelope_nr].xsize;
2913 int max_ysize = level.envelope[envelope_nr].ysize;
2914 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2915 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2916 int xend = max_xsize;
2917 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2918 int xstep = (xstart < xend ? 1 : 0);
2919 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2921 int end = MAX(xend - xstart, yend - ystart);
2924 for (i = start; i <= end; i++)
2926 int last_frame = end; // last frame of this "for" loop
2927 int x = xstart + i * xstep;
2928 int y = ystart + i * ystep;
2929 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2930 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2931 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2932 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2935 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2937 BlitScreenToBitmap(backbuffer);
2939 SetDrawtoField(DRAW_TO_BACKBUFFER);
2941 for (yy = 0; yy < ysize; yy++)
2942 for (xx = 0; xx < xsize; xx++)
2943 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2945 DrawTextArea(sx + font_width, sy + font_height,
2946 level.envelope[envelope_nr].text, font_nr, max_xsize,
2947 xsize - 2, ysize - 2, 0, mask_mode,
2948 level.envelope[envelope_nr].autowrap,
2949 level.envelope[envelope_nr].centered, FALSE);
2951 redraw_mask |= REDRAW_FIELD;
2954 SkipUntilDelayReached(&anim_delay, &i, last_frame);
2957 ClearAutoRepeatKeyEvents();
2960 void ShowEnvelope(int envelope_nr)
2962 int element = EL_ENVELOPE_1 + envelope_nr;
2963 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2964 int sound_opening = element_info[element].sound[ACTION_OPENING];
2965 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2966 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2967 boolean no_delay = (tape.warp_forward);
2968 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2969 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2970 int anim_mode = graphic_info[graphic].anim_mode;
2971 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2972 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2973 boolean overlay_enabled = GetOverlayEnabled();
2975 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2977 SetOverlayEnabled(FALSE);
2980 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2982 if (anim_mode == ANIM_DEFAULT)
2983 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2985 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2988 Delay_WithScreenUpdates(wait_delay_value);
2990 WaitForEventToContinue();
2993 SetOverlayEnabled(overlay_enabled);
2995 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2997 if (anim_mode != ANIM_NONE)
2998 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
3000 if (anim_mode == ANIM_DEFAULT)
3001 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
3003 game.envelope_active = FALSE;
3005 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3007 redraw_mask |= REDRAW_FIELD;
3011 void ShowEnvelope_MM(int envelope_nr)
3013 BlitBitmap(backbuffer, bitmap_db_field, REAL_SX, REAL_SY,
3014 FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
3016 ShowEnvelope(envelope_nr);
3018 SetDrawtoField(DRAW_TO_BACKBUFFER);
3020 BlitBitmap(bitmap_db_field, backbuffer, REAL_SX, REAL_SY,
3021 FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
3024 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
3025 int xsize, int ysize)
3027 if (!global.use_envelope_request ||
3028 request.sort_priority <= 0)
3031 if (request.bitmap == NULL ||
3032 xsize > request.xsize ||
3033 ysize > request.ysize)
3035 if (request.bitmap != NULL)
3036 FreeBitmap(request.bitmap);
3038 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
3040 SDL_Surface *surface = request.bitmap->surface;
3042 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
3043 Fail("SDLGetNativeSurface() failed");
3046 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
3048 SDLFreeBitmapTextures(request.bitmap);
3049 SDLCreateBitmapTextures(request.bitmap);
3051 // set envelope request run-time values
3054 request.xsize = xsize;
3055 request.ysize = ysize;
3058 void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage)
3060 if (global.use_envelope_request &&
3061 game.request_active_or_moving &&
3062 request.sort_priority > 0 &&
3063 drawing_target == DRAW_TO_SCREEN &&
3064 drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
3066 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
3067 request.sx, request.sy);
3071 static void setRequestBasePosition(int *x, int *y)
3073 int sx_base, sy_base;
3075 if (request.x != -1)
3076 sx_base = request.x;
3077 else if (request.align == ALIGN_LEFT)
3079 else if (request.align == ALIGN_RIGHT)
3080 sx_base = SX + SXSIZE;
3082 sx_base = SX + SXSIZE / 2;
3084 if (request.y != -1)
3085 sy_base = request.y;
3086 else if (request.valign == VALIGN_TOP)
3088 else if (request.valign == VALIGN_BOTTOM)
3089 sy_base = SY + SYSIZE;
3091 sy_base = SY + SYSIZE / 2;
3097 static void setRequestPositionExt(int *x, int *y, int width, int height,
3098 boolean add_border_size)
3100 int border_size = request.border_size;
3101 int sx_base, sy_base;
3104 setRequestBasePosition(&sx_base, &sy_base);
3106 if (request.align == ALIGN_LEFT)
3108 else if (request.align == ALIGN_RIGHT)
3109 sx = sx_base - width;
3111 sx = sx_base - width / 2;
3113 if (request.valign == VALIGN_TOP)
3115 else if (request.valign == VALIGN_BOTTOM)
3116 sy = sy_base - height;
3118 sy = sy_base - height / 2;
3120 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
3121 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
3123 if (add_border_size)
3133 static void setRequestPosition(int *x, int *y, boolean add_border_size)
3135 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
3138 static void DrawEnvelopeRequest(char *text)
3140 char *text_final = text;
3141 char *text_door_style = NULL;
3142 int graphic = IMG_BACKGROUND_REQUEST;
3143 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
3144 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
3145 int font_nr = FONT_REQUEST;
3146 int font_width = getFontWidth(font_nr);
3147 int font_height = getFontHeight(font_nr);
3148 int border_size = request.border_size;
3149 int line_spacing = request.line_spacing;
3150 int line_height = font_height + line_spacing;
3151 int max_text_width = request.width - 2 * border_size;
3152 int max_text_height = request.height - 2 * border_size;
3153 int line_length = max_text_width / font_width;
3154 int max_lines = max_text_height / line_height;
3155 int text_width = line_length * font_width;
3156 int width = request.width;
3157 int height = request.height;
3158 int tile_size = MAX(request.step_offset, 1);
3159 int x_steps = width / tile_size;
3160 int y_steps = height / tile_size;
3161 int sx_offset = border_size;
3162 int sy_offset = border_size;
3166 if (request.centered)
3167 sx_offset = (request.width - text_width) / 2;
3169 if (request.wrap_single_words && !request.autowrap)
3171 char *src_text_ptr, *dst_text_ptr;
3173 text_door_style = checked_malloc(2 * strlen(text) + 1);
3175 src_text_ptr = text;
3176 dst_text_ptr = text_door_style;
3178 while (*src_text_ptr)
3180 if (*src_text_ptr == ' ' ||
3181 *src_text_ptr == '?' ||
3182 *src_text_ptr == '!')
3183 *dst_text_ptr++ = '\n';
3185 if (*src_text_ptr != ' ')
3186 *dst_text_ptr++ = *src_text_ptr;
3191 *dst_text_ptr = '\0';
3193 text_final = text_door_style;
3196 setRequestPosition(&sx, &sy, FALSE);
3198 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3200 for (y = 0; y < y_steps; y++)
3201 for (x = 0; x < x_steps; x++)
3202 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3203 x, y, x_steps, y_steps,
3204 tile_size, tile_size);
3206 // force DOOR font inside door area
3207 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3209 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3210 line_length, -1, max_lines, line_spacing, mask_mode,
3211 request.autowrap, request.centered, FALSE);
3215 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3216 RedrawGadget(tool_gadget[i]);
3218 // store readily prepared envelope request for later use when animating
3219 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3221 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3223 if (text_door_style)
3224 free(text_door_style);
3227 static void AnimateEnvelopeRequest(int anim_mode, int action)
3229 int graphic = IMG_BACKGROUND_REQUEST;
3230 boolean draw_masked = graphic_info[graphic].draw_masked;
3231 int delay_value_normal = request.step_delay;
3232 int delay_value_fast = delay_value_normal / 2;
3233 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3234 boolean no_delay = (tape.warp_forward);
3235 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3236 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3237 DelayCounter anim_delay = { anim_delay_value };
3239 int tile_size = MAX(request.step_offset, 1);
3240 int max_xsize = request.width / tile_size;
3241 int max_ysize = request.height / tile_size;
3242 int max_xsize_inner = max_xsize - 2;
3243 int max_ysize_inner = max_ysize - 2;
3245 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3246 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3247 int xend = max_xsize_inner;
3248 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3249 int xstep = (xstart < xend ? 1 : 0);
3250 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3252 int end = MAX(xend - xstart, yend - ystart);
3255 if (setup.quick_doors)
3262 for (i = start; i <= end; i++)
3264 int last_frame = end; // last frame of this "for" loop
3265 int x = xstart + i * xstep;
3266 int y = ystart + i * ystep;
3267 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3268 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3269 int xsize_size_left = (xsize - 1) * tile_size;
3270 int ysize_size_top = (ysize - 1) * tile_size;
3271 int max_xsize_pos = (max_xsize - 1) * tile_size;
3272 int max_ysize_pos = (max_ysize - 1) * tile_size;
3273 int width = xsize * tile_size;
3274 int height = ysize * tile_size;
3279 setRequestPosition(&src_x, &src_y, FALSE);
3280 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3282 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3284 for (yy = 0; yy < 2; yy++)
3286 for (xx = 0; xx < 2; xx++)
3288 int src_xx = src_x + xx * max_xsize_pos;
3289 int src_yy = src_y + yy * max_ysize_pos;
3290 int dst_xx = dst_x + xx * xsize_size_left;
3291 int dst_yy = dst_y + yy * ysize_size_top;
3292 int xx_size = (xx ? tile_size : xsize_size_left);
3293 int yy_size = (yy ? tile_size : ysize_size_top);
3296 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3297 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3299 BlitBitmap(bitmap_db_store_2, backbuffer,
3300 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3304 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3306 redraw_mask |= REDRAW_FIELD;
3310 SkipUntilDelayReached(&anim_delay, &i, last_frame);
3313 ClearAutoRepeatKeyEvents();
3316 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3318 int graphic = IMG_BACKGROUND_REQUEST;
3319 int sound_opening = SND_REQUEST_OPENING;
3320 int sound_closing = SND_REQUEST_CLOSING;
3321 int anim_mode_1 = request.anim_mode; // (higher priority)
3322 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3323 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3324 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3325 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3327 if (game_status == GAME_MODE_PLAYING)
3328 BlitScreenToBitmap(backbuffer);
3330 if (game_status == GAME_MODE_PLAYING || action == ACTION_OPENING)
3331 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3333 SetDrawtoField(DRAW_TO_BACKBUFFER);
3335 // SetDrawBackgroundMask(REDRAW_NONE);
3337 if (action == ACTION_OPENING)
3339 if (req_state & REQ_ASK)
3341 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3342 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3343 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3344 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3346 else if (req_state & REQ_CONFIRM)
3348 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3349 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3351 else if (req_state & REQ_PLAYER)
3353 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3354 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3355 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3356 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3359 DrawEnvelopeRequest(text);
3362 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3364 if (action == ACTION_OPENING)
3366 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3368 if (anim_mode == ANIM_DEFAULT)
3369 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3371 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3375 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3377 if (anim_mode != ANIM_NONE)
3378 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3380 if (anim_mode == ANIM_DEFAULT)
3381 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3384 game.envelope_active = FALSE;
3386 if (action == ACTION_CLOSING)
3387 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3389 // SetDrawBackgroundMask(last_draw_background_mask);
3391 redraw_mask |= REDRAW_FIELD;
3395 if (action == ACTION_CLOSING &&
3396 game_status == GAME_MODE_PLAYING &&
3397 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3398 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3401 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3403 if (IS_MM_WALL(element))
3405 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3411 int graphic = el2preimg(element);
3413 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3414 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3419 void DrawLevel(int draw_background_mask)
3423 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3424 SetDrawBackgroundMask(draw_background_mask);
3428 for (x = BX1; x <= BX2; x++)
3429 for (y = BY1; y <= BY2; y++)
3430 DrawScreenField(x, y);
3432 redraw_mask |= REDRAW_FIELD;
3435 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3440 for (x = 0; x < size_x; x++)
3441 for (y = 0; y < size_y; y++)
3442 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3444 redraw_mask |= REDRAW_FIELD;
3447 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3451 for (x = 0; x < size_x; x++)
3452 for (y = 0; y < size_y; y++)
3453 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3455 redraw_mask |= REDRAW_FIELD;
3458 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3460 boolean show_level_border = (BorderElement != EL_EMPTY);
3461 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3462 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3463 int tile_size = preview.tile_size;
3464 int preview_width = preview.xsize * tile_size;
3465 int preview_height = preview.ysize * tile_size;
3466 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3467 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3468 int real_preview_width = real_preview_xsize * tile_size;
3469 int real_preview_height = real_preview_ysize * tile_size;
3470 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3471 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3474 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3477 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3479 dst_x += (preview_width - real_preview_width) / 2;
3480 dst_y += (preview_height - real_preview_height) / 2;
3482 for (x = 0; x < real_preview_xsize; x++)
3484 for (y = 0; y < real_preview_ysize; y++)
3486 int lx = from_x + x + (show_level_border ? -1 : 0);
3487 int ly = from_y + y + (show_level_border ? -1 : 0);
3488 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3489 getBorderElement(lx, ly));
3491 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3492 element, tile_size);
3496 redraw_mask |= REDRAW_FIELD;
3499 #define MICROLABEL_EMPTY 0
3500 #define MICROLABEL_LEVEL_NAME 1
3501 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3502 #define MICROLABEL_LEVEL_AUTHOR 3
3503 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3504 #define MICROLABEL_IMPORTED_FROM 5
3505 #define MICROLABEL_IMPORTED_BY_HEAD 6
3506 #define MICROLABEL_IMPORTED_BY 7
3508 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3510 int max_text_width = SXSIZE;
3511 int font_width = getFontWidth(font_nr);
3513 if (pos->align == ALIGN_CENTER)
3514 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3515 else if (pos->align == ALIGN_RIGHT)
3516 max_text_width = pos->x;
3518 max_text_width = SXSIZE - pos->x;
3520 return max_text_width / font_width;
3523 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3525 char label_text[MAX_OUTPUT_LINESIZE + 1];
3526 int max_len_label_text;
3527 int font_nr = pos->font;
3530 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3533 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3534 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3535 mode == MICROLABEL_IMPORTED_BY_HEAD)
3536 font_nr = pos->font_alt;
3538 max_len_label_text = getMaxTextLength(pos, font_nr);
3540 if (pos->size != -1)
3541 max_len_label_text = pos->size;
3543 for (i = 0; i < max_len_label_text; i++)
3544 label_text[i] = ' ';
3545 label_text[max_len_label_text] = '\0';
3547 if (strlen(label_text) > 0)
3548 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3551 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3552 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3553 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3554 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3555 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3556 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3557 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3558 max_len_label_text);
3559 label_text[max_len_label_text] = '\0';
3561 if (strlen(label_text) > 0)
3562 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3564 redraw_mask |= REDRAW_FIELD;
3567 static void DrawPreviewLevelLabel(int mode)
3569 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3572 static void DrawPreviewLevelInfo(int mode)
3574 if (mode == MICROLABEL_LEVEL_NAME)
3575 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3576 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3577 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3580 static void DrawPreviewLevelExt(boolean restart)
3582 static DelayCounter scroll_delay = { 0 };
3583 static DelayCounter label_delay = { 0 };
3584 static int from_x, from_y, scroll_direction;
3585 static int label_state, label_counter;
3586 boolean show_level_border = (BorderElement != EL_EMPTY);
3587 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3588 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3590 scroll_delay.value = preview.step_delay;
3591 label_delay.value = MICROLEVEL_LABEL_DELAY;
3598 if (preview.anim_mode == ANIM_CENTERED)
3600 if (level_xsize > preview.xsize)
3601 from_x = (level_xsize - preview.xsize) / 2;
3602 if (level_ysize > preview.ysize)
3603 from_y = (level_ysize - preview.ysize) / 2;
3606 from_x += preview.xoffset;
3607 from_y += preview.yoffset;
3609 scroll_direction = MV_RIGHT;
3613 DrawPreviewLevelPlayfield(from_x, from_y);
3614 DrawPreviewLevelLabel(label_state);
3616 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3617 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3619 // initialize delay counters
3620 ResetDelayCounter(&scroll_delay);
3621 ResetDelayCounter(&label_delay);
3623 if (leveldir_current->name)
3625 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3626 char label_text[MAX_OUTPUT_LINESIZE + 1];
3627 int font_nr = pos->font;
3628 int max_len_label_text = getMaxTextLength(pos, font_nr);
3630 if (pos->size != -1)
3631 max_len_label_text = pos->size;
3633 strncpy(label_text, leveldir_current->name, max_len_label_text);
3634 label_text[max_len_label_text] = '\0';
3636 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3637 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3643 // scroll preview level, if needed
3644 if (preview.anim_mode != ANIM_NONE &&
3645 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3646 DelayReached(&scroll_delay))
3648 switch (scroll_direction)
3653 from_x -= preview.step_offset;
3654 from_x = (from_x < 0 ? 0 : from_x);
3657 scroll_direction = MV_UP;
3661 if (from_x < level_xsize - preview.xsize)
3663 from_x += preview.step_offset;
3664 from_x = (from_x > level_xsize - preview.xsize ?
3665 level_xsize - preview.xsize : from_x);
3668 scroll_direction = MV_DOWN;
3674 from_y -= preview.step_offset;
3675 from_y = (from_y < 0 ? 0 : from_y);
3678 scroll_direction = MV_RIGHT;
3682 if (from_y < level_ysize - preview.ysize)
3684 from_y += preview.step_offset;
3685 from_y = (from_y > level_ysize - preview.ysize ?
3686 level_ysize - preview.ysize : from_y);
3689 scroll_direction = MV_LEFT;
3696 DrawPreviewLevelPlayfield(from_x, from_y);
3699 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3700 // redraw micro level label, if needed
3701 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3702 !strEqual(level.author, ANONYMOUS_NAME) &&
3703 !strEqual(level.author, leveldir_current->name) &&
3704 DelayReached(&label_delay))
3706 int max_label_counter = 23;
3708 if (leveldir_current->imported_from != NULL &&
3709 strlen(leveldir_current->imported_from) > 0)
3710 max_label_counter += 14;
3711 if (leveldir_current->imported_by != NULL &&
3712 strlen(leveldir_current->imported_by) > 0)
3713 max_label_counter += 14;
3715 label_counter = (label_counter + 1) % max_label_counter;
3716 label_state = (label_counter >= 0 && label_counter <= 7 ?
3717 MICROLABEL_LEVEL_NAME :
3718 label_counter >= 9 && label_counter <= 12 ?
3719 MICROLABEL_LEVEL_AUTHOR_HEAD :
3720 label_counter >= 14 && label_counter <= 21 ?
3721 MICROLABEL_LEVEL_AUTHOR :
3722 label_counter >= 23 && label_counter <= 26 ?
3723 MICROLABEL_IMPORTED_FROM_HEAD :
3724 label_counter >= 28 && label_counter <= 35 ?
3725 MICROLABEL_IMPORTED_FROM :
3726 label_counter >= 37 && label_counter <= 40 ?
3727 MICROLABEL_IMPORTED_BY_HEAD :
3728 label_counter >= 42 && label_counter <= 49 ?
3729 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3731 if (leveldir_current->imported_from == NULL &&
3732 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3733 label_state == MICROLABEL_IMPORTED_FROM))
3734 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3735 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3737 DrawPreviewLevelLabel(label_state);
3741 void DrawPreviewPlayers(void)
3743 if (game_status != GAME_MODE_MAIN)
3746 // do not draw preview players if level preview redefined, but players aren't
3747 if (preview.redefined && !menu.main.preview_players.redefined)
3750 boolean player_found[MAX_PLAYERS];
3751 int num_players = 0;
3754 for (i = 0; i < MAX_PLAYERS; i++)
3755 player_found[i] = FALSE;
3757 // check which players can be found in the level (simple approach)
3758 for (x = 0; x < lev_fieldx; x++)
3760 for (y = 0; y < lev_fieldy; y++)
3762 int element = level.field[x][y];
3764 if (IS_PLAYER_ELEMENT(element))
3766 int player_nr = GET_PLAYER_NR(element);
3768 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3770 if (!player_found[player_nr])
3773 player_found[player_nr] = TRUE;
3778 struct TextPosInfo *pos = &menu.main.preview_players;
3779 int tile_size = pos->tile_size;
3780 int border_size = pos->border_size;
3781 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3782 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3783 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3784 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3785 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3786 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3787 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3788 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3789 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3790 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3791 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3792 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3794 // clear area in which the players will be drawn
3795 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3796 max_players_width, max_players_height);
3798 if (!network.enabled && !setup.team_mode)
3801 // only draw players if level is suited for team mode
3802 if (num_players < 2)
3805 // draw all players that were found in the level
3806 for (i = 0; i < MAX_PLAYERS; i++)
3808 if (player_found[i])
3810 int graphic = el2img(EL_PLAYER_1 + i);
3812 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3814 xpos += player_xoffset;
3815 ypos += player_yoffset;
3820 void DrawPreviewLevelInitial(void)
3822 DrawPreviewLevelExt(TRUE);
3823 DrawPreviewPlayers();
3826 void DrawPreviewLevelAnimation(void)
3828 DrawPreviewLevelExt(FALSE);
3831 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3832 int border_size, int font_nr)
3834 int graphic = el2img(EL_PLAYER_1 + player_nr);
3835 int font_height = getFontHeight(font_nr);
3836 int player_height = MAX(tile_size, font_height);
3837 int xoffset_text = tile_size + border_size;
3838 int yoffset_text = (player_height - font_height) / 2;
3839 int yoffset_graphic = (player_height - tile_size) / 2;
3840 char *player_name = getNetworkPlayerName(player_nr + 1);
3842 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3844 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3847 static void DrawNetworkPlayersExt(boolean force)
3849 if (game_status != GAME_MODE_MAIN)
3852 if (!network.connected && !force)
3855 // do not draw network players if level preview redefined, but players aren't
3856 if (preview.redefined && !menu.main.network_players.redefined)
3859 int num_players = 0;
3862 for (i = 0; i < MAX_PLAYERS; i++)
3863 if (stored_player[i].connected_network)
3866 struct TextPosInfo *pos = &menu.main.network_players;
3867 int tile_size = pos->tile_size;
3868 int border_size = pos->border_size;
3869 int xoffset_text = tile_size + border_size;
3870 int font_nr = pos->font;
3871 int font_width = getFontWidth(font_nr);
3872 int font_height = getFontHeight(font_nr);
3873 int player_height = MAX(tile_size, font_height);
3874 int player_yoffset = player_height + border_size;
3875 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3876 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3877 int all_players_height = num_players * player_yoffset - border_size;
3878 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3879 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3880 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3882 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3883 max_players_width, max_players_height);
3885 // first draw local network player ...
3886 for (i = 0; i < MAX_PLAYERS; i++)
3888 if (stored_player[i].connected_network &&
3889 stored_player[i].connected_locally)
3891 char *player_name = getNetworkPlayerName(i + 1);
3892 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3893 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3895 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3897 ypos += player_yoffset;
3901 // ... then draw all other network players
3902 for (i = 0; i < MAX_PLAYERS; i++)
3904 if (stored_player[i].connected_network &&
3905 !stored_player[i].connected_locally)
3907 char *player_name = getNetworkPlayerName(i + 1);
3908 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3909 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3911 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3913 ypos += player_yoffset;
3918 void DrawNetworkPlayers(void)
3920 DrawNetworkPlayersExt(FALSE);
3923 void ClearNetworkPlayers(void)
3925 DrawNetworkPlayersExt(TRUE);
3928 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3929 int graphic, int lx, int ly,
3932 int frame = getGraphicAnimationFrameXY(graphic, lx, ly);
3934 if (mask_mode == USE_MASKING)
3935 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3937 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3940 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3941 int graphic, int sync_frame, int mask_mode)
3943 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3945 if (mask_mode == USE_MASKING)
3946 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3948 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3951 static void DrawGraphicAnimation(int x, int y, int graphic)
3953 int lx = LEVELX(x), ly = LEVELY(y);
3954 int mask_mode = NO_MASKING;
3956 if (!IN_SCR_FIELD(x, y))
3959 if (game.use_masked_elements)
3961 if (Tile[lx][ly] != EL_EMPTY)
3963 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3965 mask_mode = USE_MASKING;
3969 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3970 graphic, lx, ly, mask_mode);
3972 MarkTileDirty(x, y);
3975 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3977 int lx = LEVELX(x), ly = LEVELY(y);
3978 int mask_mode = NO_MASKING;
3980 if (!IN_SCR_FIELD(x, y))
3983 if (game.use_masked_elements)
3985 if (Tile[lx][ly] != EL_EMPTY)
3987 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3989 mask_mode = USE_MASKING;
3993 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3994 graphic, lx, ly, mask_mode);
3996 MarkTileDirty(x, y);
3999 void DrawLevelGraphicAnimation(int x, int y, int graphic)
4001 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4004 void DrawLevelElementAnimation(int x, int y, int element)
4006 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4008 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4011 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
4013 int sx = SCREENX(x), sy = SCREENY(y);
4015 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4018 if (Tile[x][y] == EL_EMPTY)
4019 graphic = el2img(GfxElementEmpty[x][y]);
4021 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4024 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4027 DrawGraphicAnimation(sx, sy, graphic);
4030 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4031 DrawLevelFieldCrumbled(x, y);
4033 if (GFX_CRUMBLED(Tile[x][y]))
4034 DrawLevelFieldCrumbled(x, y);
4038 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4040 int sx = SCREENX(x), sy = SCREENY(y);
4043 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4046 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4048 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4051 DrawGraphicAnimation(sx, sy, graphic);
4053 if (GFX_CRUMBLED(element))
4054 DrawLevelFieldCrumbled(x, y);
4057 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4059 if (player->use_murphy)
4061 // this works only because currently only one player can be "murphy" ...
4062 static int last_horizontal_dir = MV_LEFT;
4063 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4065 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4066 last_horizontal_dir = move_dir;
4068 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4070 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4072 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4078 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
4081 static boolean equalGraphics(int graphic1, int graphic2)
4083 struct GraphicInfo *g1 = &graphic_info[graphic1];
4084 struct GraphicInfo *g2 = &graphic_info[graphic2];
4086 return (g1->bitmap == g2->bitmap &&
4087 g1->src_x == g2->src_x &&
4088 g1->src_y == g2->src_y &&
4089 g1->anim_frames == g2->anim_frames &&
4090 g1->anim_delay == g2->anim_delay &&
4091 g1->anim_mode == g2->anim_mode);
4094 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4098 DRAW_PLAYER_STAGE_INIT = 0,
4099 DRAW_PLAYER_STAGE_LAST_FIELD,
4100 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4101 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4102 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4103 DRAW_PLAYER_STAGE_PLAYER,
4105 DRAW_PLAYER_STAGE_PLAYER,
4106 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4108 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4109 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4111 NUM_DRAW_PLAYER_STAGES
4114 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4116 static int static_last_player_graphic[MAX_PLAYERS];
4117 static int static_last_player_frame[MAX_PLAYERS];
4118 static boolean static_player_is_opaque[MAX_PLAYERS];
4119 static boolean draw_player[MAX_PLAYERS];
4120 int pnr = player->index_nr;
4122 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4124 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4125 static_last_player_frame[pnr] = player->Frame;
4126 static_player_is_opaque[pnr] = FALSE;
4128 draw_player[pnr] = TRUE;
4131 if (!draw_player[pnr])
4135 if (!IN_LEV_FIELD(player->jx, player->jy))
4137 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4138 Debug("draw:DrawPlayerExt", "This should never happen!");
4140 draw_player[pnr] = FALSE;
4146 int last_player_graphic = static_last_player_graphic[pnr];
4147 int last_player_frame = static_last_player_frame[pnr];
4148 boolean player_is_opaque = static_player_is_opaque[pnr];
4150 int jx = player->jx;
4151 int jy = player->jy;
4152 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4153 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4154 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4155 int last_jx = (player->is_moving ? jx - dx : jx);
4156 int last_jy = (player->is_moving ? jy - dy : jy);
4157 int next_jx = jx + dx;
4158 int next_jy = jy + dy;
4159 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4160 int sx = SCREENX(jx);
4161 int sy = SCREENY(jy);
4162 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4163 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4164 int element = Tile[jx][jy];
4165 int last_element = Tile[last_jx][last_jy];
4166 int action = (player->is_pushing ? ACTION_PUSHING :
4167 player->is_digging ? ACTION_DIGGING :
4168 player->is_collecting ? ACTION_COLLECTING :
4169 player->is_moving ? ACTION_MOVING :
4170 player->is_snapping ? ACTION_SNAPPING :
4171 player->is_dropping ? ACTION_DROPPING :
4172 player->is_waiting ? player->action_waiting :
4175 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4177 // ------------------------------------------------------------------------
4178 // initialize drawing the player
4179 // ------------------------------------------------------------------------
4181 draw_player[pnr] = FALSE;
4183 // GfxElement[][] is set to the element the player is digging or collecting;
4184 // remove also for off-screen player if the player is not moving anymore
4185 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4186 GfxElement[jx][jy] = EL_UNDEFINED;
4188 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4191 if (element == EL_EXPLOSION)
4194 InitPlayerGfxAnimation(player, action, move_dir);
4196 draw_player[pnr] = TRUE;
4198 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4200 // ------------------------------------------------------------------------
4201 // draw things in the field the player is leaving, if needed
4202 // ------------------------------------------------------------------------
4204 if (!IN_SCR_FIELD(sx, sy))
4205 draw_player[pnr] = FALSE;
4207 if (!player->is_moving)
4210 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4212 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4214 if (last_element == EL_DYNAMITE_ACTIVE ||
4215 last_element == EL_EM_DYNAMITE_ACTIVE ||
4216 last_element == EL_SP_DISK_RED_ACTIVE)
4217 DrawDynamite(last_jx, last_jy);
4219 DrawLevelFieldThruMask(last_jx, last_jy);
4221 else if (last_element == EL_DYNAMITE_ACTIVE ||
4222 last_element == EL_EM_DYNAMITE_ACTIVE ||
4223 last_element == EL_SP_DISK_RED_ACTIVE)
4224 DrawDynamite(last_jx, last_jy);
4226 DrawLevelField(last_jx, last_jy);
4228 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4230 // ------------------------------------------------------------------------
4231 // draw things behind the player, if needed
4232 // ------------------------------------------------------------------------
4236 DrawLevelElement(jx, jy, Back[jx][jy]);
4241 if (IS_ACTIVE_BOMB(element))
4243 DrawLevelElement(jx, jy, EL_EMPTY);
4248 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4250 int old_element = GfxElement[jx][jy];
4251 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4252 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4254 if (GFX_CRUMBLED(old_element))
4255 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4257 DrawScreenGraphic(sx, sy, old_graphic, frame);
4259 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4260 static_player_is_opaque[pnr] = TRUE;
4264 GfxElement[jx][jy] = EL_UNDEFINED;
4266 // make sure that pushed elements are drawn with correct frame rate
4267 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4269 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4270 GfxFrame[jx][jy] = player->StepFrame;
4272 DrawLevelField(jx, jy);
4275 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4277 // ------------------------------------------------------------------------
4278 // draw things the player is pushing, if needed
4279 // ------------------------------------------------------------------------
4281 if (!player->is_pushing || !player->is_moving)
4284 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4287 int gfx_frame = GfxFrame[jx][jy];
4289 if (!IS_MOVING(jx, jy)) // push movement already finished
4291 element = Tile[next_jx][next_jy];
4292 gfx_frame = GfxFrame[next_jx][next_jy];
4295 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4296 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4297 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4299 // draw background element under pushed element (like the Sokoban field)
4300 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4302 // this allows transparent pushing animation over non-black background
4305 DrawLevelElement(jx, jy, Back[jx][jy]);
4307 DrawLevelElement(jx, jy, EL_EMPTY);
4310 if (Back[next_jx][next_jy])
4311 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4313 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4315 int px = SCREENX(jx), py = SCREENY(jy);
4316 int pxx = (TILEX - ABS(sxx)) * dx;
4317 int pyy = (TILEY - ABS(syy)) * dy;
4320 // do not draw (EM style) pushing animation when pushing is finished
4321 // (two-tile animations usually do not contain start and end frame)
4322 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4323 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4325 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4327 // masked drawing is needed for EMC style (double) movement graphics
4328 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4329 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4332 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4334 // ------------------------------------------------------------------------
4335 // draw player himself
4336 // ------------------------------------------------------------------------
4338 int graphic = getPlayerGraphic(player, move_dir);
4340 // in the case of changed player action or direction, prevent the current
4341 // animation frame from being restarted for identical animations
4342 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4343 player->Frame = last_player_frame;
4345 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4347 if (player_is_opaque)
4348 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4350 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4352 if (SHIELD_ON(player))
4354 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4355 IMG_SHIELD_NORMAL_ACTIVE);
4356 frame = getGraphicAnimationFrame(graphic, -1);
4358 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4361 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4363 // ------------------------------------------------------------------------
4364 // draw things in front of player (active dynamite or dynabombs)
4365 // ------------------------------------------------------------------------
4367 if (IS_ACTIVE_BOMB(element))
4369 int graphic = el2img(element);
4370 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4372 if (game.emulation == EMU_SUPAPLEX)
4373 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4375 DrawGraphicThruMask(sx, sy, graphic, frame);
4378 if (player_is_moving && last_element == EL_EXPLOSION)
4380 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4381 GfxElement[last_jx][last_jy] : EL_EMPTY);
4382 int graphic = el_act2img(element, ACTION_EXPLODING);
4383 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4384 int phase = ExplodePhase[last_jx][last_jy] - 1;
4385 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4388 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4391 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4393 // ------------------------------------------------------------------------
4394 // draw elements the player is just walking/passing through/under
4395 // ------------------------------------------------------------------------
4397 if (player_is_moving)
4399 // handle the field the player is leaving ...
4400 if (IS_ACCESSIBLE_INSIDE(last_element))
4401 DrawLevelField(last_jx, last_jy);
4402 else if (IS_ACCESSIBLE_UNDER(last_element))
4403 DrawLevelFieldThruMask(last_jx, last_jy);
4406 // do not redraw accessible elements if the player is just pushing them
4407 if (!player_is_moving || !player->is_pushing)
4409 // ... and the field the player is entering
4410 if (IS_ACCESSIBLE_INSIDE(element))
4411 DrawLevelField(jx, jy);
4412 else if (IS_ACCESSIBLE_UNDER(element))
4413 DrawLevelFieldThruMask(jx, jy);
4416 MarkTileDirty(sx, sy);
4420 void DrawPlayer(struct PlayerInfo *player)
4424 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4425 DrawPlayerExt(player, i);
4428 void DrawAllPlayers(void)
4432 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4433 for (j = 0; j < MAX_PLAYERS; j++)
4434 if (stored_player[j].active)
4435 DrawPlayerExt(&stored_player[j], i);
4438 void DrawPlayerField(int x, int y)
4440 if (!IS_PLAYER(x, y))
4443 DrawPlayer(PLAYERINFO(x, y));
4446 // ----------------------------------------------------------------------------
4448 void WaitForEventToContinue(void)
4450 boolean first_wait = TRUE;
4451 boolean still_wait = TRUE;
4453 if (program.headless)
4456 // simulate releasing mouse button over last gadget, if still pressed
4458 HandleGadgets(-1, -1, 0);
4460 button_status = MB_RELEASED;
4463 ClearPlayerAction();
4469 if (NextValidEvent(&event))
4473 case EVENT_BUTTONPRESS:
4474 case EVENT_FINGERPRESS:
4478 case EVENT_BUTTONRELEASE:
4479 case EVENT_FINGERRELEASE:
4480 still_wait = first_wait;
4483 case EVENT_KEYPRESS:
4484 case SDL_CONTROLLERBUTTONDOWN:
4485 case SDL_JOYBUTTONDOWN:
4490 HandleOtherEvents(&event);
4494 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4499 if (!PendingEvent())
4504 #define MAX_REQUEST_LINES 13
4505 #define MAX_REQUEST_LINE_FONT1_LEN 7
4506 #define MAX_REQUEST_LINE_FONT2_LEN 10
4508 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4510 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4512 int draw_buffer_last = GetDrawtoField();
4513 int width = request.width;
4514 int height = request.height;
4518 // when showing request dialog after game ended, deactivate game panel
4519 if (game_just_ended)
4520 game.panel.active = FALSE;
4522 game.request_active = TRUE;
4524 setRequestPosition(&sx, &sy, FALSE);
4526 button_status = MB_RELEASED;
4528 request_gadget_id = -1;
4533 boolean event_handled = FALSE;
4535 if (game_just_ended)
4537 SetDrawtoField(draw_buffer_game);
4539 HandleGameActions();
4541 SetDrawtoField(DRAW_TO_BACKBUFFER);
4543 if (global.use_envelope_request)
4545 // copy current state of request area to middle of playfield area
4546 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4554 while (NextValidEvent(&event))
4556 event_handled = TRUE;
4560 case EVENT_BUTTONPRESS:
4561 case EVENT_BUTTONRELEASE:
4562 case EVENT_MOTIONNOTIFY:
4566 if (event.type == EVENT_MOTIONNOTIFY)
4571 motion_status = TRUE;
4572 mx = ((MotionEvent *) &event)->x;
4573 my = ((MotionEvent *) &event)->y;
4577 motion_status = FALSE;
4578 mx = ((ButtonEvent *) &event)->x;
4579 my = ((ButtonEvent *) &event)->y;
4580 if (event.type == EVENT_BUTTONPRESS)
4581 button_status = ((ButtonEvent *) &event)->button;
4583 button_status = MB_RELEASED;
4586 // this sets 'request_gadget_id'
4587 HandleGadgets(mx, my, button_status);
4589 switch (request_gadget_id)
4591 case TOOL_CTRL_ID_YES:
4592 case TOOL_CTRL_ID_TOUCH_YES:
4595 case TOOL_CTRL_ID_NO:
4596 case TOOL_CTRL_ID_TOUCH_NO:
4599 case TOOL_CTRL_ID_CONFIRM:
4600 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4601 result = TRUE | FALSE;
4604 case TOOL_CTRL_ID_PLAYER_1:
4607 case TOOL_CTRL_ID_PLAYER_2:
4610 case TOOL_CTRL_ID_PLAYER_3:
4613 case TOOL_CTRL_ID_PLAYER_4:
4618 // only check clickable animations if no request gadget clicked
4619 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4626 case SDL_WINDOWEVENT:
4627 HandleWindowEvent((WindowEvent *) &event);
4630 case SDL_APP_WILLENTERBACKGROUND:
4631 case SDL_APP_DIDENTERBACKGROUND:
4632 case SDL_APP_WILLENTERFOREGROUND:
4633 case SDL_APP_DIDENTERFOREGROUND:
4634 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4637 case EVENT_KEYPRESS:
4639 Key key = GetEventKey((KeyEvent *)&event);
4644 if (req_state & REQ_CONFIRM)
4653 #if defined(KSYM_Rewind)
4654 case KSYM_Rewind: // for Amazon Fire TV remote
4663 #if defined(KSYM_FastForward)
4664 case KSYM_FastForward: // for Amazon Fire TV remote
4670 HandleKeysDebug(key, KEY_PRESSED);
4674 if (req_state & REQ_PLAYER)
4676 int old_player_nr = setup.network_player_nr;
4679 result = old_player_nr + 1;
4684 result = old_player_nr + 1;
4715 case EVENT_FINGERRELEASE:
4716 case EVENT_KEYRELEASE:
4717 ClearPlayerAction();
4720 case SDL_CONTROLLERBUTTONDOWN:
4721 switch (event.cbutton.button)
4723 case SDL_CONTROLLER_BUTTON_A:
4724 case SDL_CONTROLLER_BUTTON_X:
4725 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4726 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4730 case SDL_CONTROLLER_BUTTON_B:
4731 case SDL_CONTROLLER_BUTTON_Y:
4732 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4733 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4734 case SDL_CONTROLLER_BUTTON_BACK:
4739 if (req_state & REQ_PLAYER)
4741 int old_player_nr = setup.network_player_nr;
4744 result = old_player_nr + 1;
4746 switch (event.cbutton.button)
4748 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4749 case SDL_CONTROLLER_BUTTON_Y:
4753 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4754 case SDL_CONTROLLER_BUTTON_B:
4758 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4759 case SDL_CONTROLLER_BUTTON_A:
4763 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4764 case SDL_CONTROLLER_BUTTON_X:
4775 case SDL_CONTROLLERBUTTONUP:
4776 HandleJoystickEvent(&event);
4777 ClearPlayerAction();
4781 HandleOtherEvents(&event);
4786 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4788 int joy = AnyJoystick();
4790 if (joy & JOY_BUTTON_1)
4792 else if (joy & JOY_BUTTON_2)
4795 else if (AnyJoystick())
4797 int joy = AnyJoystick();
4799 if (req_state & REQ_PLAYER)
4803 else if (joy & JOY_RIGHT)
4805 else if (joy & JOY_DOWN)
4807 else if (joy & JOY_LEFT)
4814 if (game_just_ended)
4816 if (global.use_envelope_request)
4818 // copy back current state of pressed buttons inside request area
4819 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4823 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4829 SetDrawtoField(draw_buffer_last);
4831 game.request_active = FALSE;
4836 static boolean RequestDoor(char *text, unsigned int req_state)
4838 int draw_buffer_last = GetDrawtoField();
4839 unsigned int old_door_state;
4840 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4841 int font_nr = FONT_TEXT_2;
4846 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4848 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4849 font_nr = FONT_TEXT_1;
4852 if (game_status == GAME_MODE_PLAYING)
4853 BlitScreenToBitmap(backbuffer);
4855 // disable deactivated drawing when quick-loading level tape recording
4856 if (tape.playing && tape.deactivate_display)
4857 TapeDeactivateDisplayOff(TRUE);
4859 SetMouseCursor(CURSOR_DEFAULT);
4861 // pause network game while waiting for request to answer
4862 if (network.enabled &&
4863 game_status == GAME_MODE_PLAYING &&
4864 !game.all_players_gone &&
4865 req_state & REQUEST_WAIT_FOR_INPUT)
4866 SendToServer_PausePlaying();
4868 old_door_state = GetDoorState();
4870 // simulate releasing mouse button over last gadget, if still pressed
4872 HandleGadgets(-1, -1, 0);
4876 // draw released gadget before proceeding
4879 if (old_door_state & DOOR_OPEN_1)
4881 CloseDoor(DOOR_CLOSE_1);
4883 // save old door content
4884 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4885 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4888 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4889 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4891 // clear door drawing field
4892 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4894 // force DOOR font inside door area
4895 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4897 // write text for request
4898 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4900 char text_line[max_request_line_len + 1];
4906 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4908 tc = *(text_ptr + tx);
4909 // if (!tc || tc == ' ')
4910 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4914 if ((tc == '?' || tc == '!') && tl == 0)
4924 strncpy(text_line, text_ptr, tl);
4927 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4928 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4929 text_line, font_nr);
4931 text_ptr += tl + (tc == ' ' ? 1 : 0);
4932 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4937 if (req_state & REQ_ASK)
4939 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4940 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4941 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4942 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4944 else if (req_state & REQ_CONFIRM)
4946 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4947 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4949 else if (req_state & REQ_PLAYER)
4951 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4952 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4953 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4954 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4957 // copy request gadgets to door backbuffer
4958 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4960 OpenDoor(DOOR_OPEN_1);
4962 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4964 if (game_status == GAME_MODE_PLAYING)
4966 SetPanelBackground();
4967 SetDrawBackgroundMask(REDRAW_DOOR_1);
4971 SetDrawBackgroundMask(REDRAW_FIELD);
4977 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4979 // ---------- handle request buttons ----------
4980 result = RequestHandleEvents(req_state, draw_buffer_last);
4984 if (!(req_state & REQ_STAY_OPEN))
4986 CloseDoor(DOOR_CLOSE_1);
4988 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4989 (req_state & REQ_REOPEN))
4990 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4995 if (game_status == GAME_MODE_PLAYING)
4997 SetPanelBackground();
4998 SetDrawBackgroundMask(REDRAW_DOOR_1);
5002 SetDrawBackgroundMask(REDRAW_FIELD);
5005 // continue network game after request
5006 if (network.enabled &&
5007 game_status == GAME_MODE_PLAYING &&
5008 !game.all_players_gone &&
5009 req_state & REQUEST_WAIT_FOR_INPUT)
5010 SendToServer_ContinuePlaying();
5012 // restore deactivated drawing when quick-loading level tape recording
5013 if (tape.playing && tape.deactivate_display)
5014 TapeDeactivateDisplayOn();
5019 static boolean RequestEnvelope(char *text, unsigned int req_state)
5021 int draw_buffer_last = GetDrawtoField();
5024 if (game_status == GAME_MODE_PLAYING)
5025 BlitScreenToBitmap(backbuffer);
5027 // disable deactivated drawing when quick-loading level tape recording
5028 if (tape.playing && tape.deactivate_display)
5029 TapeDeactivateDisplayOff(TRUE);
5031 SetMouseCursor(CURSOR_DEFAULT);
5033 // pause network game while waiting for request to answer
5034 if (network.enabled &&
5035 game_status == GAME_MODE_PLAYING &&
5036 !game.all_players_gone &&
5037 req_state & REQUEST_WAIT_FOR_INPUT)
5038 SendToServer_PausePlaying();
5040 // simulate releasing mouse button over last gadget, if still pressed
5042 HandleGadgets(-1, -1, 0);
5046 // (replace with setting corresponding request background)
5047 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5048 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5050 // clear door drawing field
5051 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5053 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5055 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5057 if (game_status == GAME_MODE_PLAYING)
5059 SetPanelBackground();
5060 SetDrawBackgroundMask(REDRAW_DOOR_1);
5064 SetDrawBackgroundMask(REDRAW_FIELD);
5070 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5072 // ---------- handle request buttons ----------
5073 result = RequestHandleEvents(req_state, draw_buffer_last);
5077 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5081 if (game_status == GAME_MODE_PLAYING)
5083 SetPanelBackground();
5084 SetDrawBackgroundMask(REDRAW_DOOR_1);
5088 SetDrawBackgroundMask(REDRAW_FIELD);
5091 // continue network game after request
5092 if (network.enabled &&
5093 game_status == GAME_MODE_PLAYING &&
5094 !game.all_players_gone &&
5095 req_state & REQUEST_WAIT_FOR_INPUT)
5096 SendToServer_ContinuePlaying();
5098 // restore deactivated drawing when quick-loading level tape recording
5099 if (tape.playing && tape.deactivate_display)
5100 TapeDeactivateDisplayOn();
5105 boolean Request(char *text, unsigned int req_state)
5107 boolean overlay_enabled = GetOverlayEnabled();
5110 game.request_active_or_moving = TRUE;
5112 SetOverlayEnabled(FALSE);
5114 if (global.use_envelope_request)
5115 result = RequestEnvelope(text, req_state);
5117 result = RequestDoor(text, req_state);
5119 SetOverlayEnabled(overlay_enabled);
5121 game.request_active_or_moving = FALSE;
5126 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5128 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5129 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5132 if (dpo1->sort_priority != dpo2->sort_priority)
5133 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5135 compare_result = dpo1->nr - dpo2->nr;
5137 return compare_result;
5140 void InitGraphicCompatibilityInfo_Doors(void)
5146 struct DoorInfo *door;
5150 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5151 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5153 { -1, -1, -1, NULL }
5155 struct Rect door_rect_list[] =
5157 { DX, DY, DXSIZE, DYSIZE },
5158 { VX, VY, VXSIZE, VYSIZE }
5162 for (i = 0; doors[i].door_token != -1; i++)
5164 int door_token = doors[i].door_token;
5165 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5166 int part_1 = doors[i].part_1;
5167 int part_8 = doors[i].part_8;
5168 int part_2 = part_1 + 1;
5169 int part_3 = part_1 + 2;
5170 struct DoorInfo *door = doors[i].door;
5171 struct Rect *door_rect = &door_rect_list[door_index];
5172 boolean door_gfx_redefined = FALSE;
5174 // check if any door part graphic definitions have been redefined
5176 for (j = 0; door_part_controls[j].door_token != -1; j++)
5178 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5179 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5181 if (dpc->door_token == door_token && fi->redefined)
5182 door_gfx_redefined = TRUE;
5185 // check for old-style door graphic/animation modifications
5187 if (!door_gfx_redefined)
5189 if (door->anim_mode & ANIM_STATIC_PANEL)
5191 door->panel.step_xoffset = 0;
5192 door->panel.step_yoffset = 0;
5195 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5197 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5198 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5199 int num_door_steps, num_panel_steps;
5201 // remove door part graphics other than the two default wings
5203 for (j = 0; door_part_controls[j].door_token != -1; j++)
5205 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5206 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5208 if (dpc->graphic >= part_3 &&
5209 dpc->graphic <= part_8)
5213 // set graphics and screen positions of the default wings
5215 g_part_1->width = door_rect->width;
5216 g_part_1->height = door_rect->height;
5217 g_part_2->width = door_rect->width;
5218 g_part_2->height = door_rect->height;
5219 g_part_2->src_x = door_rect->width;
5220 g_part_2->src_y = g_part_1->src_y;
5222 door->part_2.x = door->part_1.x;
5223 door->part_2.y = door->part_1.y;
5225 if (door->width != -1)
5227 g_part_1->width = door->width;
5228 g_part_2->width = door->width;
5230 // special treatment for graphics and screen position of right wing
5231 g_part_2->src_x += door_rect->width - door->width;
5232 door->part_2.x += door_rect->width - door->width;
5235 if (door->height != -1)
5237 g_part_1->height = door->height;
5238 g_part_2->height = door->height;
5240 // special treatment for graphics and screen position of bottom wing
5241 g_part_2->src_y += door_rect->height - door->height;
5242 door->part_2.y += door_rect->height - door->height;
5245 // set animation delays for the default wings and panels
5247 door->part_1.step_delay = door->step_delay;
5248 door->part_2.step_delay = door->step_delay;
5249 door->panel.step_delay = door->step_delay;
5251 // set animation draw order for the default wings
5253 door->part_1.sort_priority = 2; // draw left wing over ...
5254 door->part_2.sort_priority = 1; // ... right wing
5256 // set animation draw offset for the default wings
5258 if (door->anim_mode & ANIM_HORIZONTAL)
5260 door->part_1.step_xoffset = door->step_offset;
5261 door->part_1.step_yoffset = 0;
5262 door->part_2.step_xoffset = door->step_offset * -1;
5263 door->part_2.step_yoffset = 0;
5265 num_door_steps = g_part_1->width / door->step_offset;
5267 else // ANIM_VERTICAL
5269 door->part_1.step_xoffset = 0;
5270 door->part_1.step_yoffset = door->step_offset;
5271 door->part_2.step_xoffset = 0;
5272 door->part_2.step_yoffset = door->step_offset * -1;
5274 num_door_steps = g_part_1->height / door->step_offset;
5277 // set animation draw offset for the default panels
5279 if (door->step_offset > 1)
5281 num_panel_steps = 2 * door_rect->height / door->step_offset;
5282 door->panel.start_step = num_panel_steps - num_door_steps;
5283 door->panel.start_step_closing = door->panel.start_step;
5287 num_panel_steps = door_rect->height / door->step_offset;
5288 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5289 door->panel.start_step_closing = door->panel.start_step;
5290 door->panel.step_delay *= 2;
5297 void InitDoors(void)
5301 for (i = 0; door_part_controls[i].door_token != -1; i++)
5303 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5304 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5306 // initialize "start_step_opening" and "start_step_closing", if needed
5307 if (dpc->pos->start_step_opening == 0 &&
5308 dpc->pos->start_step_closing == 0)
5310 // dpc->pos->start_step_opening = dpc->pos->start_step;
5311 dpc->pos->start_step_closing = dpc->pos->start_step;
5314 // fill structure for door part draw order (sorted below)
5316 dpo->sort_priority = dpc->pos->sort_priority;
5319 // sort door part controls according to sort_priority and graphic number
5320 qsort(door_part_order, MAX_DOOR_PARTS,
5321 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5324 unsigned int OpenDoor(unsigned int door_state)
5326 if (door_state & DOOR_COPY_BACK)
5328 if (door_state & DOOR_OPEN_1)
5329 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5330 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5332 if (door_state & DOOR_OPEN_2)
5333 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5334 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5336 door_state &= ~DOOR_COPY_BACK;
5339 return MoveDoor(door_state);
5342 unsigned int CloseDoor(unsigned int door_state)
5344 unsigned int old_door_state = GetDoorState();
5346 if (!(door_state & DOOR_NO_COPY_BACK))
5348 if (old_door_state & DOOR_OPEN_1)
5349 BlitBitmap(backbuffer, bitmap_db_door_1,
5350 DX, DY, DXSIZE, DYSIZE, 0, 0);
5352 if (old_door_state & DOOR_OPEN_2)
5353 BlitBitmap(backbuffer, bitmap_db_door_2,
5354 VX, VY, VXSIZE, VYSIZE, 0, 0);
5356 door_state &= ~DOOR_NO_COPY_BACK;
5359 return MoveDoor(door_state);
5362 unsigned int GetDoorState(void)
5364 return MoveDoor(DOOR_GET_STATE);
5367 unsigned int SetDoorState(unsigned int door_state)
5369 return MoveDoor(door_state | DOOR_SET_STATE);
5372 static int euclid(int a, int b)
5374 return (b ? euclid(b, a % b) : a);
5377 unsigned int MoveDoor(unsigned int door_state)
5379 struct Rect door_rect_list[] =
5381 { DX, DY, DXSIZE, DYSIZE },
5382 { VX, VY, VXSIZE, VYSIZE }
5384 static int door1 = DOOR_CLOSE_1;
5385 static int door2 = DOOR_CLOSE_2;
5386 DelayCounter door_delay = { 0 };
5389 if (door_state == DOOR_GET_STATE)
5390 return (door1 | door2);
5392 if (door_state & DOOR_SET_STATE)
5394 if (door_state & DOOR_ACTION_1)
5395 door1 = door_state & DOOR_ACTION_1;
5396 if (door_state & DOOR_ACTION_2)
5397 door2 = door_state & DOOR_ACTION_2;
5399 return (door1 | door2);
5402 if (!(door_state & DOOR_FORCE_REDRAW))
5404 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5405 door_state &= ~DOOR_OPEN_1;
5406 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5407 door_state &= ~DOOR_CLOSE_1;
5408 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5409 door_state &= ~DOOR_OPEN_2;
5410 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5411 door_state &= ~DOOR_CLOSE_2;
5414 if (global.autoplay_leveldir)
5416 door_state |= DOOR_NO_DELAY;
5417 door_state &= ~DOOR_CLOSE_ALL;
5420 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5421 door_state |= DOOR_NO_DELAY;
5423 if (door_state & DOOR_ACTION)
5425 boolean door_panel_drawn[NUM_DOORS];
5426 boolean panel_has_doors[NUM_DOORS];
5427 boolean door_part_skip[MAX_DOOR_PARTS];
5428 boolean door_part_done[MAX_DOOR_PARTS];
5429 boolean door_part_done_all;
5430 int num_steps[MAX_DOOR_PARTS];
5431 int max_move_delay = 0; // delay for complete animations of all doors
5432 int max_step_delay = 0; // delay (ms) between two animation frames
5433 int num_move_steps = 0; // number of animation steps for all doors
5434 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5435 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5439 for (i = 0; i < NUM_DOORS; i++)
5440 panel_has_doors[i] = FALSE;
5442 for (i = 0; i < MAX_DOOR_PARTS; i++)
5444 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5445 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5446 int door_token = dpc->door_token;
5448 door_part_done[i] = FALSE;
5449 door_part_skip[i] = (!(door_state & door_token) ||
5453 for (i = 0; i < MAX_DOOR_PARTS; i++)
5455 int nr = door_part_order[i].nr;
5456 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5457 struct DoorPartPosInfo *pos = dpc->pos;
5458 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5459 int door_token = dpc->door_token;
5460 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5461 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5462 int step_xoffset = ABS(pos->step_xoffset);
5463 int step_yoffset = ABS(pos->step_yoffset);
5464 int step_delay = pos->step_delay;
5465 int current_door_state = door_state & door_token;
5466 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5467 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5468 boolean part_opening = (is_panel ? door_closing : door_opening);
5469 int start_step = (part_opening ? pos->start_step_opening :
5470 pos->start_step_closing);
5471 float move_xsize = (step_xoffset ? g->width : 0);
5472 float move_ysize = (step_yoffset ? g->height : 0);
5473 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5474 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5475 int move_steps = (move_xsteps && move_ysteps ?
5476 MIN(move_xsteps, move_ysteps) :
5477 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5478 int move_delay = move_steps * step_delay;
5480 if (door_part_skip[nr])
5483 max_move_delay = MAX(max_move_delay, move_delay);
5484 max_step_delay = (max_step_delay == 0 ? step_delay :
5485 euclid(max_step_delay, step_delay));
5486 num_steps[nr] = move_steps;
5490 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5492 panel_has_doors[door_index] = TRUE;
5496 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5498 num_move_steps = max_move_delay / max_step_delay;
5499 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5501 door_delay.value = max_step_delay;
5503 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5505 start = num_move_steps - 1;
5509 // opening door sound has priority over simultaneously closing door
5510 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5512 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5514 if (door_state & DOOR_OPEN_1)
5515 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5516 if (door_state & DOOR_OPEN_2)
5517 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5519 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5521 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5523 if (door_state & DOOR_CLOSE_1)
5524 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5525 if (door_state & DOOR_CLOSE_2)
5526 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5530 for (k = start; k < num_move_steps; k++)
5532 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5534 door_part_done_all = TRUE;
5536 for (i = 0; i < NUM_DOORS; i++)
5537 door_panel_drawn[i] = FALSE;
5539 for (i = 0; i < MAX_DOOR_PARTS; i++)
5541 int nr = door_part_order[i].nr;
5542 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5543 struct DoorPartPosInfo *pos = dpc->pos;
5544 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5545 int door_token = dpc->door_token;
5546 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5547 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5548 boolean is_panel_and_door_has_closed = FALSE;
5549 struct Rect *door_rect = &door_rect_list[door_index];
5550 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5552 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5553 int current_door_state = door_state & door_token;
5554 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5555 boolean door_closing = !door_opening;
5556 boolean part_opening = (is_panel ? door_closing : door_opening);
5557 boolean part_closing = !part_opening;
5558 int start_step = (part_opening ? pos->start_step_opening :
5559 pos->start_step_closing);
5560 int step_delay = pos->step_delay;
5561 int step_factor = step_delay / max_step_delay;
5562 int k1 = (step_factor ? k / step_factor + 1 : k);
5563 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5564 int kk = MAX(0, k2);
5567 int src_x, src_y, src_xx, src_yy;
5568 int dst_x, dst_y, dst_xx, dst_yy;
5571 if (door_part_skip[nr])
5574 if (!(door_state & door_token))
5582 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5583 int kk_door = MAX(0, k2_door);
5584 int sync_frame = kk_door * door_delay.value;
5585 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5587 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5588 &g_src_x, &g_src_y);
5593 if (!door_panel_drawn[door_index])
5595 ClearRectangle(drawto, door_rect->x, door_rect->y,
5596 door_rect->width, door_rect->height);
5598 door_panel_drawn[door_index] = TRUE;
5601 // draw opening or closing door parts
5603 if (pos->step_xoffset < 0) // door part on right side
5606 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5609 if (dst_xx + width > door_rect->width)
5610 width = door_rect->width - dst_xx;
5612 else // door part on left side
5615 dst_xx = pos->x - kk * pos->step_xoffset;
5619 src_xx = ABS(dst_xx);
5623 width = g->width - src_xx;
5625 if (width > door_rect->width)
5626 width = door_rect->width;
5628 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5631 if (pos->step_yoffset < 0) // door part on bottom side
5634 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5637 if (dst_yy + height > door_rect->height)
5638 height = door_rect->height - dst_yy;
5640 else // door part on top side
5643 dst_yy = pos->y - kk * pos->step_yoffset;
5647 src_yy = ABS(dst_yy);
5651 height = g->height - src_yy;
5654 src_x = g_src_x + src_xx;
5655 src_y = g_src_y + src_yy;
5657 dst_x = door_rect->x + dst_xx;
5658 dst_y = door_rect->y + dst_yy;
5660 is_panel_and_door_has_closed =
5663 panel_has_doors[door_index] &&
5664 k >= num_move_steps_doors_only - 1);
5666 if (width >= 0 && width <= g->width &&
5667 height >= 0 && height <= g->height &&
5668 !is_panel_and_door_has_closed)
5670 if (is_panel || !pos->draw_masked)
5671 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5674 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5678 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5680 if ((part_opening && (width < 0 || height < 0)) ||
5681 (part_closing && (width >= g->width && height >= g->height)))
5682 door_part_done[nr] = TRUE;
5684 // continue door part animations, but not panel after door has closed
5685 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5686 door_part_done_all = FALSE;
5689 if (!(door_state & DOOR_NO_DELAY))
5693 SkipUntilDelayReached(&door_delay, &k, last_frame);
5695 // prevent OS (Windows) from complaining about program not responding
5699 if (door_part_done_all)
5703 if (!(door_state & DOOR_NO_DELAY))
5705 // wait for specified door action post delay
5706 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5707 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5708 else if (door_state & DOOR_ACTION_1)
5709 door_delay.value = door_1.post_delay;
5710 else if (door_state & DOOR_ACTION_2)
5711 door_delay.value = door_2.post_delay;
5713 while (!DelayReached(&door_delay))
5718 if (door_state & DOOR_ACTION_1)
5719 door1 = door_state & DOOR_ACTION_1;
5720 if (door_state & DOOR_ACTION_2)
5721 door2 = door_state & DOOR_ACTION_2;
5723 // draw masked border over door area
5724 DrawMaskedBorder(REDRAW_DOOR_1);
5725 DrawMaskedBorder(REDRAW_DOOR_2);
5727 ClearAutoRepeatKeyEvents();
5729 return (door1 | door2);
5732 static boolean useSpecialEditorDoor(void)
5734 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5735 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5737 // do not draw special editor door if editor border defined or redefined
5738 if (graphic_info[graphic].bitmap != NULL || redefined)
5741 // do not draw special editor door if global border defined to be empty
5742 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5745 // do not draw special editor door if viewport definitions do not match
5749 EY + EYSIZE != VY + VYSIZE)
5755 void DrawSpecialEditorDoor(void)
5757 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5758 int top_border_width = gfx1->width;
5759 int top_border_height = gfx1->height;
5760 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5761 int ex = EX - outer_border;
5762 int ey = EY - outer_border;
5763 int vy = VY - outer_border;
5764 int exsize = EXSIZE + 2 * outer_border;
5766 if (!useSpecialEditorDoor())
5769 // draw bigger level editor toolbox window
5770 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5771 top_border_width, top_border_height, ex, ey - top_border_height);
5772 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5773 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5775 redraw_mask |= REDRAW_ALL;
5778 void UndrawSpecialEditorDoor(void)
5780 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5781 int top_border_width = gfx1->width;
5782 int top_border_height = gfx1->height;
5783 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5784 int ex = EX - outer_border;
5785 int ey = EY - outer_border;
5786 int ey_top = ey - top_border_height;
5787 int exsize = EXSIZE + 2 * outer_border;
5788 int eysize = EYSIZE + 2 * outer_border;
5790 if (!useSpecialEditorDoor())
5793 // draw normal tape recorder window
5794 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5796 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5797 ex, ey_top, top_border_width, top_border_height,
5799 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5800 ex, ey, exsize, eysize, ex, ey);
5804 // if screen background is set to "[NONE]", clear editor toolbox window
5805 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5806 ClearRectangle(drawto, ex, ey, exsize, eysize);
5809 redraw_mask |= REDRAW_ALL;
5813 // ---------- new tool button stuff -------------------------------------------
5818 struct TextPosInfo *pos;
5820 boolean is_touch_button;
5822 } toolbutton_info[NUM_TOOL_BUTTONS] =
5825 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5826 TOOL_CTRL_ID_YES, FALSE, "yes"
5829 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5830 TOOL_CTRL_ID_NO, FALSE, "no"
5833 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5834 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5837 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5838 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5841 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5842 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5845 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5846 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5849 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5850 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5853 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5854 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5857 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5858 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5861 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5862 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5866 void CreateToolButtons(void)
5870 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5872 int graphic = toolbutton_info[i].graphic;
5873 struct GraphicInfo *gfx = &graphic_info[graphic];
5874 struct TextPosInfo *pos = toolbutton_info[i].pos;
5875 struct GadgetInfo *gi;
5876 Bitmap *deco_bitmap = None;
5877 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5878 unsigned int event_mask = GD_EVENT_RELEASED;
5879 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5880 int base_x = (is_touch_button ? 0 : DX);
5881 int base_y = (is_touch_button ? 0 : DY);
5882 int gd_x = gfx->src_x;
5883 int gd_y = gfx->src_y;
5884 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5885 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5890 // do not use touch buttons if overlay touch buttons are disabled
5891 if (is_touch_button && !setup.touch.overlay_buttons)
5894 if (global.use_envelope_request && !is_touch_button)
5896 setRequestPosition(&base_x, &base_y, TRUE);
5898 // check if request buttons are outside of envelope and fix, if needed
5899 if (x < 0 || x + gfx->width > request.width ||
5900 y < 0 || y + gfx->height > request.height)
5902 if (id == TOOL_CTRL_ID_YES)
5905 y = request.height - 2 * request.border_size - gfx->height;
5907 else if (id == TOOL_CTRL_ID_NO)
5909 x = request.width - 2 * request.border_size - gfx->width;
5910 y = request.height - 2 * request.border_size - gfx->height;
5912 else if (id == TOOL_CTRL_ID_CONFIRM)
5914 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5915 y = request.height - 2 * request.border_size - gfx->height;
5917 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5919 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5921 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5922 y = request.height - 2 * request.border_size - gfx->height * 2;
5924 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5925 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5930 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5933 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5935 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5936 pos->size, &deco_bitmap, &deco_x, &deco_y);
5937 deco_xpos = (gfx->width - pos->size) / 2;
5938 deco_ypos = (gfx->height - pos->size) / 2;
5941 gi = CreateGadget(GDI_CUSTOM_ID, id,
5942 GDI_IMAGE_ID, graphic,
5943 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5946 GDI_WIDTH, gfx->width,
5947 GDI_HEIGHT, gfx->height,
5948 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5949 GDI_STATE, GD_BUTTON_UNPRESSED,
5950 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5951 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5952 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5953 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5954 GDI_DECORATION_SIZE, pos->size, pos->size,
5955 GDI_DECORATION_SHIFTING, 1, 1,
5956 GDI_DIRECT_DRAW, FALSE,
5957 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5958 GDI_EVENT_MASK, event_mask,
5959 GDI_CALLBACK_ACTION, HandleToolButtons,
5963 Fail("cannot create gadget");
5965 tool_gadget[id] = gi;
5969 void FreeToolButtons(void)
5973 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5974 FreeGadget(tool_gadget[i]);
5977 static void UnmapToolButtons(void)
5981 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5982 UnmapGadget(tool_gadget[i]);
5985 static void HandleToolButtons(struct GadgetInfo *gi)
5987 request_gadget_id = gi->custom_id;
5990 static struct Mapping_EM_to_RND_object
5993 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5994 boolean is_backside; // backside of moving element
6000 em_object_mapping_list[GAME_TILE_MAX + 1] =
6003 Zborder, FALSE, FALSE,
6007 Zplayer, FALSE, FALSE,
6016 Ztank, FALSE, FALSE,
6020 Zeater, FALSE, FALSE,
6024 Zdynamite, FALSE, FALSE,
6028 Zboom, FALSE, FALSE,
6033 Xchain, FALSE, FALSE,
6034 EL_DEFAULT, ACTION_EXPLODING, -1
6037 Xboom_bug, FALSE, FALSE,
6038 EL_BUG, ACTION_EXPLODING, -1
6041 Xboom_tank, FALSE, FALSE,
6042 EL_SPACESHIP, ACTION_EXPLODING, -1
6045 Xboom_android, FALSE, FALSE,
6046 EL_EMC_ANDROID, ACTION_OTHER, -1
6049 Xboom_1, FALSE, FALSE,
6050 EL_DEFAULT, ACTION_EXPLODING, -1
6053 Xboom_2, FALSE, FALSE,
6054 EL_DEFAULT, ACTION_EXPLODING, -1
6058 Xblank, TRUE, FALSE,
6063 Xsplash_e, FALSE, FALSE,
6064 EL_ACID_SPLASH_RIGHT, -1, -1
6067 Xsplash_w, FALSE, FALSE,
6068 EL_ACID_SPLASH_LEFT, -1, -1
6072 Xplant, TRUE, FALSE,
6073 EL_EMC_PLANT, -1, -1
6076 Yplant, FALSE, FALSE,
6077 EL_EMC_PLANT, -1, -1
6081 Xacid_1, TRUE, FALSE,
6085 Xacid_2, FALSE, FALSE,
6089 Xacid_3, FALSE, FALSE,
6093 Xacid_4, FALSE, FALSE,
6097 Xacid_5, FALSE, FALSE,
6101 Xacid_6, FALSE, FALSE,
6105 Xacid_7, FALSE, FALSE,
6109 Xacid_8, FALSE, FALSE,
6114 Xfake_acid_1, TRUE, FALSE,
6115 EL_EMC_FAKE_ACID, -1, -1
6118 Xfake_acid_2, FALSE, FALSE,
6119 EL_EMC_FAKE_ACID, -1, -1
6122 Xfake_acid_3, FALSE, FALSE,
6123 EL_EMC_FAKE_ACID, -1, -1
6126 Xfake_acid_4, FALSE, FALSE,
6127 EL_EMC_FAKE_ACID, -1, -1
6130 Xfake_acid_5, FALSE, FALSE,
6131 EL_EMC_FAKE_ACID, -1, -1
6134 Xfake_acid_6, FALSE, FALSE,
6135 EL_EMC_FAKE_ACID, -1, -1
6138 Xfake_acid_7, FALSE, FALSE,
6139 EL_EMC_FAKE_ACID, -1, -1
6142 Xfake_acid_8, FALSE, FALSE,
6143 EL_EMC_FAKE_ACID, -1, -1
6147 Xfake_acid_1_player, FALSE, FALSE,
6148 EL_EMC_FAKE_ACID, -1, -1
6151 Xfake_acid_2_player, FALSE, FALSE,
6152 EL_EMC_FAKE_ACID, -1, -1
6155 Xfake_acid_3_player, FALSE, FALSE,
6156 EL_EMC_FAKE_ACID, -1, -1
6159 Xfake_acid_4_player, FALSE, FALSE,
6160 EL_EMC_FAKE_ACID, -1, -1
6163 Xfake_acid_5_player, FALSE, FALSE,
6164 EL_EMC_FAKE_ACID, -1, -1
6167 Xfake_acid_6_player, FALSE, FALSE,
6168 EL_EMC_FAKE_ACID, -1, -1
6171 Xfake_acid_7_player, FALSE, FALSE,
6172 EL_EMC_FAKE_ACID, -1, -1
6175 Xfake_acid_8_player, FALSE, FALSE,
6176 EL_EMC_FAKE_ACID, -1, -1
6180 Xgrass, TRUE, FALSE,
6181 EL_EMC_GRASS, -1, -1
6184 Ygrass_nB, FALSE, FALSE,
6185 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6188 Ygrass_eB, FALSE, FALSE,
6189 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6192 Ygrass_sB, FALSE, FALSE,
6193 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6196 Ygrass_wB, FALSE, FALSE,
6197 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6205 Ydirt_nB, FALSE, FALSE,
6206 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6209 Ydirt_eB, FALSE, FALSE,
6210 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6213 Ydirt_sB, FALSE, FALSE,
6214 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6217 Ydirt_wB, FALSE, FALSE,
6218 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6222 Xandroid, TRUE, FALSE,
6223 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6226 Xandroid_1_n, FALSE, FALSE,
6227 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6230 Xandroid_2_n, FALSE, FALSE,
6231 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6234 Xandroid_1_e, FALSE, FALSE,
6235 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6238 Xandroid_2_e, FALSE, FALSE,
6239 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6242 Xandroid_1_w, FALSE, FALSE,
6243 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6246 Xandroid_2_w, FALSE, FALSE,
6247 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6250 Xandroid_1_s, FALSE, FALSE,
6251 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6254 Xandroid_2_s, FALSE, FALSE,
6255 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6258 Yandroid_n, FALSE, FALSE,
6259 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6262 Yandroid_nB, FALSE, TRUE,
6263 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6266 Yandroid_ne, FALSE, FALSE,
6267 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6270 Yandroid_neB, FALSE, TRUE,
6271 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6274 Yandroid_e, FALSE, FALSE,
6275 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6278 Yandroid_eB, FALSE, TRUE,
6279 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6282 Yandroid_se, FALSE, FALSE,
6283 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6286 Yandroid_seB, FALSE, TRUE,
6287 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6290 Yandroid_s, FALSE, FALSE,
6291 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6294 Yandroid_sB, FALSE, TRUE,
6295 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6298 Yandroid_sw, FALSE, FALSE,
6299 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6302 Yandroid_swB, FALSE, TRUE,
6303 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6306 Yandroid_w, FALSE, FALSE,
6307 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6310 Yandroid_wB, FALSE, TRUE,
6311 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6314 Yandroid_nw, FALSE, FALSE,
6315 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6318 Yandroid_nwB, FALSE, TRUE,
6319 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6323 Xeater_n, TRUE, FALSE,
6324 EL_YAMYAM_UP, -1, -1
6327 Xeater_e, TRUE, FALSE,
6328 EL_YAMYAM_RIGHT, -1, -1
6331 Xeater_w, TRUE, FALSE,
6332 EL_YAMYAM_LEFT, -1, -1
6335 Xeater_s, TRUE, FALSE,
6336 EL_YAMYAM_DOWN, -1, -1
6339 Yeater_n, FALSE, FALSE,
6340 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6343 Yeater_nB, FALSE, TRUE,
6344 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6347 Yeater_e, FALSE, FALSE,
6348 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6351 Yeater_eB, FALSE, TRUE,
6352 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6355 Yeater_s, FALSE, FALSE,
6356 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6359 Yeater_sB, FALSE, TRUE,
6360 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6363 Yeater_w, FALSE, FALSE,
6364 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6367 Yeater_wB, FALSE, TRUE,
6368 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6371 Yeater_stone, FALSE, FALSE,
6372 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6375 Yeater_spring, FALSE, FALSE,
6376 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6380 Xalien, TRUE, FALSE,
6384 Xalien_pause, FALSE, FALSE,
6388 Yalien_n, FALSE, FALSE,
6389 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6392 Yalien_nB, FALSE, TRUE,
6393 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6396 Yalien_e, FALSE, FALSE,
6397 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6400 Yalien_eB, FALSE, TRUE,
6401 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6404 Yalien_s, FALSE, FALSE,
6405 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6408 Yalien_sB, FALSE, TRUE,
6409 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6412 Yalien_w, FALSE, FALSE,
6413 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6416 Yalien_wB, FALSE, TRUE,
6417 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6420 Yalien_stone, FALSE, FALSE,
6421 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6424 Yalien_spring, FALSE, FALSE,
6425 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6429 Xbug_1_n, TRUE, FALSE,
6433 Xbug_1_e, TRUE, FALSE,
6434 EL_BUG_RIGHT, -1, -1
6437 Xbug_1_s, TRUE, FALSE,
6441 Xbug_1_w, TRUE, FALSE,
6445 Xbug_2_n, FALSE, FALSE,
6449 Xbug_2_e, FALSE, FALSE,
6450 EL_BUG_RIGHT, -1, -1
6453 Xbug_2_s, FALSE, FALSE,
6457 Xbug_2_w, FALSE, FALSE,
6461 Ybug_n, FALSE, FALSE,
6462 EL_BUG, ACTION_MOVING, MV_BIT_UP
6465 Ybug_nB, FALSE, TRUE,
6466 EL_BUG, ACTION_MOVING, MV_BIT_UP
6469 Ybug_e, FALSE, FALSE,
6470 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6473 Ybug_eB, FALSE, TRUE,
6474 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6477 Ybug_s, FALSE, FALSE,
6478 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6481 Ybug_sB, FALSE, TRUE,
6482 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6485 Ybug_w, FALSE, FALSE,
6486 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6489 Ybug_wB, FALSE, TRUE,
6490 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6493 Ybug_w_n, FALSE, FALSE,
6494 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6497 Ybug_n_e, FALSE, FALSE,
6498 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6501 Ybug_e_s, FALSE, FALSE,
6502 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6505 Ybug_s_w, FALSE, FALSE,
6506 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6509 Ybug_e_n, FALSE, FALSE,
6510 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6513 Ybug_s_e, FALSE, FALSE,
6514 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6517 Ybug_w_s, FALSE, FALSE,
6518 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6521 Ybug_n_w, FALSE, FALSE,
6522 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6525 Ybug_stone, FALSE, FALSE,
6526 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6529 Ybug_spring, FALSE, FALSE,
6530 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6534 Xtank_1_n, TRUE, FALSE,
6535 EL_SPACESHIP_UP, -1, -1
6538 Xtank_1_e, TRUE, FALSE,
6539 EL_SPACESHIP_RIGHT, -1, -1
6542 Xtank_1_s, TRUE, FALSE,
6543 EL_SPACESHIP_DOWN, -1, -1
6546 Xtank_1_w, TRUE, FALSE,
6547 EL_SPACESHIP_LEFT, -1, -1
6550 Xtank_2_n, FALSE, FALSE,
6551 EL_SPACESHIP_UP, -1, -1
6554 Xtank_2_e, FALSE, FALSE,
6555 EL_SPACESHIP_RIGHT, -1, -1
6558 Xtank_2_s, FALSE, FALSE,
6559 EL_SPACESHIP_DOWN, -1, -1
6562 Xtank_2_w, FALSE, FALSE,
6563 EL_SPACESHIP_LEFT, -1, -1
6566 Ytank_n, FALSE, FALSE,
6567 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6570 Ytank_nB, FALSE, TRUE,
6571 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6574 Ytank_e, FALSE, FALSE,
6575 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6578 Ytank_eB, FALSE, TRUE,
6579 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6582 Ytank_s, FALSE, FALSE,
6583 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6586 Ytank_sB, FALSE, TRUE,
6587 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6590 Ytank_w, FALSE, FALSE,
6591 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6594 Ytank_wB, FALSE, TRUE,
6595 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6598 Ytank_w_n, FALSE, FALSE,
6599 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6602 Ytank_n_e, FALSE, FALSE,
6603 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6606 Ytank_e_s, FALSE, FALSE,
6607 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6610 Ytank_s_w, FALSE, FALSE,
6611 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6614 Ytank_e_n, FALSE, FALSE,
6615 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6618 Ytank_s_e, FALSE, FALSE,
6619 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6622 Ytank_w_s, FALSE, FALSE,
6623 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6626 Ytank_n_w, FALSE, FALSE,
6627 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6630 Ytank_stone, FALSE, FALSE,
6631 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6634 Ytank_spring, FALSE, FALSE,
6635 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6639 Xemerald, TRUE, FALSE,
6643 Xemerald_pause, FALSE, FALSE,
6647 Xemerald_fall, FALSE, FALSE,
6651 Xemerald_shine, FALSE, FALSE,
6652 EL_EMERALD, ACTION_TWINKLING, -1
6655 Yemerald_s, FALSE, FALSE,
6656 EL_EMERALD, ACTION_FALLING, -1
6659 Yemerald_sB, FALSE, TRUE,
6660 EL_EMERALD, ACTION_FALLING, -1
6663 Yemerald_e, FALSE, FALSE,
6664 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6667 Yemerald_eB, FALSE, TRUE,
6668 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6671 Yemerald_w, FALSE, FALSE,
6672 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6675 Yemerald_wB, FALSE, TRUE,
6676 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6679 Yemerald_blank, FALSE, FALSE,
6680 EL_EMERALD, ACTION_COLLECTING, -1
6684 Xdiamond, TRUE, FALSE,
6688 Xdiamond_pause, FALSE, FALSE,
6692 Xdiamond_fall, FALSE, FALSE,
6696 Xdiamond_shine, FALSE, FALSE,
6697 EL_DIAMOND, ACTION_TWINKLING, -1
6700 Ydiamond_s, FALSE, FALSE,
6701 EL_DIAMOND, ACTION_FALLING, -1
6704 Ydiamond_sB, FALSE, TRUE,
6705 EL_DIAMOND, ACTION_FALLING, -1
6708 Ydiamond_e, FALSE, FALSE,
6709 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6712 Ydiamond_eB, FALSE, TRUE,
6713 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6716 Ydiamond_w, FALSE, FALSE,
6717 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6720 Ydiamond_wB, FALSE, TRUE,
6721 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6724 Ydiamond_blank, FALSE, FALSE,
6725 EL_DIAMOND, ACTION_COLLECTING, -1
6728 Ydiamond_stone, FALSE, FALSE,
6729 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6733 Xstone, TRUE, FALSE,
6737 Xstone_pause, FALSE, FALSE,
6741 Xstone_fall, FALSE, FALSE,
6745 Ystone_s, FALSE, FALSE,
6746 EL_ROCK, ACTION_FALLING, -1
6749 Ystone_sB, FALSE, TRUE,
6750 EL_ROCK, ACTION_FALLING, -1
6753 Ystone_e, FALSE, FALSE,
6754 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6757 Ystone_eB, FALSE, TRUE,
6758 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6761 Ystone_w, FALSE, FALSE,
6762 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6765 Ystone_wB, FALSE, TRUE,
6766 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6774 Xbomb_pause, FALSE, FALSE,
6778 Xbomb_fall, FALSE, FALSE,
6782 Ybomb_s, FALSE, FALSE,
6783 EL_BOMB, ACTION_FALLING, -1
6786 Ybomb_sB, FALSE, TRUE,
6787 EL_BOMB, ACTION_FALLING, -1
6790 Ybomb_e, FALSE, FALSE,
6791 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6794 Ybomb_eB, FALSE, TRUE,
6795 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6798 Ybomb_w, FALSE, FALSE,
6799 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6802 Ybomb_wB, FALSE, TRUE,
6803 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6806 Ybomb_blank, FALSE, FALSE,
6807 EL_BOMB, ACTION_ACTIVATING, -1
6815 Xnut_pause, FALSE, FALSE,
6819 Xnut_fall, FALSE, FALSE,
6823 Ynut_s, FALSE, FALSE,
6824 EL_NUT, ACTION_FALLING, -1
6827 Ynut_sB, FALSE, TRUE,
6828 EL_NUT, ACTION_FALLING, -1
6831 Ynut_e, FALSE, FALSE,
6832 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6835 Ynut_eB, FALSE, TRUE,
6836 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6839 Ynut_w, FALSE, FALSE,
6840 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6843 Ynut_wB, FALSE, TRUE,
6844 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6847 Ynut_stone, FALSE, FALSE,
6848 EL_NUT, ACTION_BREAKING, -1
6852 Xspring, TRUE, FALSE,
6856 Xspring_pause, FALSE, FALSE,
6860 Xspring_e, TRUE, FALSE,
6861 EL_SPRING_RIGHT, -1, -1
6864 Xspring_w, TRUE, FALSE,
6865 EL_SPRING_LEFT, -1, -1
6868 Xspring_fall, FALSE, FALSE,
6872 Yspring_s, FALSE, FALSE,
6873 EL_SPRING, ACTION_FALLING, -1
6876 Yspring_sB, FALSE, TRUE,
6877 EL_SPRING, ACTION_FALLING, -1
6880 Yspring_e, FALSE, FALSE,
6881 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6884 Yspring_eB, FALSE, TRUE,
6885 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6888 Yspring_w, FALSE, FALSE,
6889 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6892 Yspring_wB, FALSE, TRUE,
6893 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6896 Yspring_alien_e, FALSE, FALSE,
6897 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6900 Yspring_alien_eB, FALSE, TRUE,
6901 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6904 Yspring_alien_w, FALSE, FALSE,
6905 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6908 Yspring_alien_wB, FALSE, TRUE,
6909 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6913 Xpush_emerald_e, FALSE, FALSE,
6914 EL_EMERALD, -1, MV_BIT_RIGHT
6917 Xpush_emerald_w, FALSE, FALSE,
6918 EL_EMERALD, -1, MV_BIT_LEFT
6921 Xpush_diamond_e, FALSE, FALSE,
6922 EL_DIAMOND, -1, MV_BIT_RIGHT
6925 Xpush_diamond_w, FALSE, FALSE,
6926 EL_DIAMOND, -1, MV_BIT_LEFT
6929 Xpush_stone_e, FALSE, FALSE,
6930 EL_ROCK, -1, MV_BIT_RIGHT
6933 Xpush_stone_w, FALSE, FALSE,
6934 EL_ROCK, -1, MV_BIT_LEFT
6937 Xpush_bomb_e, FALSE, FALSE,
6938 EL_BOMB, -1, MV_BIT_RIGHT
6941 Xpush_bomb_w, FALSE, FALSE,
6942 EL_BOMB, -1, MV_BIT_LEFT
6945 Xpush_nut_e, FALSE, FALSE,
6946 EL_NUT, -1, MV_BIT_RIGHT
6949 Xpush_nut_w, FALSE, FALSE,
6950 EL_NUT, -1, MV_BIT_LEFT
6953 Xpush_spring_e, FALSE, FALSE,
6954 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6957 Xpush_spring_w, FALSE, FALSE,
6958 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6962 Xdynamite, TRUE, FALSE,
6963 EL_EM_DYNAMITE, -1, -1
6966 Ydynamite_blank, FALSE, FALSE,
6967 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6970 Xdynamite_1, TRUE, FALSE,
6971 EL_EM_DYNAMITE_ACTIVE, -1, -1
6974 Xdynamite_2, FALSE, FALSE,
6975 EL_EM_DYNAMITE_ACTIVE, -1, -1
6978 Xdynamite_3, FALSE, FALSE,
6979 EL_EM_DYNAMITE_ACTIVE, -1, -1
6982 Xdynamite_4, FALSE, FALSE,
6983 EL_EM_DYNAMITE_ACTIVE, -1, -1
6987 Xkey_1, TRUE, FALSE,
6991 Xkey_2, TRUE, FALSE,
6995 Xkey_3, TRUE, FALSE,
6999 Xkey_4, TRUE, FALSE,
7003 Xkey_5, TRUE, FALSE,
7004 EL_EMC_KEY_5, -1, -1
7007 Xkey_6, TRUE, FALSE,
7008 EL_EMC_KEY_6, -1, -1
7011 Xkey_7, TRUE, FALSE,
7012 EL_EMC_KEY_7, -1, -1
7015 Xkey_8, TRUE, FALSE,
7016 EL_EMC_KEY_8, -1, -1
7020 Xdoor_1, TRUE, FALSE,
7021 EL_EM_GATE_1, -1, -1
7024 Xdoor_2, TRUE, FALSE,
7025 EL_EM_GATE_2, -1, -1
7028 Xdoor_3, TRUE, FALSE,
7029 EL_EM_GATE_3, -1, -1
7032 Xdoor_4, TRUE, FALSE,
7033 EL_EM_GATE_4, -1, -1
7036 Xdoor_5, TRUE, FALSE,
7037 EL_EMC_GATE_5, -1, -1
7040 Xdoor_6, TRUE, FALSE,
7041 EL_EMC_GATE_6, -1, -1
7044 Xdoor_7, TRUE, FALSE,
7045 EL_EMC_GATE_7, -1, -1
7048 Xdoor_8, TRUE, FALSE,
7049 EL_EMC_GATE_8, -1, -1
7053 Xfake_door_1, TRUE, FALSE,
7054 EL_EM_GATE_1_GRAY, -1, -1
7057 Xfake_door_2, TRUE, FALSE,
7058 EL_EM_GATE_2_GRAY, -1, -1
7061 Xfake_door_3, TRUE, FALSE,
7062 EL_EM_GATE_3_GRAY, -1, -1
7065 Xfake_door_4, TRUE, FALSE,
7066 EL_EM_GATE_4_GRAY, -1, -1
7069 Xfake_door_5, TRUE, FALSE,
7070 EL_EMC_GATE_5_GRAY, -1, -1
7073 Xfake_door_6, TRUE, FALSE,
7074 EL_EMC_GATE_6_GRAY, -1, -1
7077 Xfake_door_7, TRUE, FALSE,
7078 EL_EMC_GATE_7_GRAY, -1, -1
7081 Xfake_door_8, TRUE, FALSE,
7082 EL_EMC_GATE_8_GRAY, -1, -1
7086 Xballoon, TRUE, FALSE,
7090 Yballoon_n, FALSE, FALSE,
7091 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7094 Yballoon_nB, FALSE, TRUE,
7095 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7098 Yballoon_e, FALSE, FALSE,
7099 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7102 Yballoon_eB, FALSE, TRUE,
7103 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7106 Yballoon_s, FALSE, FALSE,
7107 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7110 Yballoon_sB, FALSE, TRUE,
7111 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7114 Yballoon_w, FALSE, FALSE,
7115 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7118 Yballoon_wB, FALSE, TRUE,
7119 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7123 Xball_1, TRUE, FALSE,
7124 EL_EMC_MAGIC_BALL, -1, -1
7127 Yball_1, FALSE, FALSE,
7128 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7131 Xball_2, FALSE, FALSE,
7132 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7135 Yball_2, FALSE, FALSE,
7136 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7139 Yball_blank, FALSE, FALSE,
7140 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7144 Xamoeba_1, TRUE, FALSE,
7145 EL_AMOEBA_DRY, ACTION_OTHER, -1
7148 Xamoeba_2, FALSE, FALSE,
7149 EL_AMOEBA_DRY, ACTION_OTHER, -1
7152 Xamoeba_3, FALSE, FALSE,
7153 EL_AMOEBA_DRY, ACTION_OTHER, -1
7156 Xamoeba_4, FALSE, FALSE,
7157 EL_AMOEBA_DRY, ACTION_OTHER, -1
7160 Xamoeba_5, TRUE, FALSE,
7161 EL_AMOEBA_WET, ACTION_OTHER, -1
7164 Xamoeba_6, FALSE, FALSE,
7165 EL_AMOEBA_WET, ACTION_OTHER, -1
7168 Xamoeba_7, FALSE, FALSE,
7169 EL_AMOEBA_WET, ACTION_OTHER, -1
7172 Xamoeba_8, FALSE, FALSE,
7173 EL_AMOEBA_WET, ACTION_OTHER, -1
7178 EL_AMOEBA_DROP, ACTION_GROWING, -1
7181 Xdrip_fall, FALSE, FALSE,
7182 EL_AMOEBA_DROP, -1, -1
7185 Xdrip_stretch, FALSE, FALSE,
7186 EL_AMOEBA_DROP, ACTION_FALLING, -1
7189 Xdrip_stretchB, FALSE, TRUE,
7190 EL_AMOEBA_DROP, ACTION_FALLING, -1
7193 Ydrip_1_s, FALSE, FALSE,
7194 EL_AMOEBA_DROP, ACTION_FALLING, -1
7197 Ydrip_1_sB, FALSE, TRUE,
7198 EL_AMOEBA_DROP, ACTION_FALLING, -1
7201 Ydrip_2_s, FALSE, FALSE,
7202 EL_AMOEBA_DROP, ACTION_FALLING, -1
7205 Ydrip_2_sB, FALSE, TRUE,
7206 EL_AMOEBA_DROP, ACTION_FALLING, -1
7210 Xwonderwall, TRUE, FALSE,
7211 EL_MAGIC_WALL, -1, -1
7214 Ywonderwall, FALSE, FALSE,
7215 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7219 Xwheel, TRUE, FALSE,
7220 EL_ROBOT_WHEEL, -1, -1
7223 Ywheel, FALSE, FALSE,
7224 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7228 Xswitch, TRUE, FALSE,
7229 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7232 Yswitch, FALSE, FALSE,
7233 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7237 Xbumper, TRUE, FALSE,
7238 EL_EMC_SPRING_BUMPER, -1, -1
7241 Ybumper, FALSE, FALSE,
7242 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7246 Xacid_nw, TRUE, FALSE,
7247 EL_ACID_POOL_TOPLEFT, -1, -1
7250 Xacid_ne, TRUE, FALSE,
7251 EL_ACID_POOL_TOPRIGHT, -1, -1
7254 Xacid_sw, TRUE, FALSE,
7255 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7258 Xacid_s, TRUE, FALSE,
7259 EL_ACID_POOL_BOTTOM, -1, -1
7262 Xacid_se, TRUE, FALSE,
7263 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7267 Xfake_blank, TRUE, FALSE,
7268 EL_INVISIBLE_WALL, -1, -1
7271 Yfake_blank, FALSE, FALSE,
7272 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7276 Xfake_grass, TRUE, FALSE,
7277 EL_EMC_FAKE_GRASS, -1, -1
7280 Yfake_grass, FALSE, FALSE,
7281 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7285 Xfake_amoeba, TRUE, FALSE,
7286 EL_EMC_DRIPPER, -1, -1
7289 Yfake_amoeba, FALSE, FALSE,
7290 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7294 Xlenses, TRUE, FALSE,
7295 EL_EMC_LENSES, -1, -1
7299 Xmagnify, TRUE, FALSE,
7300 EL_EMC_MAGNIFIER, -1, -1
7305 EL_QUICKSAND_EMPTY, -1, -1
7308 Xsand_stone, TRUE, FALSE,
7309 EL_QUICKSAND_FULL, -1, -1
7312 Xsand_stonein_1, FALSE, TRUE,
7313 EL_ROCK, ACTION_FILLING, -1
7316 Xsand_stonein_2, FALSE, TRUE,
7317 EL_ROCK, ACTION_FILLING, -1
7320 Xsand_stonein_3, FALSE, TRUE,
7321 EL_ROCK, ACTION_FILLING, -1
7324 Xsand_stonein_4, FALSE, TRUE,
7325 EL_ROCK, ACTION_FILLING, -1
7328 Xsand_sandstone_1, FALSE, FALSE,
7329 EL_QUICKSAND_FILLING, -1, -1
7332 Xsand_sandstone_2, FALSE, FALSE,
7333 EL_QUICKSAND_FILLING, -1, -1
7336 Xsand_sandstone_3, FALSE, FALSE,
7337 EL_QUICKSAND_FILLING, -1, -1
7340 Xsand_sandstone_4, FALSE, FALSE,
7341 EL_QUICKSAND_FILLING, -1, -1
7344 Xsand_stonesand_1, FALSE, FALSE,
7345 EL_QUICKSAND_EMPTYING, -1, -1
7348 Xsand_stonesand_2, FALSE, FALSE,
7349 EL_QUICKSAND_EMPTYING, -1, -1
7352 Xsand_stonesand_3, FALSE, FALSE,
7353 EL_QUICKSAND_EMPTYING, -1, -1
7356 Xsand_stonesand_4, FALSE, FALSE,
7357 EL_QUICKSAND_EMPTYING, -1, -1
7360 Xsand_stoneout_1, FALSE, FALSE,
7361 EL_ROCK, ACTION_EMPTYING, -1
7364 Xsand_stoneout_2, FALSE, FALSE,
7365 EL_ROCK, ACTION_EMPTYING, -1
7368 Xsand_stonesand_quickout_1, FALSE, FALSE,
7369 EL_QUICKSAND_EMPTYING, -1, -1
7372 Xsand_stonesand_quickout_2, FALSE, FALSE,
7373 EL_QUICKSAND_EMPTYING, -1, -1
7377 Xslide_ns, TRUE, FALSE,
7378 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7381 Yslide_ns_blank, FALSE, FALSE,
7382 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7385 Xslide_ew, TRUE, FALSE,
7386 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7389 Yslide_ew_blank, FALSE, FALSE,
7390 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7394 Xwind_n, TRUE, FALSE,
7395 EL_BALLOON_SWITCH_UP, -1, -1
7398 Xwind_e, TRUE, FALSE,
7399 EL_BALLOON_SWITCH_RIGHT, -1, -1
7402 Xwind_s, TRUE, FALSE,
7403 EL_BALLOON_SWITCH_DOWN, -1, -1
7406 Xwind_w, TRUE, FALSE,
7407 EL_BALLOON_SWITCH_LEFT, -1, -1
7410 Xwind_any, TRUE, FALSE,
7411 EL_BALLOON_SWITCH_ANY, -1, -1
7414 Xwind_stop, TRUE, FALSE,
7415 EL_BALLOON_SWITCH_NONE, -1, -1
7420 EL_EM_EXIT_CLOSED, -1, -1
7423 Xexit_1, TRUE, FALSE,
7424 EL_EM_EXIT_OPEN, -1, -1
7427 Xexit_2, FALSE, FALSE,
7428 EL_EM_EXIT_OPEN, -1, -1
7431 Xexit_3, FALSE, FALSE,
7432 EL_EM_EXIT_OPEN, -1, -1
7436 Xpause, FALSE, FALSE,
7441 Xwall_1, TRUE, FALSE,
7445 Xwall_2, TRUE, FALSE,
7446 EL_EMC_WALL_14, -1, -1
7449 Xwall_3, TRUE, FALSE,
7450 EL_EMC_WALL_15, -1, -1
7453 Xwall_4, TRUE, FALSE,
7454 EL_EMC_WALL_16, -1, -1
7458 Xroundwall_1, TRUE, FALSE,
7459 EL_WALL_SLIPPERY, -1, -1
7462 Xroundwall_2, TRUE, FALSE,
7463 EL_EMC_WALL_SLIPPERY_2, -1, -1
7466 Xroundwall_3, TRUE, FALSE,
7467 EL_EMC_WALL_SLIPPERY_3, -1, -1
7470 Xroundwall_4, TRUE, FALSE,
7471 EL_EMC_WALL_SLIPPERY_4, -1, -1
7475 Xsteel_1, TRUE, FALSE,
7476 EL_STEELWALL, -1, -1
7479 Xsteel_2, TRUE, FALSE,
7480 EL_EMC_STEELWALL_2, -1, -1
7483 Xsteel_3, TRUE, FALSE,
7484 EL_EMC_STEELWALL_3, -1, -1
7487 Xsteel_4, TRUE, FALSE,
7488 EL_EMC_STEELWALL_4, -1, -1
7492 Xdecor_1, TRUE, FALSE,
7493 EL_EMC_WALL_8, -1, -1
7496 Xdecor_2, TRUE, FALSE,
7497 EL_EMC_WALL_6, -1, -1
7500 Xdecor_3, TRUE, FALSE,
7501 EL_EMC_WALL_4, -1, -1
7504 Xdecor_4, TRUE, FALSE,
7505 EL_EMC_WALL_7, -1, -1
7508 Xdecor_5, TRUE, FALSE,
7509 EL_EMC_WALL_5, -1, -1
7512 Xdecor_6, TRUE, FALSE,
7513 EL_EMC_WALL_9, -1, -1
7516 Xdecor_7, TRUE, FALSE,
7517 EL_EMC_WALL_10, -1, -1
7520 Xdecor_8, TRUE, FALSE,
7521 EL_EMC_WALL_1, -1, -1
7524 Xdecor_9, TRUE, FALSE,
7525 EL_EMC_WALL_2, -1, -1
7528 Xdecor_10, TRUE, FALSE,
7529 EL_EMC_WALL_3, -1, -1
7532 Xdecor_11, TRUE, FALSE,
7533 EL_EMC_WALL_11, -1, -1
7536 Xdecor_12, TRUE, FALSE,
7537 EL_EMC_WALL_12, -1, -1
7541 Xalpha_0, TRUE, FALSE,
7542 EL_CHAR('0'), -1, -1
7545 Xalpha_1, TRUE, FALSE,
7546 EL_CHAR('1'), -1, -1
7549 Xalpha_2, TRUE, FALSE,
7550 EL_CHAR('2'), -1, -1
7553 Xalpha_3, TRUE, FALSE,
7554 EL_CHAR('3'), -1, -1
7557 Xalpha_4, TRUE, FALSE,
7558 EL_CHAR('4'), -1, -1
7561 Xalpha_5, TRUE, FALSE,
7562 EL_CHAR('5'), -1, -1
7565 Xalpha_6, TRUE, FALSE,
7566 EL_CHAR('6'), -1, -1
7569 Xalpha_7, TRUE, FALSE,
7570 EL_CHAR('7'), -1, -1
7573 Xalpha_8, TRUE, FALSE,
7574 EL_CHAR('8'), -1, -1
7577 Xalpha_9, TRUE, FALSE,
7578 EL_CHAR('9'), -1, -1
7581 Xalpha_excla, TRUE, FALSE,
7582 EL_CHAR('!'), -1, -1
7585 Xalpha_apost, TRUE, FALSE,
7586 EL_CHAR('\''), -1, -1
7589 Xalpha_comma, TRUE, FALSE,
7590 EL_CHAR(','), -1, -1
7593 Xalpha_minus, TRUE, FALSE,
7594 EL_CHAR('-'), -1, -1
7597 Xalpha_perio, TRUE, FALSE,
7598 EL_CHAR('.'), -1, -1
7601 Xalpha_colon, TRUE, FALSE,
7602 EL_CHAR(':'), -1, -1
7605 Xalpha_quest, TRUE, FALSE,
7606 EL_CHAR('?'), -1, -1
7609 Xalpha_a, TRUE, FALSE,
7610 EL_CHAR('A'), -1, -1
7613 Xalpha_b, TRUE, FALSE,
7614 EL_CHAR('B'), -1, -1
7617 Xalpha_c, TRUE, FALSE,
7618 EL_CHAR('C'), -1, -1
7621 Xalpha_d, TRUE, FALSE,
7622 EL_CHAR('D'), -1, -1
7625 Xalpha_e, TRUE, FALSE,
7626 EL_CHAR('E'), -1, -1
7629 Xalpha_f, TRUE, FALSE,
7630 EL_CHAR('F'), -1, -1
7633 Xalpha_g, TRUE, FALSE,
7634 EL_CHAR('G'), -1, -1
7637 Xalpha_h, TRUE, FALSE,
7638 EL_CHAR('H'), -1, -1
7641 Xalpha_i, TRUE, FALSE,
7642 EL_CHAR('I'), -1, -1
7645 Xalpha_j, TRUE, FALSE,
7646 EL_CHAR('J'), -1, -1
7649 Xalpha_k, TRUE, FALSE,
7650 EL_CHAR('K'), -1, -1
7653 Xalpha_l, TRUE, FALSE,
7654 EL_CHAR('L'), -1, -1
7657 Xalpha_m, TRUE, FALSE,
7658 EL_CHAR('M'), -1, -1
7661 Xalpha_n, TRUE, FALSE,
7662 EL_CHAR('N'), -1, -1
7665 Xalpha_o, TRUE, FALSE,
7666 EL_CHAR('O'), -1, -1
7669 Xalpha_p, TRUE, FALSE,
7670 EL_CHAR('P'), -1, -1
7673 Xalpha_q, TRUE, FALSE,
7674 EL_CHAR('Q'), -1, -1
7677 Xalpha_r, TRUE, FALSE,
7678 EL_CHAR('R'), -1, -1
7681 Xalpha_s, TRUE, FALSE,
7682 EL_CHAR('S'), -1, -1
7685 Xalpha_t, TRUE, FALSE,
7686 EL_CHAR('T'), -1, -1
7689 Xalpha_u, TRUE, FALSE,
7690 EL_CHAR('U'), -1, -1
7693 Xalpha_v, TRUE, FALSE,
7694 EL_CHAR('V'), -1, -1
7697 Xalpha_w, TRUE, FALSE,
7698 EL_CHAR('W'), -1, -1
7701 Xalpha_x, TRUE, FALSE,
7702 EL_CHAR('X'), -1, -1
7705 Xalpha_y, TRUE, FALSE,
7706 EL_CHAR('Y'), -1, -1
7709 Xalpha_z, TRUE, FALSE,
7710 EL_CHAR('Z'), -1, -1
7713 Xalpha_arrow_e, TRUE, FALSE,
7714 EL_CHAR('>'), -1, -1
7717 Xalpha_arrow_w, TRUE, FALSE,
7718 EL_CHAR('<'), -1, -1
7721 Xalpha_copyr, TRUE, FALSE,
7722 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7726 Ykey_1_blank, FALSE, FALSE,
7727 EL_EM_KEY_1, ACTION_COLLECTING, -1
7730 Ykey_2_blank, FALSE, FALSE,
7731 EL_EM_KEY_2, ACTION_COLLECTING, -1
7734 Ykey_3_blank, FALSE, FALSE,
7735 EL_EM_KEY_3, ACTION_COLLECTING, -1
7738 Ykey_4_blank, FALSE, FALSE,
7739 EL_EM_KEY_4, ACTION_COLLECTING, -1
7742 Ykey_5_blank, FALSE, FALSE,
7743 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7746 Ykey_6_blank, FALSE, FALSE,
7747 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7750 Ykey_7_blank, FALSE, FALSE,
7751 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7754 Ykey_8_blank, FALSE, FALSE,
7755 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7758 Ylenses_blank, FALSE, FALSE,
7759 EL_EMC_LENSES, ACTION_COLLECTING, -1
7762 Ymagnify_blank, FALSE, FALSE,
7763 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7766 Ygrass_blank, FALSE, FALSE,
7767 EL_EMC_GRASS, ACTION_SNAPPING, -1
7770 Ydirt_blank, FALSE, FALSE,
7771 EL_SAND, ACTION_SNAPPING, -1
7780 static struct Mapping_EM_to_RND_player
7789 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7793 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7797 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7801 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7805 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7809 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7813 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7817 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7821 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7825 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7829 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7833 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7837 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7841 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7845 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7849 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7853 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7857 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7861 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7865 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7869 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7873 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7877 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7881 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7885 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7889 EL_PLAYER_1, ACTION_DEFAULT, -1,
7893 EL_PLAYER_2, ACTION_DEFAULT, -1,
7897 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7901 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7905 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7909 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7913 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7917 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7921 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7925 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7929 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7933 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7937 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7941 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7945 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7949 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7953 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7957 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7961 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7965 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7969 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7973 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7977 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7981 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7985 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7989 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7993 EL_PLAYER_3, ACTION_DEFAULT, -1,
7997 EL_PLAYER_4, ACTION_DEFAULT, -1,
8006 int map_element_RND_to_EM_cave(int element_rnd)
8008 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
8009 static boolean mapping_initialized = FALSE;
8011 if (!mapping_initialized)
8015 // return "Xalpha_quest" for all undefined elements in mapping array
8016 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8017 mapping_RND_to_EM[i] = Xalpha_quest;
8019 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8020 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
8021 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
8022 em_object_mapping_list[i].element_em;
8024 mapping_initialized = TRUE;
8027 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
8029 Warn("invalid RND level element %d", element_rnd);
8034 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
8037 int map_element_EM_to_RND_cave(int element_em_cave)
8039 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8040 static boolean mapping_initialized = FALSE;
8042 if (!mapping_initialized)
8046 // return "EL_UNKNOWN" for all undefined elements in mapping array
8047 for (i = 0; i < GAME_TILE_MAX; i++)
8048 mapping_EM_to_RND[i] = EL_UNKNOWN;
8050 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8051 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8052 em_object_mapping_list[i].element_rnd;
8054 mapping_initialized = TRUE;
8057 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8059 Warn("invalid EM cave element %d", element_em_cave);
8064 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8067 int map_element_EM_to_RND_game(int element_em_game)
8069 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8070 static boolean mapping_initialized = FALSE;
8072 if (!mapping_initialized)
8076 // return "EL_UNKNOWN" for all undefined elements in mapping array
8077 for (i = 0; i < GAME_TILE_MAX; i++)
8078 mapping_EM_to_RND[i] = EL_UNKNOWN;
8080 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8081 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8082 em_object_mapping_list[i].element_rnd;
8084 mapping_initialized = TRUE;
8087 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8089 Warn("invalid EM game element %d", element_em_game);
8094 return mapping_EM_to_RND[element_em_game];
8097 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8099 struct LevelInfo_EM *level_em = level->native_em_level;
8100 struct CAVE *cav = level_em->cav;
8103 for (i = 0; i < GAME_TILE_MAX; i++)
8104 cav->android_array[i] = Cblank;
8106 for (i = 0; i < level->num_android_clone_elements; i++)
8108 int element_rnd = level->android_clone_element[i];
8109 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8111 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8112 if (em_object_mapping_list[j].element_rnd == element_rnd)
8113 cav->android_array[em_object_mapping_list[j].element_em] =
8118 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8120 struct LevelInfo_EM *level_em = level->native_em_level;
8121 struct CAVE *cav = level_em->cav;
8124 level->num_android_clone_elements = 0;
8126 for (i = 0; i < GAME_TILE_MAX; i++)
8128 int element_em_cave = cav->android_array[i];
8130 boolean element_found = FALSE;
8132 if (element_em_cave == Cblank)
8135 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8137 for (j = 0; j < level->num_android_clone_elements; j++)
8138 if (level->android_clone_element[j] == element_rnd)
8139 element_found = TRUE;
8143 level->android_clone_element[level->num_android_clone_elements++] =
8146 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8151 if (level->num_android_clone_elements == 0)
8153 level->num_android_clone_elements = 1;
8154 level->android_clone_element[0] = EL_EMPTY;
8158 int map_direction_RND_to_EM(int direction)
8160 return (direction == MV_UP ? 0 :
8161 direction == MV_RIGHT ? 1 :
8162 direction == MV_DOWN ? 2 :
8163 direction == MV_LEFT ? 3 :
8167 int map_direction_EM_to_RND(int direction)
8169 return (direction == 0 ? MV_UP :
8170 direction == 1 ? MV_RIGHT :
8171 direction == 2 ? MV_DOWN :
8172 direction == 3 ? MV_LEFT :
8176 int map_element_RND_to_SP(int element_rnd)
8178 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8180 if (element_rnd >= EL_SP_START &&
8181 element_rnd <= EL_SP_END)
8182 element_sp = element_rnd - EL_SP_START;
8183 else if (element_rnd == EL_EMPTY_SPACE)
8185 else if (element_rnd == EL_INVISIBLE_WALL)
8191 int map_element_SP_to_RND(int element_sp)
8193 int element_rnd = EL_UNKNOWN;
8195 if (element_sp >= 0x00 &&
8197 element_rnd = EL_SP_START + element_sp;
8198 else if (element_sp == 0x28)
8199 element_rnd = EL_INVISIBLE_WALL;
8204 int map_action_SP_to_RND(int action_sp)
8208 case actActive: return ACTION_ACTIVE;
8209 case actImpact: return ACTION_IMPACT;
8210 case actExploding: return ACTION_EXPLODING;
8211 case actDigging: return ACTION_DIGGING;
8212 case actSnapping: return ACTION_SNAPPING;
8213 case actCollecting: return ACTION_COLLECTING;
8214 case actPassing: return ACTION_PASSING;
8215 case actPushing: return ACTION_PUSHING;
8216 case actDropping: return ACTION_DROPPING;
8218 default: return ACTION_DEFAULT;
8222 int map_element_RND_to_MM(int element_rnd)
8224 return (element_rnd >= EL_MM_START_1 &&
8225 element_rnd <= EL_MM_END_1 ?
8226 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8228 element_rnd >= EL_MM_START_2 &&
8229 element_rnd <= EL_MM_END_2 ?
8230 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8232 element_rnd >= EL_CHAR_START &&
8233 element_rnd <= EL_CHAR_END ?
8234 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8236 element_rnd >= EL_MM_RUNTIME_START &&
8237 element_rnd <= EL_MM_RUNTIME_END ?
8238 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8240 EL_MM_EMPTY_NATIVE);
8243 int map_element_MM_to_RND(int element_mm)
8245 return (element_mm == EL_MM_EMPTY_NATIVE ||
8246 element_mm == EL_DF_EMPTY_NATIVE ?
8249 element_mm >= EL_MM_START_1_NATIVE &&
8250 element_mm <= EL_MM_END_1_NATIVE ?
8251 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8253 element_mm >= EL_MM_START_2_NATIVE &&
8254 element_mm <= EL_MM_END_2_NATIVE ?
8255 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8257 element_mm >= EL_MM_CHAR_START_NATIVE &&
8258 element_mm <= EL_MM_CHAR_END_NATIVE ?
8259 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8261 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8262 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8263 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8268 int map_action_MM_to_RND(int action_mm)
8270 // all MM actions are defined to exactly match their RND counterparts
8274 int map_sound_MM_to_RND(int sound_mm)
8278 case SND_MM_GAME_LEVELTIME_CHARGING:
8279 return SND_GAME_LEVELTIME_CHARGING;
8281 case SND_MM_GAME_HEALTH_CHARGING:
8282 return SND_GAME_HEALTH_CHARGING;
8285 return SND_UNDEFINED;
8289 int map_mm_wall_element(int element)
8291 return (element >= EL_MM_STEEL_WALL_START &&
8292 element <= EL_MM_STEEL_WALL_END ?
8295 element >= EL_MM_WOODEN_WALL_START &&
8296 element <= EL_MM_WOODEN_WALL_END ?
8299 element >= EL_MM_ICE_WALL_START &&
8300 element <= EL_MM_ICE_WALL_END ?
8303 element >= EL_MM_AMOEBA_WALL_START &&
8304 element <= EL_MM_AMOEBA_WALL_END ?
8307 element >= EL_DF_STEEL_WALL_START &&
8308 element <= EL_DF_STEEL_WALL_END ?
8311 element >= EL_DF_WOODEN_WALL_START &&
8312 element <= EL_DF_WOODEN_WALL_END ?
8318 int map_mm_wall_element_editor(int element)
8322 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8323 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8324 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8325 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8326 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8327 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8329 default: return element;
8333 int get_next_element(int element)
8337 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8338 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8339 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8340 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8341 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8342 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8343 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8344 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8345 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8346 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8347 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8349 default: return element;
8353 int el2img_mm(int element_mm)
8355 return el2img(map_element_MM_to_RND(element_mm));
8358 int el_act2img_mm(int element_mm, int action)
8360 return el_act2img(map_element_MM_to_RND(element_mm), action);
8363 int el_act_dir2img(int element, int action, int direction)
8365 element = GFX_ELEMENT(element);
8366 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8368 // direction_graphic[][] == graphic[] for undefined direction graphics
8369 return element_info[element].direction_graphic[action][direction];
8372 static int el_act_dir2crm(int element, int action, int direction)
8374 element = GFX_ELEMENT(element);
8375 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8377 // direction_graphic[][] == graphic[] for undefined direction graphics
8378 return element_info[element].direction_crumbled[action][direction];
8381 int el_act2img(int element, int action)
8383 element = GFX_ELEMENT(element);
8385 return element_info[element].graphic[action];
8388 int el_act2crm(int element, int action)
8390 element = GFX_ELEMENT(element);
8392 return element_info[element].crumbled[action];
8395 int el_dir2img(int element, int direction)
8397 element = GFX_ELEMENT(element);
8399 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8402 int el2baseimg(int element)
8404 return element_info[element].graphic[ACTION_DEFAULT];
8407 int el2img(int element)
8409 element = GFX_ELEMENT(element);
8411 return element_info[element].graphic[ACTION_DEFAULT];
8414 int el2edimg(int element)
8416 element = GFX_ELEMENT(element);
8418 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8421 int el2preimg(int element)
8423 element = GFX_ELEMENT(element);
8425 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8428 int el2panelimg(int element)
8430 element = GFX_ELEMENT(element);
8432 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8435 int font2baseimg(int font_nr)
8437 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8440 int getBeltNrFromBeltElement(int element)
8442 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8443 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8444 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8447 int getBeltNrFromBeltActiveElement(int element)
8449 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8450 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8451 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8454 int getBeltNrFromBeltSwitchElement(int element)
8456 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8457 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8458 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8461 int getBeltDirNrFromBeltElement(int element)
8463 static int belt_base_element[4] =
8465 EL_CONVEYOR_BELT_1_LEFT,
8466 EL_CONVEYOR_BELT_2_LEFT,
8467 EL_CONVEYOR_BELT_3_LEFT,
8468 EL_CONVEYOR_BELT_4_LEFT
8471 int belt_nr = getBeltNrFromBeltElement(element);
8472 int belt_dir_nr = element - belt_base_element[belt_nr];
8474 return (belt_dir_nr % 3);
8477 int getBeltDirNrFromBeltSwitchElement(int element)
8479 static int belt_base_element[4] =
8481 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8482 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8483 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8484 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8487 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8488 int belt_dir_nr = element - belt_base_element[belt_nr];
8490 return (belt_dir_nr % 3);
8493 int getBeltDirFromBeltElement(int element)
8495 static int belt_move_dir[3] =
8502 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8504 return belt_move_dir[belt_dir_nr];
8507 int getBeltDirFromBeltSwitchElement(int element)
8509 static int belt_move_dir[3] =
8516 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8518 return belt_move_dir[belt_dir_nr];
8521 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8523 static int belt_base_element[4] =
8525 EL_CONVEYOR_BELT_1_LEFT,
8526 EL_CONVEYOR_BELT_2_LEFT,
8527 EL_CONVEYOR_BELT_3_LEFT,
8528 EL_CONVEYOR_BELT_4_LEFT
8531 return belt_base_element[belt_nr] + belt_dir_nr;
8534 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8536 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8538 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8541 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8543 static int belt_base_element[4] =
8545 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8546 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8547 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8548 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8551 return belt_base_element[belt_nr] + belt_dir_nr;
8554 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8556 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8558 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8561 boolean swapTiles_EM(boolean is_pre_emc_cave)
8563 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8566 boolean getTeamMode_EM(void)
8568 return game.team_mode || network_playing;
8571 boolean isActivePlayer_EM(int player_nr)
8573 return stored_player[player_nr].active;
8576 unsigned int InitRND(int seed)
8578 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8579 return InitEngineRandom_EM(seed);
8580 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8581 return InitEngineRandom_SP(seed);
8582 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8583 return InitEngineRandom_MM(seed);
8585 return InitEngineRandom_RND(seed);
8588 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8589 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8591 static int get_effective_element_EM(int tile, int frame_em)
8593 int element = object_mapping[tile].element_rnd;
8594 int action = object_mapping[tile].action;
8595 boolean is_backside = object_mapping[tile].is_backside;
8596 boolean action_removing = (action == ACTION_DIGGING ||
8597 action == ACTION_SNAPPING ||
8598 action == ACTION_COLLECTING);
8606 return (frame_em > 5 ? EL_EMPTY : element);
8612 else // frame_em == 7
8623 case Ydiamond_stone:
8627 case Xdrip_stretchB:
8643 case Ymagnify_blank:
8646 case Xsand_stonein_1:
8647 case Xsand_stonein_2:
8648 case Xsand_stonein_3:
8649 case Xsand_stonein_4:
8653 return (is_backside || action_removing ? EL_EMPTY : element);
8658 static boolean check_linear_animation_EM(int tile)
8662 case Xsand_stonesand_1:
8663 case Xsand_stonesand_quickout_1:
8664 case Xsand_sandstone_1:
8665 case Xsand_stonein_1:
8666 case Xsand_stoneout_1:
8694 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8695 boolean has_crumbled_graphics,
8696 int crumbled, int sync_frame)
8698 // if element can be crumbled, but certain action graphics are just empty
8699 // space (like instantly snapping sand to empty space in 1 frame), do not
8700 // treat these empty space graphics as crumbled graphics in EMC engine
8701 if (crumbled == IMG_EMPTY_SPACE)
8702 has_crumbled_graphics = FALSE;
8704 if (has_crumbled_graphics)
8706 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8707 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8708 g_crumbled->anim_delay,
8709 g_crumbled->anim_mode,
8710 g_crumbled->anim_start_frame,
8713 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8714 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8716 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8717 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8719 g_em->has_crumbled_graphics = TRUE;
8723 g_em->crumbled_bitmap = NULL;
8724 g_em->crumbled_src_x = 0;
8725 g_em->crumbled_src_y = 0;
8726 g_em->crumbled_border_size = 0;
8727 g_em->crumbled_tile_size = 0;
8729 g_em->has_crumbled_graphics = FALSE;
8734 void ResetGfxAnimation_EM(int x, int y, int tile)
8740 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8741 int tile, int frame_em, int x, int y)
8743 int action = object_mapping[tile].action;
8744 int direction = object_mapping[tile].direction;
8745 int effective_element = get_effective_element_EM(tile, frame_em);
8746 int graphic = (direction == MV_NONE ?
8747 el_act2img(effective_element, action) :
8748 el_act_dir2img(effective_element, action, direction));
8749 struct GraphicInfo *g = &graphic_info[graphic];
8751 boolean action_removing = (action == ACTION_DIGGING ||
8752 action == ACTION_SNAPPING ||
8753 action == ACTION_COLLECTING);
8754 boolean action_moving = (action == ACTION_FALLING ||
8755 action == ACTION_MOVING ||
8756 action == ACTION_PUSHING ||
8757 action == ACTION_EATING ||
8758 action == ACTION_FILLING ||
8759 action == ACTION_EMPTYING);
8760 boolean action_falling = (action == ACTION_FALLING ||
8761 action == ACTION_FILLING ||
8762 action == ACTION_EMPTYING);
8764 // special case: graphic uses "2nd movement tile" and has defined
8765 // 7 frames for movement animation (or less) => use default graphic
8766 // for last (8th) frame which ends the movement animation
8767 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8769 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8770 graphic = (direction == MV_NONE ?
8771 el_act2img(effective_element, action) :
8772 el_act_dir2img(effective_element, action, direction));
8774 g = &graphic_info[graphic];
8777 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8781 else if (action_moving)
8783 boolean is_backside = object_mapping[tile].is_backside;
8787 int direction = object_mapping[tile].direction;
8788 int move_dir = (action_falling ? MV_DOWN : direction);
8793 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8794 if (g->double_movement && frame_em == 0)
8798 if (move_dir == MV_LEFT)
8799 GfxFrame[x - 1][y] = GfxFrame[x][y];
8800 else if (move_dir == MV_RIGHT)
8801 GfxFrame[x + 1][y] = GfxFrame[x][y];
8802 else if (move_dir == MV_UP)
8803 GfxFrame[x][y - 1] = GfxFrame[x][y];
8804 else if (move_dir == MV_DOWN)
8805 GfxFrame[x][y + 1] = GfxFrame[x][y];
8812 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8813 if (tile == Xsand_stonesand_quickout_1 ||
8814 tile == Xsand_stonesand_quickout_2)
8818 if (graphic_info[graphic].anim_global_sync)
8819 sync_frame = FrameCounter;
8820 else if (graphic_info[graphic].anim_global_anim_sync)
8821 sync_frame = getGlobalAnimSyncFrame();
8822 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8823 sync_frame = GfxFrame[x][y];
8825 sync_frame = 0; // playfield border (pseudo steel)
8827 SetRandomAnimationValue(x, y);
8829 int frame = getAnimationFrame(g->anim_frames,
8832 g->anim_start_frame,
8835 g_em->unique_identifier =
8836 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8839 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8840 int tile, int frame_em, int x, int y)
8842 int action = object_mapping[tile].action;
8843 int direction = object_mapping[tile].direction;
8844 boolean is_backside = object_mapping[tile].is_backside;
8845 int effective_element = get_effective_element_EM(tile, frame_em);
8846 int effective_action = action;
8847 int graphic = (direction == MV_NONE ?
8848 el_act2img(effective_element, effective_action) :
8849 el_act_dir2img(effective_element, effective_action,
8851 int crumbled = (direction == MV_NONE ?
8852 el_act2crm(effective_element, effective_action) :
8853 el_act_dir2crm(effective_element, effective_action,
8855 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8856 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8857 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8858 struct GraphicInfo *g = &graphic_info[graphic];
8861 // special case: graphic uses "2nd movement tile" and has defined
8862 // 7 frames for movement animation (or less) => use default graphic
8863 // for last (8th) frame which ends the movement animation
8864 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8866 effective_action = ACTION_DEFAULT;
8867 graphic = (direction == MV_NONE ?
8868 el_act2img(effective_element, effective_action) :
8869 el_act_dir2img(effective_element, effective_action,
8871 crumbled = (direction == MV_NONE ?
8872 el_act2crm(effective_element, effective_action) :
8873 el_act_dir2crm(effective_element, effective_action,
8876 g = &graphic_info[graphic];
8879 if (graphic_info[graphic].anim_global_sync)
8880 sync_frame = FrameCounter;
8881 else if (graphic_info[graphic].anim_global_anim_sync)
8882 sync_frame = getGlobalAnimSyncFrame();
8883 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8884 sync_frame = GfxFrame[x][y];
8886 sync_frame = 0; // playfield border (pseudo steel)
8888 SetRandomAnimationValue(x, y);
8890 int frame = getAnimationFrame(g->anim_frames,
8893 g->anim_start_frame,
8896 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8897 g->double_movement && is_backside);
8899 // (updating the "crumbled" graphic definitions is probably not really needed,
8900 // as animations for crumbled graphics can't be longer than one EMC cycle)
8901 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8905 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8906 int player_nr, int anim, int frame_em)
8908 int element = player_mapping[player_nr][anim].element_rnd;
8909 int action = player_mapping[player_nr][anim].action;
8910 int direction = player_mapping[player_nr][anim].direction;
8911 int graphic = (direction == MV_NONE ?
8912 el_act2img(element, action) :
8913 el_act_dir2img(element, action, direction));
8914 struct GraphicInfo *g = &graphic_info[graphic];
8917 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8919 stored_player[player_nr].StepFrame = frame_em;
8921 sync_frame = stored_player[player_nr].Frame;
8923 int frame = getAnimationFrame(g->anim_frames,
8926 g->anim_start_frame,
8929 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8930 &g_em->src_x, &g_em->src_y, FALSE);
8933 void InitGraphicInfo_EM(void)
8937 // always start with reliable default values
8938 for (i = 0; i < GAME_TILE_MAX; i++)
8940 object_mapping[i].element_rnd = EL_UNKNOWN;
8941 object_mapping[i].is_backside = FALSE;
8942 object_mapping[i].action = ACTION_DEFAULT;
8943 object_mapping[i].direction = MV_NONE;
8946 // always start with reliable default values
8947 for (p = 0; p < MAX_PLAYERS; p++)
8949 for (i = 0; i < PLY_MAX; i++)
8951 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8952 player_mapping[p][i].action = ACTION_DEFAULT;
8953 player_mapping[p][i].direction = MV_NONE;
8957 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8959 int e = em_object_mapping_list[i].element_em;
8961 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8962 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8964 if (em_object_mapping_list[i].action != -1)
8965 object_mapping[e].action = em_object_mapping_list[i].action;
8967 if (em_object_mapping_list[i].direction != -1)
8968 object_mapping[e].direction =
8969 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8972 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8974 int a = em_player_mapping_list[i].action_em;
8975 int p = em_player_mapping_list[i].player_nr;
8977 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8979 if (em_player_mapping_list[i].action != -1)
8980 player_mapping[p][a].action = em_player_mapping_list[i].action;
8982 if (em_player_mapping_list[i].direction != -1)
8983 player_mapping[p][a].direction =
8984 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8987 for (i = 0; i < GAME_TILE_MAX; i++)
8989 int element = object_mapping[i].element_rnd;
8990 int action = object_mapping[i].action;
8991 int direction = object_mapping[i].direction;
8992 boolean is_backside = object_mapping[i].is_backside;
8993 boolean action_exploding = ((action == ACTION_EXPLODING ||
8994 action == ACTION_SMASHED_BY_ROCK ||
8995 action == ACTION_SMASHED_BY_SPRING) &&
8996 element != EL_DIAMOND);
8997 boolean action_active = (action == ACTION_ACTIVE);
8998 boolean action_other = (action == ACTION_OTHER);
9000 for (j = 0; j < 8; j++)
9002 int effective_element = get_effective_element_EM(i, j);
9003 int effective_action = (j < 7 ? action :
9004 i == Xdrip_stretch ? action :
9005 i == Xdrip_stretchB ? action :
9006 i == Ydrip_1_s ? action :
9007 i == Ydrip_1_sB ? action :
9008 i == Yball_1 ? action :
9009 i == Xball_2 ? action :
9010 i == Yball_2 ? action :
9011 i == Yball_blank ? action :
9012 i == Ykey_1_blank ? action :
9013 i == Ykey_2_blank ? action :
9014 i == Ykey_3_blank ? action :
9015 i == Ykey_4_blank ? action :
9016 i == Ykey_5_blank ? action :
9017 i == Ykey_6_blank ? action :
9018 i == Ykey_7_blank ? action :
9019 i == Ykey_8_blank ? action :
9020 i == Ylenses_blank ? action :
9021 i == Ymagnify_blank ? action :
9022 i == Ygrass_blank ? action :
9023 i == Ydirt_blank ? action :
9024 i == Xsand_stonein_1 ? action :
9025 i == Xsand_stonein_2 ? action :
9026 i == Xsand_stonein_3 ? action :
9027 i == Xsand_stonein_4 ? action :
9028 i == Xsand_stoneout_1 ? action :
9029 i == Xsand_stoneout_2 ? action :
9030 i == Xboom_android ? ACTION_EXPLODING :
9031 action_exploding ? ACTION_EXPLODING :
9032 action_active ? action :
9033 action_other ? action :
9035 int graphic = (el_act_dir2img(effective_element, effective_action,
9037 int crumbled = (el_act_dir2crm(effective_element, effective_action,
9039 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
9040 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
9041 boolean has_action_graphics = (graphic != base_graphic);
9042 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9043 struct GraphicInfo *g = &graphic_info[graphic];
9044 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9047 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9048 boolean special_animation = (action != ACTION_DEFAULT &&
9049 g->anim_frames == 3 &&
9050 g->anim_delay == 2 &&
9051 g->anim_mode & ANIM_LINEAR);
9052 int sync_frame = (i == Xdrip_stretch ? 7 :
9053 i == Xdrip_stretchB ? 7 :
9054 i == Ydrip_2_s ? j + 8 :
9055 i == Ydrip_2_sB ? j + 8 :
9064 i == Xfake_acid_1 ? 0 :
9065 i == Xfake_acid_2 ? 10 :
9066 i == Xfake_acid_3 ? 20 :
9067 i == Xfake_acid_4 ? 30 :
9068 i == Xfake_acid_5 ? 40 :
9069 i == Xfake_acid_6 ? 50 :
9070 i == Xfake_acid_7 ? 60 :
9071 i == Xfake_acid_8 ? 70 :
9072 i == Xfake_acid_1_player ? 0 :
9073 i == Xfake_acid_2_player ? 10 :
9074 i == Xfake_acid_3_player ? 20 :
9075 i == Xfake_acid_4_player ? 30 :
9076 i == Xfake_acid_5_player ? 40 :
9077 i == Xfake_acid_6_player ? 50 :
9078 i == Xfake_acid_7_player ? 60 :
9079 i == Xfake_acid_8_player ? 70 :
9081 i == Yball_2 ? j + 8 :
9082 i == Yball_blank ? j + 1 :
9083 i == Ykey_1_blank ? j + 1 :
9084 i == Ykey_2_blank ? j + 1 :
9085 i == Ykey_3_blank ? j + 1 :
9086 i == Ykey_4_blank ? j + 1 :
9087 i == Ykey_5_blank ? j + 1 :
9088 i == Ykey_6_blank ? j + 1 :
9089 i == Ykey_7_blank ? j + 1 :
9090 i == Ykey_8_blank ? j + 1 :
9091 i == Ylenses_blank ? j + 1 :
9092 i == Ymagnify_blank ? j + 1 :
9093 i == Ygrass_blank ? j + 1 :
9094 i == Ydirt_blank ? j + 1 :
9095 i == Xamoeba_1 ? 0 :
9096 i == Xamoeba_2 ? 1 :
9097 i == Xamoeba_3 ? 2 :
9098 i == Xamoeba_4 ? 3 :
9099 i == Xamoeba_5 ? 0 :
9100 i == Xamoeba_6 ? 1 :
9101 i == Xamoeba_7 ? 2 :
9102 i == Xamoeba_8 ? 3 :
9103 i == Xexit_2 ? j + 8 :
9104 i == Xexit_3 ? j + 16 :
9105 i == Xdynamite_1 ? 0 :
9106 i == Xdynamite_2 ? 8 :
9107 i == Xdynamite_3 ? 16 :
9108 i == Xdynamite_4 ? 24 :
9109 i == Xsand_stonein_1 ? j + 1 :
9110 i == Xsand_stonein_2 ? j + 9 :
9111 i == Xsand_stonein_3 ? j + 17 :
9112 i == Xsand_stonein_4 ? j + 25 :
9113 i == Xsand_stoneout_1 && j == 0 ? 0 :
9114 i == Xsand_stoneout_1 && j == 1 ? 0 :
9115 i == Xsand_stoneout_1 && j == 2 ? 1 :
9116 i == Xsand_stoneout_1 && j == 3 ? 2 :
9117 i == Xsand_stoneout_1 && j == 4 ? 2 :
9118 i == Xsand_stoneout_1 && j == 5 ? 3 :
9119 i == Xsand_stoneout_1 && j == 6 ? 4 :
9120 i == Xsand_stoneout_1 && j == 7 ? 4 :
9121 i == Xsand_stoneout_2 && j == 0 ? 5 :
9122 i == Xsand_stoneout_2 && j == 1 ? 6 :
9123 i == Xsand_stoneout_2 && j == 2 ? 7 :
9124 i == Xsand_stoneout_2 && j == 3 ? 8 :
9125 i == Xsand_stoneout_2 && j == 4 ? 9 :
9126 i == Xsand_stoneout_2 && j == 5 ? 11 :
9127 i == Xsand_stoneout_2 && j == 6 ? 13 :
9128 i == Xsand_stoneout_2 && j == 7 ? 15 :
9129 i == Xboom_bug && j == 1 ? 2 :
9130 i == Xboom_bug && j == 2 ? 2 :
9131 i == Xboom_bug && j == 3 ? 4 :
9132 i == Xboom_bug && j == 4 ? 4 :
9133 i == Xboom_bug && j == 5 ? 2 :
9134 i == Xboom_bug && j == 6 ? 2 :
9135 i == Xboom_bug && j == 7 ? 0 :
9136 i == Xboom_tank && j == 1 ? 2 :
9137 i == Xboom_tank && j == 2 ? 2 :
9138 i == Xboom_tank && j == 3 ? 4 :
9139 i == Xboom_tank && j == 4 ? 4 :
9140 i == Xboom_tank && j == 5 ? 2 :
9141 i == Xboom_tank && j == 6 ? 2 :
9142 i == Xboom_tank && j == 7 ? 0 :
9143 i == Xboom_android && j == 7 ? 6 :
9144 i == Xboom_1 && j == 1 ? 2 :
9145 i == Xboom_1 && j == 2 ? 2 :
9146 i == Xboom_1 && j == 3 ? 4 :
9147 i == Xboom_1 && j == 4 ? 4 :
9148 i == Xboom_1 && j == 5 ? 6 :
9149 i == Xboom_1 && j == 6 ? 6 :
9150 i == Xboom_1 && j == 7 ? 8 :
9151 i == Xboom_2 && j == 0 ? 8 :
9152 i == Xboom_2 && j == 1 ? 8 :
9153 i == Xboom_2 && j == 2 ? 10 :
9154 i == Xboom_2 && j == 3 ? 10 :
9155 i == Xboom_2 && j == 4 ? 10 :
9156 i == Xboom_2 && j == 5 ? 12 :
9157 i == Xboom_2 && j == 6 ? 12 :
9158 i == Xboom_2 && j == 7 ? 12 :
9159 special_animation && j == 4 ? 3 :
9160 effective_action != action ? 0 :
9162 int frame = getAnimationFrame(g->anim_frames,
9165 g->anim_start_frame,
9168 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9169 g->double_movement && is_backside);
9171 g_em->bitmap = src_bitmap;
9172 g_em->src_x = src_x;
9173 g_em->src_y = src_y;
9174 g_em->src_offset_x = 0;
9175 g_em->src_offset_y = 0;
9176 g_em->dst_offset_x = 0;
9177 g_em->dst_offset_y = 0;
9178 g_em->width = TILEX;
9179 g_em->height = TILEY;
9181 g_em->preserve_background = FALSE;
9183 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9186 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9187 effective_action == ACTION_MOVING ||
9188 effective_action == ACTION_PUSHING ||
9189 effective_action == ACTION_EATING)) ||
9190 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9191 effective_action == ACTION_EMPTYING)))
9194 (effective_action == ACTION_FALLING ||
9195 effective_action == ACTION_FILLING ||
9196 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9197 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9198 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9199 int num_steps = (i == Ydrip_1_s ? 16 :
9200 i == Ydrip_1_sB ? 16 :
9201 i == Ydrip_2_s ? 16 :
9202 i == Ydrip_2_sB ? 16 :
9203 i == Xsand_stonein_1 ? 32 :
9204 i == Xsand_stonein_2 ? 32 :
9205 i == Xsand_stonein_3 ? 32 :
9206 i == Xsand_stonein_4 ? 32 :
9207 i == Xsand_stoneout_1 ? 16 :
9208 i == Xsand_stoneout_2 ? 16 : 8);
9209 int cx = ABS(dx) * (TILEX / num_steps);
9210 int cy = ABS(dy) * (TILEY / num_steps);
9211 int step_frame = (i == Ydrip_2_s ? j + 8 :
9212 i == Ydrip_2_sB ? j + 8 :
9213 i == Xsand_stonein_2 ? j + 8 :
9214 i == Xsand_stonein_3 ? j + 16 :
9215 i == Xsand_stonein_4 ? j + 24 :
9216 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9217 int step = (is_backside ? step_frame : num_steps - step_frame);
9219 if (is_backside) // tile where movement starts
9221 if (dx < 0 || dy < 0)
9223 g_em->src_offset_x = cx * step;
9224 g_em->src_offset_y = cy * step;
9228 g_em->dst_offset_x = cx * step;
9229 g_em->dst_offset_y = cy * step;
9232 else // tile where movement ends
9234 if (dx < 0 || dy < 0)
9236 g_em->dst_offset_x = cx * step;
9237 g_em->dst_offset_y = cy * step;
9241 g_em->src_offset_x = cx * step;
9242 g_em->src_offset_y = cy * step;
9246 g_em->width = TILEX - cx * step;
9247 g_em->height = TILEY - cy * step;
9250 // create unique graphic identifier to decide if tile must be redrawn
9251 /* bit 31 - 16 (16 bit): EM style graphic
9252 bit 15 - 12 ( 4 bit): EM style frame
9253 bit 11 - 6 ( 6 bit): graphic width
9254 bit 5 - 0 ( 6 bit): graphic height */
9255 g_em->unique_identifier =
9256 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9260 for (i = 0; i < GAME_TILE_MAX; i++)
9262 for (j = 0; j < 8; j++)
9264 int element = object_mapping[i].element_rnd;
9265 int action = object_mapping[i].action;
9266 int direction = object_mapping[i].direction;
9267 boolean is_backside = object_mapping[i].is_backside;
9268 int graphic_action = el_act_dir2img(element, action, direction);
9269 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9271 if ((action == ACTION_SMASHED_BY_ROCK ||
9272 action == ACTION_SMASHED_BY_SPRING ||
9273 action == ACTION_EATING) &&
9274 graphic_action == graphic_default)
9276 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9277 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9278 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9279 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9282 // no separate animation for "smashed by rock" -- use rock instead
9283 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9284 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9286 g_em->bitmap = g_xx->bitmap;
9287 g_em->src_x = g_xx->src_x;
9288 g_em->src_y = g_xx->src_y;
9289 g_em->src_offset_x = g_xx->src_offset_x;
9290 g_em->src_offset_y = g_xx->src_offset_y;
9291 g_em->dst_offset_x = g_xx->dst_offset_x;
9292 g_em->dst_offset_y = g_xx->dst_offset_y;
9293 g_em->width = g_xx->width;
9294 g_em->height = g_xx->height;
9295 g_em->unique_identifier = g_xx->unique_identifier;
9298 g_em->preserve_background = TRUE;
9303 for (p = 0; p < MAX_PLAYERS; p++)
9305 for (i = 0; i < PLY_MAX; i++)
9307 int element = player_mapping[p][i].element_rnd;
9308 int action = player_mapping[p][i].action;
9309 int direction = player_mapping[p][i].direction;
9311 for (j = 0; j < 8; j++)
9313 int effective_element = element;
9314 int effective_action = action;
9315 int graphic = (direction == MV_NONE ?
9316 el_act2img(effective_element, effective_action) :
9317 el_act_dir2img(effective_element, effective_action,
9319 struct GraphicInfo *g = &graphic_info[graphic];
9320 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9324 int frame = getAnimationFrame(g->anim_frames,
9327 g->anim_start_frame,
9330 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9332 g_em->bitmap = src_bitmap;
9333 g_em->src_x = src_x;
9334 g_em->src_y = src_y;
9335 g_em->src_offset_x = 0;
9336 g_em->src_offset_y = 0;
9337 g_em->dst_offset_x = 0;
9338 g_em->dst_offset_y = 0;
9339 g_em->width = TILEX;
9340 g_em->height = TILEY;
9346 static void CheckSaveEngineSnapshot_EM(int frame,
9347 boolean any_player_moving,
9348 boolean any_player_snapping,
9349 boolean any_player_dropping)
9351 if (frame == 7 && !any_player_dropping)
9353 if (!local_player->was_waiting)
9355 if (!CheckSaveEngineSnapshotToList())
9358 local_player->was_waiting = TRUE;
9361 else if (any_player_moving || any_player_snapping || any_player_dropping)
9363 local_player->was_waiting = FALSE;
9367 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9368 boolean murphy_is_dropping)
9370 if (murphy_is_waiting)
9372 if (!local_player->was_waiting)
9374 if (!CheckSaveEngineSnapshotToList())
9377 local_player->was_waiting = TRUE;
9382 local_player->was_waiting = FALSE;
9386 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9387 boolean button_released)
9389 if (button_released)
9391 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9392 CheckSaveEngineSnapshotToList();
9394 else if (element_clicked)
9396 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9397 CheckSaveEngineSnapshotToList();
9399 game.snapshot.changed_action = TRUE;
9403 boolean CheckSingleStepMode_EM(int frame,
9404 boolean any_player_moving,
9405 boolean any_player_snapping,
9406 boolean any_player_dropping)
9408 if (tape.single_step && tape.recording && !tape.pausing)
9409 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9410 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9412 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9413 any_player_snapping, any_player_dropping);
9415 return tape.pausing;
9418 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9419 boolean murphy_is_dropping)
9421 boolean murphy_starts_dropping = FALSE;
9424 for (i = 0; i < MAX_PLAYERS; i++)
9425 if (stored_player[i].force_dropping)
9426 murphy_starts_dropping = TRUE;
9428 if (tape.single_step && tape.recording && !tape.pausing)
9429 if (murphy_is_waiting && !murphy_starts_dropping)
9430 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9432 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9435 void CheckSingleStepMode_MM(boolean element_clicked,
9436 boolean button_released)
9438 if (tape.single_step && tape.recording && !tape.pausing)
9439 if (button_released)
9440 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9442 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9445 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9446 int graphic, int sync_frame)
9448 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9450 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9453 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9455 return (IS_NEXT_FRAME(sync_frame, graphic));
9458 int getGraphicInfo_Delay(int graphic)
9460 return graphic_info[graphic].anim_delay;
9463 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9465 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9468 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9474 void PlayMenuSoundExt(int sound)
9476 if (sound == SND_UNDEFINED)
9479 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9480 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9483 if (IS_LOOP_SOUND(sound))
9484 PlaySoundLoop(sound);
9489 void PlayMenuSound(void)
9491 PlayMenuSoundExt(menu.sound[game_status]);
9494 void PlayMenuSoundStereo(int sound, int stereo_position)
9496 if (sound == SND_UNDEFINED)
9499 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9500 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9503 if (IS_LOOP_SOUND(sound))
9504 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9506 PlaySoundStereo(sound, stereo_position);
9509 void PlayMenuSoundIfLoopExt(int sound)
9511 if (sound == SND_UNDEFINED)
9514 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9515 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9518 if (IS_LOOP_SOUND(sound))
9519 PlaySoundLoop(sound);
9522 void PlayMenuSoundIfLoop(void)
9524 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9527 void PlayMenuMusicExt(int music)
9529 if (music == MUS_UNDEFINED)
9532 if (!setup.sound_music)
9535 if (IS_LOOP_MUSIC(music))
9536 PlayMusicLoop(music);
9541 void PlayMenuMusic(void)
9543 char *curr_music = getCurrentlyPlayingMusicFilename();
9544 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9546 if (!strEqual(curr_music, next_music))
9547 PlayMenuMusicExt(menu.music[game_status]);
9550 void PlayMenuSoundsAndMusic(void)
9556 static void FadeMenuSounds(void)
9561 static void FadeMenuMusic(void)
9563 char *curr_music = getCurrentlyPlayingMusicFilename();
9564 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9566 if (!strEqual(curr_music, next_music))
9570 void FadeMenuSoundsAndMusic(void)
9576 void PlaySoundActivating(void)
9579 PlaySound(SND_MENU_ITEM_ACTIVATING);
9583 void PlaySoundSelecting(void)
9586 PlaySound(SND_MENU_ITEM_SELECTING);
9590 void ToggleFullscreenIfNeeded(void)
9592 // if setup and video fullscreen state are already matching, nothing do do
9593 if (setup.fullscreen == video.fullscreen_enabled ||
9594 !video.fullscreen_available)
9597 SDLSetWindowFullscreen(setup.fullscreen);
9599 // set setup value according to successfully changed fullscreen mode
9600 setup.fullscreen = video.fullscreen_enabled;
9603 void ChangeWindowScalingIfNeeded(void)
9605 // if setup and video window scaling are already matching, nothing do do
9606 if (setup.window_scaling_percent == video.window_scaling_percent ||
9607 video.fullscreen_enabled)
9610 SDLSetWindowScaling(setup.window_scaling_percent);
9612 // set setup value according to successfully changed window scaling
9613 setup.window_scaling_percent = video.window_scaling_percent;
9616 void ChangeVsyncModeIfNeeded(void)
9618 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9619 int video_vsync_mode = video.vsync_mode;
9621 // if setup and video vsync mode are already matching, nothing do do
9622 if (setup_vsync_mode == video_vsync_mode)
9625 // if renderer is using OpenGL, vsync mode can directly be changed
9626 SDLSetScreenVsyncMode(setup.vsync_mode);
9628 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9629 if (video.vsync_mode == video_vsync_mode)
9631 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9633 // save backbuffer content which gets lost when re-creating screen
9634 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9636 // force re-creating screen and renderer to set new vsync mode
9637 video.fullscreen_enabled = !setup.fullscreen;
9639 // when creating new renderer, destroy textures linked to old renderer
9640 FreeAllImageTextures(); // needs old renderer to free the textures
9642 // re-create screen and renderer (including change of vsync mode)
9643 ChangeVideoModeIfNeeded(setup.fullscreen);
9645 // set setup value according to successfully changed fullscreen mode
9646 setup.fullscreen = video.fullscreen_enabled;
9648 // restore backbuffer content from temporary backbuffer backup bitmap
9649 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9650 FreeBitmap(tmp_backbuffer);
9652 // update visible window/screen
9653 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9655 // when changing vsync mode, re-create textures for new renderer
9656 InitImageTextures();
9659 // set setup value according to successfully changed vsync mode
9660 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9663 static void JoinRectangles(int *x, int *y, int *width, int *height,
9664 int x2, int y2, int width2, int height2)
9666 // do not join with "off-screen" rectangle
9667 if (x2 == -1 || y2 == -1)
9672 *width = MAX(*width, width2);
9673 *height = MAX(*height, height2);
9676 void SetAnimStatus(int anim_status_new)
9678 if (anim_status_new == GAME_MODE_MAIN)
9679 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9680 else if (anim_status_new == GAME_MODE_NAMES)
9681 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9682 else if (anim_status_new == GAME_MODE_SCORES)
9683 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9685 global.anim_status_next = anim_status_new;
9687 // directly set screen modes that are entered without fading
9688 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9689 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9690 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9691 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9692 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9693 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9694 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9695 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9696 global.anim_status = global.anim_status_next;
9699 void SetGameStatus(int game_status_new)
9701 if (game_status_new != game_status)
9702 game_status_last_screen = game_status;
9704 game_status = game_status_new;
9706 SetAnimStatus(game_status_new);
9709 void SetFontStatus(int game_status_new)
9711 static int last_game_status = -1;
9713 if (game_status_new != -1)
9715 // set game status for font use after storing last game status
9716 last_game_status = game_status;
9717 game_status = game_status_new;
9721 // reset game status after font use from last stored game status
9722 game_status = last_game_status;
9726 void ResetFontStatus(void)
9731 void SetLevelSetInfo(char *identifier, int level_nr)
9733 setString(&levelset.identifier, identifier);
9735 levelset.level_nr = level_nr;
9738 boolean CheckIfAllViewportsHaveChanged(void)
9740 // if game status has not changed, viewports have not changed either
9741 if (game_status == game_status_last)
9744 // check if all viewports have changed with current game status
9746 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9747 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9748 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9749 int new_real_sx = vp_playfield->x;
9750 int new_real_sy = vp_playfield->y;
9751 int new_full_sxsize = vp_playfield->width;
9752 int new_full_sysize = vp_playfield->height;
9753 int new_dx = vp_door_1->x;
9754 int new_dy = vp_door_1->y;
9755 int new_dxsize = vp_door_1->width;
9756 int new_dysize = vp_door_1->height;
9757 int new_vx = vp_door_2->x;
9758 int new_vy = vp_door_2->y;
9759 int new_vxsize = vp_door_2->width;
9760 int new_vysize = vp_door_2->height;
9762 boolean playfield_viewport_has_changed =
9763 (new_real_sx != REAL_SX ||
9764 new_real_sy != REAL_SY ||
9765 new_full_sxsize != FULL_SXSIZE ||
9766 new_full_sysize != FULL_SYSIZE);
9768 boolean door_1_viewport_has_changed =
9771 new_dxsize != DXSIZE ||
9772 new_dysize != DYSIZE);
9774 boolean door_2_viewport_has_changed =
9777 new_vxsize != VXSIZE ||
9778 new_vysize != VYSIZE ||
9779 game_status_last == GAME_MODE_EDITOR);
9781 return (playfield_viewport_has_changed &&
9782 door_1_viewport_has_changed &&
9783 door_2_viewport_has_changed);
9786 boolean CheckFadeAll(void)
9788 return (CheckIfGlobalBorderHasChanged() ||
9789 CheckIfAllViewportsHaveChanged());
9792 void ChangeViewportPropertiesIfNeeded(void)
9794 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9795 FALSE : setup.small_game_graphics);
9796 int gfx_game_mode = getGlobalGameStatus(game_status);
9797 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9799 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9800 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9801 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9802 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9803 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9804 int new_win_xsize = vp_window->width;
9805 int new_win_ysize = vp_window->height;
9806 int border_left = vp_playfield->border_left;
9807 int border_right = vp_playfield->border_right;
9808 int border_top = vp_playfield->border_top;
9809 int border_bottom = vp_playfield->border_bottom;
9810 int new_sx = vp_playfield->x + border_left;
9811 int new_sy = vp_playfield->y + border_top;
9812 int new_sxsize = vp_playfield->width - border_left - border_right;
9813 int new_sysize = vp_playfield->height - border_top - border_bottom;
9814 int new_real_sx = vp_playfield->x;
9815 int new_real_sy = vp_playfield->y;
9816 int new_full_sxsize = vp_playfield->width;
9817 int new_full_sysize = vp_playfield->height;
9818 int new_dx = vp_door_1->x;
9819 int new_dy = vp_door_1->y;
9820 int new_dxsize = vp_door_1->width;
9821 int new_dysize = vp_door_1->height;
9822 int new_vx = vp_door_2->x;
9823 int new_vy = vp_door_2->y;
9824 int new_vxsize = vp_door_2->width;
9825 int new_vysize = vp_door_2->height;
9826 int new_ex = vp_door_3->x;
9827 int new_ey = vp_door_3->y;
9828 int new_exsize = vp_door_3->width;
9829 int new_eysize = vp_door_3->height;
9830 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9831 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9832 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9833 int new_scr_fieldx = new_sxsize / tilesize;
9834 int new_scr_fieldy = new_sysize / tilesize;
9835 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9836 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9837 boolean init_gfx_buffers = FALSE;
9838 boolean init_video_buffer = FALSE;
9839 boolean init_gadgets_and_anims = FALSE;
9840 boolean init_em_graphics = FALSE;
9842 if (new_win_xsize != WIN_XSIZE ||
9843 new_win_ysize != WIN_YSIZE)
9845 WIN_XSIZE = new_win_xsize;
9846 WIN_YSIZE = new_win_ysize;
9848 init_video_buffer = TRUE;
9849 init_gfx_buffers = TRUE;
9850 init_gadgets_and_anims = TRUE;
9852 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9855 if (new_scr_fieldx != SCR_FIELDX ||
9856 new_scr_fieldy != SCR_FIELDY)
9858 // this always toggles between MAIN and GAME when using small tile size
9860 SCR_FIELDX = new_scr_fieldx;
9861 SCR_FIELDY = new_scr_fieldy;
9863 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9874 new_sxsize != SXSIZE ||
9875 new_sysize != SYSIZE ||
9876 new_dxsize != DXSIZE ||
9877 new_dysize != DYSIZE ||
9878 new_vxsize != VXSIZE ||
9879 new_vysize != VYSIZE ||
9880 new_exsize != EXSIZE ||
9881 new_eysize != EYSIZE ||
9882 new_real_sx != REAL_SX ||
9883 new_real_sy != REAL_SY ||
9884 new_full_sxsize != FULL_SXSIZE ||
9885 new_full_sysize != FULL_SYSIZE ||
9886 new_tilesize_var != TILESIZE_VAR
9889 // ------------------------------------------------------------------------
9890 // determine next fading area for changed viewport definitions
9891 // ------------------------------------------------------------------------
9893 // start with current playfield area (default fading area)
9896 FADE_SXSIZE = FULL_SXSIZE;
9897 FADE_SYSIZE = FULL_SYSIZE;
9899 // add new playfield area if position or size has changed
9900 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9901 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9903 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9904 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9907 // add current and new door 1 area if position or size has changed
9908 if (new_dx != DX || new_dy != DY ||
9909 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9911 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9912 DX, DY, DXSIZE, DYSIZE);
9913 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9914 new_dx, new_dy, new_dxsize, new_dysize);
9917 // add current and new door 2 area if position or size has changed
9918 if (new_vx != VX || new_vy != VY ||
9919 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9921 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9922 VX, VY, VXSIZE, VYSIZE);
9923 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9924 new_vx, new_vy, new_vxsize, new_vysize);
9927 // ------------------------------------------------------------------------
9928 // handle changed tile size
9929 // ------------------------------------------------------------------------
9931 if (new_tilesize_var != TILESIZE_VAR)
9933 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9935 // changing tile size invalidates scroll values of engine snapshots
9936 FreeEngineSnapshotSingle();
9938 // changing tile size requires update of graphic mapping for EM engine
9939 init_em_graphics = TRUE;
9950 SXSIZE = new_sxsize;
9951 SYSIZE = new_sysize;
9952 DXSIZE = new_dxsize;
9953 DYSIZE = new_dysize;
9954 VXSIZE = new_vxsize;
9955 VYSIZE = new_vysize;
9956 EXSIZE = new_exsize;
9957 EYSIZE = new_eysize;
9958 REAL_SX = new_real_sx;
9959 REAL_SY = new_real_sy;
9960 FULL_SXSIZE = new_full_sxsize;
9961 FULL_SYSIZE = new_full_sysize;
9962 TILESIZE_VAR = new_tilesize_var;
9964 init_gfx_buffers = TRUE;
9965 init_gadgets_and_anims = TRUE;
9967 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9968 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9971 if (init_gfx_buffers)
9973 // Debug("tools:viewport", "init_gfx_buffers");
9975 SCR_FIELDX = new_scr_fieldx_buffers;
9976 SCR_FIELDY = new_scr_fieldy_buffers;
9980 SCR_FIELDX = new_scr_fieldx;
9981 SCR_FIELDY = new_scr_fieldy;
9983 SetDrawDeactivationMask(REDRAW_NONE);
9984 SetDrawBackgroundMask(REDRAW_FIELD);
9987 if (init_video_buffer)
9989 // Debug("tools:viewport", "init_video_buffer");
9991 FreeAllImageTextures(); // needs old renderer to free the textures
9993 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9994 InitImageTextures();
9997 if (init_gadgets_and_anims)
9999 // Debug("tools:viewport", "init_gadgets_and_anims");
10002 InitGlobalAnimations();
10005 if (init_em_graphics)
10007 InitGraphicInfo_EM();
10011 void OpenURL(char *url)
10013 #if SDL_VERSION_ATLEAST(2,0,14)
10016 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
10017 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
10018 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
10022 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
10024 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
10028 // ============================================================================
10030 // ============================================================================
10032 #if defined(PLATFORM_WINDOWS)
10033 /* FILETIME of Jan 1 1970 00:00:00. */
10034 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
10037 * timezone information is stored outside the kernel so tzp isn't used anymore.
10039 * Note: this function is not for Win32 high precision timing purpose. See
10042 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
10044 FILETIME file_time;
10045 SYSTEMTIME system_time;
10046 ULARGE_INTEGER ularge;
10048 GetSystemTime(&system_time);
10049 SystemTimeToFileTime(&system_time, &file_time);
10050 ularge.LowPart = file_time.dwLowDateTime;
10051 ularge.HighPart = file_time.dwHighDateTime;
10053 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10054 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10060 static char *test_init_uuid_random_function_simple(void)
10062 static char seed_text[100];
10063 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10065 sprintf(seed_text, "%d", seed);
10070 static char *test_init_uuid_random_function_better(void)
10072 static char seed_text[100];
10073 struct timeval current_time;
10075 gettimeofday(¤t_time, NULL);
10077 prng_seed_bytes(¤t_time, sizeof(current_time));
10079 sprintf(seed_text, "%ld.%ld",
10080 (long)current_time.tv_sec,
10081 (long)current_time.tv_usec);
10086 #if defined(PLATFORM_WINDOWS)
10087 static char *test_init_uuid_random_function_better_windows(void)
10089 static char seed_text[100];
10090 struct timeval current_time;
10092 gettimeofday_windows(¤t_time, NULL);
10094 prng_seed_bytes(¤t_time, sizeof(current_time));
10096 sprintf(seed_text, "%ld.%ld",
10097 (long)current_time.tv_sec,
10098 (long)current_time.tv_usec);
10104 static unsigned int test_uuid_random_function_simple(int max)
10106 return GetSimpleRandom(max);
10109 static unsigned int test_uuid_random_function_better(int max)
10111 return (max > 0 ? prng_get_uint() % max : 0);
10114 #if defined(PLATFORM_WINDOWS)
10115 #define NUM_UUID_TESTS 3
10117 #define NUM_UUID_TESTS 2
10120 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10122 struct hashtable *hash_seeds =
10123 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10124 struct hashtable *hash_uuids =
10125 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10126 static char message[100];
10129 char *random_name = (nr == 0 ? "simple" : "better");
10130 char *random_type = (always_seed ? "always" : "only once");
10131 char *(*init_random_function)(void) =
10133 test_init_uuid_random_function_simple :
10134 test_init_uuid_random_function_better);
10135 unsigned int (*random_function)(int) =
10137 test_uuid_random_function_simple :
10138 test_uuid_random_function_better);
10141 #if defined(PLATFORM_WINDOWS)
10144 random_name = "windows";
10145 init_random_function = test_init_uuid_random_function_better_windows;
10151 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10152 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10154 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10155 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10156 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10158 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10162 // always initialize random number generator at least once
10163 init_random_function();
10165 unsigned int time_start = SDL_GetTicks();
10167 for (i = 0; i < num_uuids; i++)
10171 char *seed = getStringCopy(init_random_function());
10173 hashtable_remove(hash_seeds, seed);
10174 hashtable_insert(hash_seeds, seed, "1");
10177 char *uuid = getStringCopy(getUUIDExt(random_function));
10179 hashtable_remove(hash_uuids, uuid);
10180 hashtable_insert(hash_uuids, uuid, "1");
10183 int num_unique_seeds = hashtable_count(hash_seeds);
10184 int num_unique_uuids = hashtable_count(hash_uuids);
10186 unsigned int time_needed = SDL_GetTicks() - time_start;
10188 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10190 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10193 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10195 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10196 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10198 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10200 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10202 Request(message, REQ_CONFIRM);
10204 hashtable_destroy(hash_seeds, 0);
10205 hashtable_destroy(hash_uuids, 0);
10208 void TestGeneratingUUIDs(void)
10210 int num_uuids = 1000000;
10213 for (i = 0; i < NUM_UUID_TESTS; i++)
10214 for (j = 0; j < 2; j++)
10215 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10217 CloseAllAndExit(0);