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 void DrawSizedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3952 int graphic, int sync_frame, int tilesize,
3955 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3957 if (mask_mode == USE_MASKING)
3958 DrawSizedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame, tilesize);
3960 DrawSizedGraphicExt(dst_bitmap, x, y, graphic, frame, tilesize);
3963 static void DrawGraphicAnimation(int x, int y, int graphic)
3965 int lx = LEVELX(x), ly = LEVELY(y);
3966 int mask_mode = NO_MASKING;
3968 if (!IN_SCR_FIELD(x, y))
3971 if (game.use_masked_elements)
3973 if (Tile[lx][ly] != EL_EMPTY)
3975 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
3977 mask_mode = USE_MASKING;
3981 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3982 graphic, lx, ly, mask_mode);
3984 MarkTileDirty(x, y);
3987 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3989 int lx = LEVELX(x), ly = LEVELY(y);
3990 int mask_mode = NO_MASKING;
3992 if (!IN_SCR_FIELD(x, y))
3995 if (game.use_masked_elements)
3997 if (Tile[lx][ly] != EL_EMPTY)
3999 DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
4001 mask_mode = USE_MASKING;
4005 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
4006 graphic, lx, ly, mask_mode);
4008 MarkTileDirty(x, y);
4011 void DrawLevelGraphicAnimation(int x, int y, int graphic)
4013 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4016 void DrawLevelElementAnimation(int x, int y, int element)
4018 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4020 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
4023 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
4025 int sx = SCREENX(x), sy = SCREENY(y);
4027 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4030 if (Tile[x][y] == EL_EMPTY)
4031 graphic = el2img(GfxElementEmpty[x][y]);
4033 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4036 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
4039 DrawGraphicAnimation(sx, sy, graphic);
4042 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
4043 DrawLevelFieldCrumbled(x, y);
4045 if (GFX_CRUMBLED(Tile[x][y]))
4046 DrawLevelFieldCrumbled(x, y);
4050 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
4052 int sx = SCREENX(x), sy = SCREENY(y);
4055 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
4058 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4060 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
4063 DrawGraphicAnimation(sx, sy, graphic);
4065 if (GFX_CRUMBLED(element))
4066 DrawLevelFieldCrumbled(x, y);
4069 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
4071 if (player->use_murphy)
4073 // this works only because currently only one player can be "murphy" ...
4074 static int last_horizontal_dir = MV_LEFT;
4075 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
4077 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4078 last_horizontal_dir = move_dir;
4080 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
4082 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
4084 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
4090 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
4093 static boolean equalGraphics(int graphic1, int graphic2)
4095 struct GraphicInfo *g1 = &graphic_info[graphic1];
4096 struct GraphicInfo *g2 = &graphic_info[graphic2];
4098 return (g1->bitmap == g2->bitmap &&
4099 g1->src_x == g2->src_x &&
4100 g1->src_y == g2->src_y &&
4101 g1->anim_frames == g2->anim_frames &&
4102 g1->anim_delay == g2->anim_delay &&
4103 g1->anim_mode == g2->anim_mode);
4106 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
4110 DRAW_PLAYER_STAGE_INIT = 0,
4111 DRAW_PLAYER_STAGE_LAST_FIELD,
4112 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
4113 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4114 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4115 DRAW_PLAYER_STAGE_PLAYER,
4117 DRAW_PLAYER_STAGE_PLAYER,
4118 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
4120 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
4121 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
4123 NUM_DRAW_PLAYER_STAGES
4126 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
4128 static int static_last_player_graphic[MAX_PLAYERS];
4129 static int static_last_player_frame[MAX_PLAYERS];
4130 static boolean static_player_is_opaque[MAX_PLAYERS];
4131 static boolean draw_player[MAX_PLAYERS];
4132 int pnr = player->index_nr;
4134 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4136 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
4137 static_last_player_frame[pnr] = player->Frame;
4138 static_player_is_opaque[pnr] = FALSE;
4140 draw_player[pnr] = TRUE;
4143 if (!draw_player[pnr])
4147 if (!IN_LEV_FIELD(player->jx, player->jy))
4149 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
4150 Debug("draw:DrawPlayerExt", "This should never happen!");
4152 draw_player[pnr] = FALSE;
4158 int last_player_graphic = static_last_player_graphic[pnr];
4159 int last_player_frame = static_last_player_frame[pnr];
4160 boolean player_is_opaque = static_player_is_opaque[pnr];
4162 int jx = player->jx;
4163 int jy = player->jy;
4164 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
4165 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
4166 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
4167 int last_jx = (player->is_moving ? jx - dx : jx);
4168 int last_jy = (player->is_moving ? jy - dy : jy);
4169 int next_jx = jx + dx;
4170 int next_jy = jy + dy;
4171 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
4172 int sx = SCREENX(jx);
4173 int sy = SCREENY(jy);
4174 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
4175 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
4176 int element = Tile[jx][jy];
4177 int last_element = Tile[last_jx][last_jy];
4178 int action = (player->is_pushing ? ACTION_PUSHING :
4179 player->is_digging ? ACTION_DIGGING :
4180 player->is_collecting ? ACTION_COLLECTING :
4181 player->is_moving ? ACTION_MOVING :
4182 player->is_snapping ? ACTION_SNAPPING :
4183 player->is_dropping ? ACTION_DROPPING :
4184 player->is_waiting ? player->action_waiting :
4187 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
4189 // ------------------------------------------------------------------------
4190 // initialize drawing the player
4191 // ------------------------------------------------------------------------
4193 draw_player[pnr] = FALSE;
4195 // GfxElement[][] is set to the element the player is digging or collecting;
4196 // remove also for off-screen player if the player is not moving anymore
4197 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
4198 GfxElement[jx][jy] = EL_UNDEFINED;
4200 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
4203 if (element == EL_EXPLOSION)
4206 InitPlayerGfxAnimation(player, action, move_dir);
4208 draw_player[pnr] = TRUE;
4210 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
4212 // ------------------------------------------------------------------------
4213 // draw things in the field the player is leaving, if needed
4214 // ------------------------------------------------------------------------
4216 if (!IN_SCR_FIELD(sx, sy))
4217 draw_player[pnr] = FALSE;
4219 if (!player->is_moving)
4222 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
4224 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
4226 if (last_element == EL_DYNAMITE_ACTIVE ||
4227 last_element == EL_EM_DYNAMITE_ACTIVE ||
4228 last_element == EL_SP_DISK_RED_ACTIVE)
4229 DrawDynamite(last_jx, last_jy);
4231 DrawLevelFieldThruMask(last_jx, last_jy);
4233 else if (last_element == EL_DYNAMITE_ACTIVE ||
4234 last_element == EL_EM_DYNAMITE_ACTIVE ||
4235 last_element == EL_SP_DISK_RED_ACTIVE)
4236 DrawDynamite(last_jx, last_jy);
4238 DrawLevelField(last_jx, last_jy);
4240 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
4242 // ------------------------------------------------------------------------
4243 // draw things behind the player, if needed
4244 // ------------------------------------------------------------------------
4248 DrawLevelElement(jx, jy, Back[jx][jy]);
4253 if (IS_ACTIVE_BOMB(element))
4255 DrawLevelElement(jx, jy, EL_EMPTY);
4260 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4262 int old_element = GfxElement[jx][jy];
4263 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4264 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4266 if (GFX_CRUMBLED(old_element))
4267 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4269 DrawScreenGraphic(sx, sy, old_graphic, frame);
4271 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4272 static_player_is_opaque[pnr] = TRUE;
4276 GfxElement[jx][jy] = EL_UNDEFINED;
4278 // make sure that pushed elements are drawn with correct frame rate
4279 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4281 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4282 GfxFrame[jx][jy] = player->StepFrame;
4284 DrawLevelField(jx, jy);
4287 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4289 // ------------------------------------------------------------------------
4290 // draw things the player is pushing, if needed
4291 // ------------------------------------------------------------------------
4293 if (!player->is_pushing || !player->is_moving)
4296 if (Tile[next_jx][next_jy] == EL_EXPLOSION)
4299 int gfx_frame = GfxFrame[jx][jy];
4301 if (!IS_MOVING(jx, jy)) // push movement already finished
4303 element = Tile[next_jx][next_jy];
4304 gfx_frame = GfxFrame[next_jx][next_jy];
4307 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4308 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4309 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4311 // draw background element under pushed element (like the Sokoban field)
4312 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4314 // this allows transparent pushing animation over non-black background
4317 DrawLevelElement(jx, jy, Back[jx][jy]);
4319 DrawLevelElement(jx, jy, EL_EMPTY);
4322 if (Back[next_jx][next_jy])
4323 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4325 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4327 int px = SCREENX(jx), py = SCREENY(jy);
4328 int pxx = (TILEX - ABS(sxx)) * dx;
4329 int pyy = (TILEY - ABS(syy)) * dy;
4332 // do not draw (EM style) pushing animation when pushing is finished
4333 // (two-tile animations usually do not contain start and end frame)
4334 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4335 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4337 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4339 // masked drawing is needed for EMC style (double) movement graphics
4340 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4341 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4344 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4346 // ------------------------------------------------------------------------
4347 // draw player himself
4348 // ------------------------------------------------------------------------
4350 int graphic = getPlayerGraphic(player, move_dir);
4352 // in the case of changed player action or direction, prevent the current
4353 // animation frame from being restarted for identical animations
4354 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4355 player->Frame = last_player_frame;
4357 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4359 if (player_is_opaque)
4360 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4362 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4364 if (SHIELD_ON(player))
4366 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4367 IMG_SHIELD_NORMAL_ACTIVE);
4368 frame = getGraphicAnimationFrame(graphic, -1);
4370 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4373 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4375 // ------------------------------------------------------------------------
4376 // draw things in front of player (active dynamite or dynabombs)
4377 // ------------------------------------------------------------------------
4379 if (IS_ACTIVE_BOMB(element))
4381 int graphic = el2img(element);
4382 int frame = getGraphicAnimationFrameXY(graphic, jx, jy);
4384 if (game.emulation == EMU_SUPAPLEX)
4385 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4387 DrawGraphicThruMask(sx, sy, graphic, frame);
4390 if (player_is_moving && last_element == EL_EXPLOSION)
4392 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4393 GfxElement[last_jx][last_jy] : EL_EMPTY);
4394 int graphic = el_act2img(element, ACTION_EXPLODING);
4395 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4396 int phase = ExplodePhase[last_jx][last_jy] - 1;
4397 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4400 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4403 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4405 // ------------------------------------------------------------------------
4406 // draw elements the player is just walking/passing through/under
4407 // ------------------------------------------------------------------------
4409 if (player_is_moving)
4411 // handle the field the player is leaving ...
4412 if (IS_ACCESSIBLE_INSIDE(last_element))
4413 DrawLevelField(last_jx, last_jy);
4414 else if (IS_ACCESSIBLE_UNDER(last_element))
4415 DrawLevelFieldThruMask(last_jx, last_jy);
4418 // do not redraw accessible elements if the player is just pushing them
4419 if (!player_is_moving || !player->is_pushing)
4421 // ... and the field the player is entering
4422 if (IS_ACCESSIBLE_INSIDE(element))
4423 DrawLevelField(jx, jy);
4424 else if (IS_ACCESSIBLE_UNDER(element))
4425 DrawLevelFieldThruMask(jx, jy);
4428 MarkTileDirty(sx, sy);
4432 void DrawPlayer(struct PlayerInfo *player)
4436 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4437 DrawPlayerExt(player, i);
4440 void DrawAllPlayers(void)
4444 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4445 for (j = 0; j < MAX_PLAYERS; j++)
4446 if (stored_player[j].active)
4447 DrawPlayerExt(&stored_player[j], i);
4450 void DrawPlayerField(int x, int y)
4452 if (!IS_PLAYER(x, y))
4455 DrawPlayer(PLAYERINFO(x, y));
4458 // ----------------------------------------------------------------------------
4460 void WaitForEventToContinue(void)
4462 boolean first_wait = TRUE;
4463 boolean still_wait = TRUE;
4465 if (program.headless)
4468 // simulate releasing mouse button over last gadget, if still pressed
4470 HandleGadgets(-1, -1, 0);
4472 button_status = MB_RELEASED;
4475 ClearPlayerAction();
4481 if (NextValidEvent(&event))
4485 case EVENT_BUTTONPRESS:
4486 case EVENT_FINGERPRESS:
4490 case EVENT_BUTTONRELEASE:
4491 case EVENT_FINGERRELEASE:
4492 still_wait = first_wait;
4495 case EVENT_KEYPRESS:
4496 case SDL_CONTROLLERBUTTONDOWN:
4497 case SDL_JOYBUTTONDOWN:
4502 HandleOtherEvents(&event);
4506 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4511 if (!PendingEvent())
4516 #define MAX_REQUEST_LINES 13
4517 #define MAX_REQUEST_LINE_FONT1_LEN 7
4518 #define MAX_REQUEST_LINE_FONT2_LEN 10
4520 static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
4522 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4524 int draw_buffer_last = GetDrawtoField();
4525 int width = request.width;
4526 int height = request.height;
4530 // when showing request dialog after game ended, deactivate game panel
4531 if (game_just_ended)
4532 game.panel.active = FALSE;
4534 game.request_active = TRUE;
4536 setRequestPosition(&sx, &sy, FALSE);
4538 button_status = MB_RELEASED;
4540 request_gadget_id = -1;
4545 boolean event_handled = FALSE;
4547 if (game_just_ended)
4549 SetDrawtoField(draw_buffer_game);
4551 HandleGameActions();
4553 SetDrawtoField(DRAW_TO_BACKBUFFER);
4555 if (global.use_envelope_request)
4557 // copy current state of request area to middle of playfield area
4558 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4566 while (NextValidEvent(&event))
4568 event_handled = TRUE;
4572 case EVENT_BUTTONPRESS:
4573 case EVENT_BUTTONRELEASE:
4574 case EVENT_MOTIONNOTIFY:
4578 if (event.type == EVENT_MOTIONNOTIFY)
4583 motion_status = TRUE;
4584 mx = ((MotionEvent *) &event)->x;
4585 my = ((MotionEvent *) &event)->y;
4589 motion_status = FALSE;
4590 mx = ((ButtonEvent *) &event)->x;
4591 my = ((ButtonEvent *) &event)->y;
4592 if (event.type == EVENT_BUTTONPRESS)
4593 button_status = ((ButtonEvent *) &event)->button;
4595 button_status = MB_RELEASED;
4598 // this sets 'request_gadget_id'
4599 HandleGadgets(mx, my, button_status);
4601 switch (request_gadget_id)
4603 case TOOL_CTRL_ID_YES:
4604 case TOOL_CTRL_ID_TOUCH_YES:
4607 case TOOL_CTRL_ID_NO:
4608 case TOOL_CTRL_ID_TOUCH_NO:
4611 case TOOL_CTRL_ID_CONFIRM:
4612 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4613 result = TRUE | FALSE;
4616 case TOOL_CTRL_ID_PLAYER_1:
4619 case TOOL_CTRL_ID_PLAYER_2:
4622 case TOOL_CTRL_ID_PLAYER_3:
4625 case TOOL_CTRL_ID_PLAYER_4:
4630 // only check clickable animations if no request gadget clicked
4631 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4638 case SDL_WINDOWEVENT:
4639 HandleWindowEvent((WindowEvent *) &event);
4642 case SDL_APP_WILLENTERBACKGROUND:
4643 case SDL_APP_DIDENTERBACKGROUND:
4644 case SDL_APP_WILLENTERFOREGROUND:
4645 case SDL_APP_DIDENTERFOREGROUND:
4646 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4649 case EVENT_KEYPRESS:
4651 Key key = GetEventKey((KeyEvent *)&event);
4656 if (req_state & REQ_CONFIRM)
4665 #if defined(KSYM_Rewind)
4666 case KSYM_Rewind: // for Amazon Fire TV remote
4675 #if defined(KSYM_FastForward)
4676 case KSYM_FastForward: // for Amazon Fire TV remote
4682 HandleKeysDebug(key, KEY_PRESSED);
4686 if (req_state & REQ_PLAYER)
4688 int old_player_nr = setup.network_player_nr;
4691 result = old_player_nr + 1;
4696 result = old_player_nr + 1;
4727 case EVENT_FINGERRELEASE:
4728 case EVENT_KEYRELEASE:
4729 ClearPlayerAction();
4732 case SDL_CONTROLLERBUTTONDOWN:
4733 switch (event.cbutton.button)
4735 case SDL_CONTROLLER_BUTTON_A:
4736 case SDL_CONTROLLER_BUTTON_X:
4737 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4738 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4742 case SDL_CONTROLLER_BUTTON_B:
4743 case SDL_CONTROLLER_BUTTON_Y:
4744 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4745 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4746 case SDL_CONTROLLER_BUTTON_BACK:
4751 if (req_state & REQ_PLAYER)
4753 int old_player_nr = setup.network_player_nr;
4756 result = old_player_nr + 1;
4758 switch (event.cbutton.button)
4760 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4761 case SDL_CONTROLLER_BUTTON_Y:
4765 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4766 case SDL_CONTROLLER_BUTTON_B:
4770 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4771 case SDL_CONTROLLER_BUTTON_A:
4775 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4776 case SDL_CONTROLLER_BUTTON_X:
4787 case SDL_CONTROLLERBUTTONUP:
4788 HandleJoystickEvent(&event);
4789 ClearPlayerAction();
4793 HandleOtherEvents(&event);
4798 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4800 int joy = AnyJoystick();
4802 if (joy & JOY_BUTTON_1)
4804 else if (joy & JOY_BUTTON_2)
4807 else if (AnyJoystick())
4809 int joy = AnyJoystick();
4811 if (req_state & REQ_PLAYER)
4815 else if (joy & JOY_RIGHT)
4817 else if (joy & JOY_DOWN)
4819 else if (joy & JOY_LEFT)
4826 if (game_just_ended)
4828 if (global.use_envelope_request)
4830 // copy back current state of pressed buttons inside request area
4831 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4835 PrepareEnvelopeRequestToScreen(drawto, sx, sy, width, height);
4841 SetDrawtoField(draw_buffer_last);
4843 game.request_active = FALSE;
4848 static boolean RequestDoor(char *text, unsigned int req_state)
4850 int draw_buffer_last = GetDrawtoField();
4851 unsigned int old_door_state;
4852 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4853 int font_nr = FONT_TEXT_2;
4858 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4860 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4861 font_nr = FONT_TEXT_1;
4864 if (game_status == GAME_MODE_PLAYING)
4865 BlitScreenToBitmap(backbuffer);
4867 // disable deactivated drawing when quick-loading level tape recording
4868 if (tape.playing && tape.deactivate_display)
4869 TapeDeactivateDisplayOff(TRUE);
4871 SetMouseCursor(CURSOR_DEFAULT);
4873 // pause network game while waiting for request to answer
4874 if (network.enabled &&
4875 game_status == GAME_MODE_PLAYING &&
4876 !game.all_players_gone &&
4877 req_state & REQUEST_WAIT_FOR_INPUT)
4878 SendToServer_PausePlaying();
4880 old_door_state = GetDoorState();
4882 // simulate releasing mouse button over last gadget, if still pressed
4884 HandleGadgets(-1, -1, 0);
4888 // draw released gadget before proceeding
4891 if (old_door_state & DOOR_OPEN_1)
4893 CloseDoor(DOOR_CLOSE_1);
4895 // save old door content
4896 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4897 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4900 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4901 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4903 // clear door drawing field
4904 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4906 // force DOOR font inside door area
4907 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4909 // write text for request
4910 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4912 char text_line[max_request_line_len + 1];
4918 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4920 tc = *(text_ptr + tx);
4921 // if (!tc || tc == ' ')
4922 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4926 if ((tc == '?' || tc == '!') && tl == 0)
4936 strncpy(text_line, text_ptr, tl);
4939 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4940 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4941 text_line, font_nr);
4943 text_ptr += tl + (tc == ' ' ? 1 : 0);
4944 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4949 if (req_state & REQ_ASK)
4951 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4952 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4953 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4954 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4956 else if (req_state & REQ_CONFIRM)
4958 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4959 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4961 else if (req_state & REQ_PLAYER)
4963 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4964 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4965 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4966 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4969 // copy request gadgets to door backbuffer
4970 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4972 OpenDoor(DOOR_OPEN_1);
4974 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4976 if (game_status == GAME_MODE_PLAYING)
4978 SetPanelBackground();
4979 SetDrawBackgroundMask(REDRAW_DOOR_1);
4983 SetDrawBackgroundMask(REDRAW_FIELD);
4989 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4991 // ---------- handle request buttons ----------
4992 result = RequestHandleEvents(req_state, draw_buffer_last);
4996 if (!(req_state & REQ_STAY_OPEN))
4998 CloseDoor(DOOR_CLOSE_1);
5000 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
5001 (req_state & REQ_REOPEN))
5002 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
5007 if (game_status == GAME_MODE_PLAYING)
5009 SetPanelBackground();
5010 SetDrawBackgroundMask(REDRAW_DOOR_1);
5014 SetDrawBackgroundMask(REDRAW_FIELD);
5017 // continue network game after request
5018 if (network.enabled &&
5019 game_status == GAME_MODE_PLAYING &&
5020 !game.all_players_gone &&
5021 req_state & REQUEST_WAIT_FOR_INPUT)
5022 SendToServer_ContinuePlaying();
5024 // restore deactivated drawing when quick-loading level tape recording
5025 if (tape.playing && tape.deactivate_display)
5026 TapeDeactivateDisplayOn();
5031 static boolean RequestEnvelope(char *text, unsigned int req_state)
5033 int draw_buffer_last = GetDrawtoField();
5036 if (game_status == GAME_MODE_PLAYING)
5037 BlitScreenToBitmap(backbuffer);
5039 // disable deactivated drawing when quick-loading level tape recording
5040 if (tape.playing && tape.deactivate_display)
5041 TapeDeactivateDisplayOff(TRUE);
5043 SetMouseCursor(CURSOR_DEFAULT);
5045 // pause network game while waiting for request to answer
5046 if (network.enabled &&
5047 game_status == GAME_MODE_PLAYING &&
5048 !game.all_players_gone &&
5049 req_state & REQUEST_WAIT_FOR_INPUT)
5050 SendToServer_PausePlaying();
5052 // simulate releasing mouse button over last gadget, if still pressed
5054 HandleGadgets(-1, -1, 0);
5058 // (replace with setting corresponding request background)
5059 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5060 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5062 // clear door drawing field
5063 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5065 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5067 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5069 if (game_status == GAME_MODE_PLAYING)
5071 SetPanelBackground();
5072 SetDrawBackgroundMask(REDRAW_DOOR_1);
5076 SetDrawBackgroundMask(REDRAW_FIELD);
5082 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5084 // ---------- handle request buttons ----------
5085 result = RequestHandleEvents(req_state, draw_buffer_last);
5089 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5093 if (game_status == GAME_MODE_PLAYING)
5095 SetPanelBackground();
5096 SetDrawBackgroundMask(REDRAW_DOOR_1);
5100 SetDrawBackgroundMask(REDRAW_FIELD);
5103 // continue network game after request
5104 if (network.enabled &&
5105 game_status == GAME_MODE_PLAYING &&
5106 !game.all_players_gone &&
5107 req_state & REQUEST_WAIT_FOR_INPUT)
5108 SendToServer_ContinuePlaying();
5110 // restore deactivated drawing when quick-loading level tape recording
5111 if (tape.playing && tape.deactivate_display)
5112 TapeDeactivateDisplayOn();
5117 boolean Request(char *text, unsigned int req_state)
5119 boolean overlay_enabled = GetOverlayEnabled();
5122 game.request_active_or_moving = TRUE;
5124 SetOverlayEnabled(FALSE);
5126 if (global.use_envelope_request)
5127 result = RequestEnvelope(text, req_state);
5129 result = RequestDoor(text, req_state);
5131 SetOverlayEnabled(overlay_enabled);
5133 game.request_active_or_moving = FALSE;
5138 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5140 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5141 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5144 if (dpo1->sort_priority != dpo2->sort_priority)
5145 compare_result = dpo1->sort_priority - dpo2->sort_priority;
5147 compare_result = dpo1->nr - dpo2->nr;
5149 return compare_result;
5152 void InitGraphicCompatibilityInfo_Doors(void)
5158 struct DoorInfo *door;
5162 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
5163 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
5165 { -1, -1, -1, NULL }
5167 struct Rect door_rect_list[] =
5169 { DX, DY, DXSIZE, DYSIZE },
5170 { VX, VY, VXSIZE, VYSIZE }
5174 for (i = 0; doors[i].door_token != -1; i++)
5176 int door_token = doors[i].door_token;
5177 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5178 int part_1 = doors[i].part_1;
5179 int part_8 = doors[i].part_8;
5180 int part_2 = part_1 + 1;
5181 int part_3 = part_1 + 2;
5182 struct DoorInfo *door = doors[i].door;
5183 struct Rect *door_rect = &door_rect_list[door_index];
5184 boolean door_gfx_redefined = FALSE;
5186 // check if any door part graphic definitions have been redefined
5188 for (j = 0; door_part_controls[j].door_token != -1; j++)
5190 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5191 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5193 if (dpc->door_token == door_token && fi->redefined)
5194 door_gfx_redefined = TRUE;
5197 // check for old-style door graphic/animation modifications
5199 if (!door_gfx_redefined)
5201 if (door->anim_mode & ANIM_STATIC_PANEL)
5203 door->panel.step_xoffset = 0;
5204 door->panel.step_yoffset = 0;
5207 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5209 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5210 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5211 int num_door_steps, num_panel_steps;
5213 // remove door part graphics other than the two default wings
5215 for (j = 0; door_part_controls[j].door_token != -1; j++)
5217 struct DoorPartControlInfo *dpc = &door_part_controls[j];
5218 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5220 if (dpc->graphic >= part_3 &&
5221 dpc->graphic <= part_8)
5225 // set graphics and screen positions of the default wings
5227 g_part_1->width = door_rect->width;
5228 g_part_1->height = door_rect->height;
5229 g_part_2->width = door_rect->width;
5230 g_part_2->height = door_rect->height;
5231 g_part_2->src_x = door_rect->width;
5232 g_part_2->src_y = g_part_1->src_y;
5234 door->part_2.x = door->part_1.x;
5235 door->part_2.y = door->part_1.y;
5237 if (door->width != -1)
5239 g_part_1->width = door->width;
5240 g_part_2->width = door->width;
5242 // special treatment for graphics and screen position of right wing
5243 g_part_2->src_x += door_rect->width - door->width;
5244 door->part_2.x += door_rect->width - door->width;
5247 if (door->height != -1)
5249 g_part_1->height = door->height;
5250 g_part_2->height = door->height;
5252 // special treatment for graphics and screen position of bottom wing
5253 g_part_2->src_y += door_rect->height - door->height;
5254 door->part_2.y += door_rect->height - door->height;
5257 // set animation delays for the default wings and panels
5259 door->part_1.step_delay = door->step_delay;
5260 door->part_2.step_delay = door->step_delay;
5261 door->panel.step_delay = door->step_delay;
5263 // set animation draw order for the default wings
5265 door->part_1.sort_priority = 2; // draw left wing over ...
5266 door->part_2.sort_priority = 1; // ... right wing
5268 // set animation draw offset for the default wings
5270 if (door->anim_mode & ANIM_HORIZONTAL)
5272 door->part_1.step_xoffset = door->step_offset;
5273 door->part_1.step_yoffset = 0;
5274 door->part_2.step_xoffset = door->step_offset * -1;
5275 door->part_2.step_yoffset = 0;
5277 num_door_steps = g_part_1->width / door->step_offset;
5279 else // ANIM_VERTICAL
5281 door->part_1.step_xoffset = 0;
5282 door->part_1.step_yoffset = door->step_offset;
5283 door->part_2.step_xoffset = 0;
5284 door->part_2.step_yoffset = door->step_offset * -1;
5286 num_door_steps = g_part_1->height / door->step_offset;
5289 // set animation draw offset for the default panels
5291 if (door->step_offset > 1)
5293 num_panel_steps = 2 * door_rect->height / door->step_offset;
5294 door->panel.start_step = num_panel_steps - num_door_steps;
5295 door->panel.start_step_closing = door->panel.start_step;
5299 num_panel_steps = door_rect->height / door->step_offset;
5300 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5301 door->panel.start_step_closing = door->panel.start_step;
5302 door->panel.step_delay *= 2;
5309 void InitDoors(void)
5313 for (i = 0; door_part_controls[i].door_token != -1; i++)
5315 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5316 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5318 // initialize "start_step_opening" and "start_step_closing", if needed
5319 if (dpc->pos->start_step_opening == 0 &&
5320 dpc->pos->start_step_closing == 0)
5322 // dpc->pos->start_step_opening = dpc->pos->start_step;
5323 dpc->pos->start_step_closing = dpc->pos->start_step;
5326 // fill structure for door part draw order (sorted below)
5328 dpo->sort_priority = dpc->pos->sort_priority;
5331 // sort door part controls according to sort_priority and graphic number
5332 qsort(door_part_order, MAX_DOOR_PARTS,
5333 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5336 unsigned int OpenDoor(unsigned int door_state)
5338 if (door_state & DOOR_COPY_BACK)
5340 if (door_state & DOOR_OPEN_1)
5341 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5342 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5344 if (door_state & DOOR_OPEN_2)
5345 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5346 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5348 door_state &= ~DOOR_COPY_BACK;
5351 return MoveDoor(door_state);
5354 unsigned int CloseDoor(unsigned int door_state)
5356 unsigned int old_door_state = GetDoorState();
5358 if (!(door_state & DOOR_NO_COPY_BACK))
5360 if (old_door_state & DOOR_OPEN_1)
5361 BlitBitmap(backbuffer, bitmap_db_door_1,
5362 DX, DY, DXSIZE, DYSIZE, 0, 0);
5364 if (old_door_state & DOOR_OPEN_2)
5365 BlitBitmap(backbuffer, bitmap_db_door_2,
5366 VX, VY, VXSIZE, VYSIZE, 0, 0);
5368 door_state &= ~DOOR_NO_COPY_BACK;
5371 return MoveDoor(door_state);
5374 unsigned int GetDoorState(void)
5376 return MoveDoor(DOOR_GET_STATE);
5379 unsigned int SetDoorState(unsigned int door_state)
5381 return MoveDoor(door_state | DOOR_SET_STATE);
5384 static int euclid(int a, int b)
5386 return (b ? euclid(b, a % b) : a);
5389 unsigned int MoveDoor(unsigned int door_state)
5391 struct Rect door_rect_list[] =
5393 { DX, DY, DXSIZE, DYSIZE },
5394 { VX, VY, VXSIZE, VYSIZE }
5396 static int door1 = DOOR_CLOSE_1;
5397 static int door2 = DOOR_CLOSE_2;
5398 DelayCounter door_delay = { 0 };
5401 if (door_state == DOOR_GET_STATE)
5402 return (door1 | door2);
5404 if (door_state & DOOR_SET_STATE)
5406 if (door_state & DOOR_ACTION_1)
5407 door1 = door_state & DOOR_ACTION_1;
5408 if (door_state & DOOR_ACTION_2)
5409 door2 = door_state & DOOR_ACTION_2;
5411 return (door1 | door2);
5414 if (!(door_state & DOOR_FORCE_REDRAW))
5416 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5417 door_state &= ~DOOR_OPEN_1;
5418 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5419 door_state &= ~DOOR_CLOSE_1;
5420 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5421 door_state &= ~DOOR_OPEN_2;
5422 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5423 door_state &= ~DOOR_CLOSE_2;
5426 if (global.autoplay_leveldir)
5428 door_state |= DOOR_NO_DELAY;
5429 door_state &= ~DOOR_CLOSE_ALL;
5432 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5433 door_state |= DOOR_NO_DELAY;
5435 if (door_state & DOOR_ACTION)
5437 boolean door_panel_drawn[NUM_DOORS];
5438 boolean panel_has_doors[NUM_DOORS];
5439 boolean door_part_skip[MAX_DOOR_PARTS];
5440 boolean door_part_done[MAX_DOOR_PARTS];
5441 boolean door_part_done_all;
5442 int num_steps[MAX_DOOR_PARTS];
5443 int max_move_delay = 0; // delay for complete animations of all doors
5444 int max_step_delay = 0; // delay (ms) between two animation frames
5445 int num_move_steps = 0; // number of animation steps for all doors
5446 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5447 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5451 for (i = 0; i < NUM_DOORS; i++)
5452 panel_has_doors[i] = FALSE;
5454 for (i = 0; i < MAX_DOOR_PARTS; i++)
5456 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5457 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5458 int door_token = dpc->door_token;
5460 door_part_done[i] = FALSE;
5461 door_part_skip[i] = (!(door_state & door_token) ||
5465 for (i = 0; i < MAX_DOOR_PARTS; i++)
5467 int nr = door_part_order[i].nr;
5468 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5469 struct DoorPartPosInfo *pos = dpc->pos;
5470 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5471 int door_token = dpc->door_token;
5472 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5473 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5474 int step_xoffset = ABS(pos->step_xoffset);
5475 int step_yoffset = ABS(pos->step_yoffset);
5476 int step_delay = pos->step_delay;
5477 int current_door_state = door_state & door_token;
5478 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5479 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5480 boolean part_opening = (is_panel ? door_closing : door_opening);
5481 int start_step = (part_opening ? pos->start_step_opening :
5482 pos->start_step_closing);
5483 float move_xsize = (step_xoffset ? g->width : 0);
5484 float move_ysize = (step_yoffset ? g->height : 0);
5485 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5486 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5487 int move_steps = (move_xsteps && move_ysteps ?
5488 MIN(move_xsteps, move_ysteps) :
5489 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5490 int move_delay = move_steps * step_delay;
5492 if (door_part_skip[nr])
5495 max_move_delay = MAX(max_move_delay, move_delay);
5496 max_step_delay = (max_step_delay == 0 ? step_delay :
5497 euclid(max_step_delay, step_delay));
5498 num_steps[nr] = move_steps;
5502 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5504 panel_has_doors[door_index] = TRUE;
5508 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5510 num_move_steps = max_move_delay / max_step_delay;
5511 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5513 door_delay.value = max_step_delay;
5515 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5517 start = num_move_steps - 1;
5521 // opening door sound has priority over simultaneously closing door
5522 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5524 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5526 if (door_state & DOOR_OPEN_1)
5527 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5528 if (door_state & DOOR_OPEN_2)
5529 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5531 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5533 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5535 if (door_state & DOOR_CLOSE_1)
5536 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5537 if (door_state & DOOR_CLOSE_2)
5538 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5542 for (k = start; k < num_move_steps; k++)
5544 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5546 door_part_done_all = TRUE;
5548 for (i = 0; i < NUM_DOORS; i++)
5549 door_panel_drawn[i] = FALSE;
5551 for (i = 0; i < MAX_DOOR_PARTS; i++)
5553 int nr = door_part_order[i].nr;
5554 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5555 struct DoorPartPosInfo *pos = dpc->pos;
5556 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5557 int door_token = dpc->door_token;
5558 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5559 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5560 boolean is_panel_and_door_has_closed = FALSE;
5561 struct Rect *door_rect = &door_rect_list[door_index];
5562 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5564 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5565 int current_door_state = door_state & door_token;
5566 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5567 boolean door_closing = !door_opening;
5568 boolean part_opening = (is_panel ? door_closing : door_opening);
5569 boolean part_closing = !part_opening;
5570 int start_step = (part_opening ? pos->start_step_opening :
5571 pos->start_step_closing);
5572 int step_delay = pos->step_delay;
5573 int step_factor = step_delay / max_step_delay;
5574 int k1 = (step_factor ? k / step_factor + 1 : k);
5575 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5576 int kk = MAX(0, k2);
5579 int src_x, src_y, src_xx, src_yy;
5580 int dst_x, dst_y, dst_xx, dst_yy;
5583 if (door_part_skip[nr])
5586 if (!(door_state & door_token))
5594 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5595 int kk_door = MAX(0, k2_door);
5596 int sync_frame = kk_door * door_delay.value;
5597 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5599 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5600 &g_src_x, &g_src_y);
5605 if (!door_panel_drawn[door_index])
5607 ClearRectangle(drawto, door_rect->x, door_rect->y,
5608 door_rect->width, door_rect->height);
5610 door_panel_drawn[door_index] = TRUE;
5613 // draw opening or closing door parts
5615 if (pos->step_xoffset < 0) // door part on right side
5618 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5621 if (dst_xx + width > door_rect->width)
5622 width = door_rect->width - dst_xx;
5624 else // door part on left side
5627 dst_xx = pos->x - kk * pos->step_xoffset;
5631 src_xx = ABS(dst_xx);
5635 width = g->width - src_xx;
5637 if (width > door_rect->width)
5638 width = door_rect->width;
5640 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5643 if (pos->step_yoffset < 0) // door part on bottom side
5646 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5649 if (dst_yy + height > door_rect->height)
5650 height = door_rect->height - dst_yy;
5652 else // door part on top side
5655 dst_yy = pos->y - kk * pos->step_yoffset;
5659 src_yy = ABS(dst_yy);
5663 height = g->height - src_yy;
5666 src_x = g_src_x + src_xx;
5667 src_y = g_src_y + src_yy;
5669 dst_x = door_rect->x + dst_xx;
5670 dst_y = door_rect->y + dst_yy;
5672 is_panel_and_door_has_closed =
5675 panel_has_doors[door_index] &&
5676 k >= num_move_steps_doors_only - 1);
5678 if (width >= 0 && width <= g->width &&
5679 height >= 0 && height <= g->height &&
5680 !is_panel_and_door_has_closed)
5682 if (is_panel || !pos->draw_masked)
5683 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5686 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5690 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5692 if ((part_opening && (width < 0 || height < 0)) ||
5693 (part_closing && (width >= g->width && height >= g->height)))
5694 door_part_done[nr] = TRUE;
5696 // continue door part animations, but not panel after door has closed
5697 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5698 door_part_done_all = FALSE;
5701 if (!(door_state & DOOR_NO_DELAY))
5705 SkipUntilDelayReached(&door_delay, &k, last_frame);
5707 // prevent OS (Windows) from complaining about program not responding
5711 if (door_part_done_all)
5715 if (!(door_state & DOOR_NO_DELAY))
5717 // wait for specified door action post delay
5718 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5719 door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5720 else if (door_state & DOOR_ACTION_1)
5721 door_delay.value = door_1.post_delay;
5722 else if (door_state & DOOR_ACTION_2)
5723 door_delay.value = door_2.post_delay;
5725 while (!DelayReached(&door_delay))
5730 if (door_state & DOOR_ACTION_1)
5731 door1 = door_state & DOOR_ACTION_1;
5732 if (door_state & DOOR_ACTION_2)
5733 door2 = door_state & DOOR_ACTION_2;
5735 // draw masked border over door area
5736 DrawMaskedBorder(REDRAW_DOOR_1);
5737 DrawMaskedBorder(REDRAW_DOOR_2);
5739 ClearAutoRepeatKeyEvents();
5741 return (door1 | door2);
5744 static boolean useSpecialEditorDoor(void)
5746 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5747 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5749 // do not draw special editor door if editor border defined or redefined
5750 if (graphic_info[graphic].bitmap != NULL || redefined)
5753 // do not draw special editor door if global border defined to be empty
5754 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5757 // do not draw special editor door if viewport definitions do not match
5761 EY + EYSIZE != VY + VYSIZE)
5767 void DrawSpecialEditorDoor(void)
5769 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5770 int top_border_width = gfx1->width;
5771 int top_border_height = gfx1->height;
5772 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5773 int ex = EX - outer_border;
5774 int ey = EY - outer_border;
5775 int vy = VY - outer_border;
5776 int exsize = EXSIZE + 2 * outer_border;
5778 if (!useSpecialEditorDoor())
5781 // draw bigger level editor toolbox window
5782 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5783 top_border_width, top_border_height, ex, ey - top_border_height);
5784 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5785 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5787 redraw_mask |= REDRAW_ALL;
5790 void UndrawSpecialEditorDoor(void)
5792 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5793 int top_border_width = gfx1->width;
5794 int top_border_height = gfx1->height;
5795 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5796 int ex = EX - outer_border;
5797 int ey = EY - outer_border;
5798 int ey_top = ey - top_border_height;
5799 int exsize = EXSIZE + 2 * outer_border;
5800 int eysize = EYSIZE + 2 * outer_border;
5802 if (!useSpecialEditorDoor())
5805 // draw normal tape recorder window
5806 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5808 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5809 ex, ey_top, top_border_width, top_border_height,
5811 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5812 ex, ey, exsize, eysize, ex, ey);
5816 // if screen background is set to "[NONE]", clear editor toolbox window
5817 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5818 ClearRectangle(drawto, ex, ey, exsize, eysize);
5821 redraw_mask |= REDRAW_ALL;
5825 // ---------- new tool button stuff -------------------------------------------
5830 struct TextPosInfo *pos;
5832 boolean is_touch_button;
5834 } toolbutton_info[NUM_TOOL_BUTTONS] =
5837 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5838 TOOL_CTRL_ID_YES, FALSE, "yes"
5841 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5842 TOOL_CTRL_ID_NO, FALSE, "no"
5845 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5846 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5849 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5850 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5853 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5854 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5857 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5858 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5861 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5862 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5865 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5866 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5869 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5870 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5873 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5874 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5878 void CreateToolButtons(void)
5882 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5884 int graphic = toolbutton_info[i].graphic;
5885 struct GraphicInfo *gfx = &graphic_info[graphic];
5886 struct TextPosInfo *pos = toolbutton_info[i].pos;
5887 struct GadgetInfo *gi;
5888 Bitmap *deco_bitmap = None;
5889 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5890 unsigned int event_mask = GD_EVENT_RELEASED;
5891 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5892 int base_x = (is_touch_button ? 0 : DX);
5893 int base_y = (is_touch_button ? 0 : DY);
5894 int gd_x = gfx->src_x;
5895 int gd_y = gfx->src_y;
5896 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5897 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5902 // do not use touch buttons if overlay touch buttons are disabled
5903 if (is_touch_button && !setup.touch.overlay_buttons)
5906 if (global.use_envelope_request && !is_touch_button)
5908 setRequestPosition(&base_x, &base_y, TRUE);
5910 // check if request buttons are outside of envelope and fix, if needed
5911 if (x < 0 || x + gfx->width > request.width ||
5912 y < 0 || y + gfx->height > request.height)
5914 if (id == TOOL_CTRL_ID_YES)
5917 y = request.height - 2 * request.border_size - gfx->height;
5919 else if (id == TOOL_CTRL_ID_NO)
5921 x = request.width - 2 * request.border_size - gfx->width;
5922 y = request.height - 2 * request.border_size - gfx->height;
5924 else if (id == TOOL_CTRL_ID_CONFIRM)
5926 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5927 y = request.height - 2 * request.border_size - gfx->height;
5929 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5931 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5933 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5934 y = request.height - 2 * request.border_size - gfx->height * 2;
5936 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5937 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5942 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5945 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5947 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5948 pos->size, &deco_bitmap, &deco_x, &deco_y);
5949 deco_xpos = (gfx->width - pos->size) / 2;
5950 deco_ypos = (gfx->height - pos->size) / 2;
5953 gi = CreateGadget(GDI_CUSTOM_ID, id,
5954 GDI_IMAGE_ID, graphic,
5955 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5958 GDI_WIDTH, gfx->width,
5959 GDI_HEIGHT, gfx->height,
5960 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5961 GDI_STATE, GD_BUTTON_UNPRESSED,
5962 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5963 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5964 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5965 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5966 GDI_DECORATION_SIZE, pos->size, pos->size,
5967 GDI_DECORATION_SHIFTING, 1, 1,
5968 GDI_DIRECT_DRAW, FALSE,
5969 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5970 GDI_EVENT_MASK, event_mask,
5971 GDI_CALLBACK_ACTION, HandleToolButtons,
5975 Fail("cannot create gadget");
5977 tool_gadget[id] = gi;
5981 void FreeToolButtons(void)
5985 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5986 FreeGadget(tool_gadget[i]);
5989 static void UnmapToolButtons(void)
5993 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5994 UnmapGadget(tool_gadget[i]);
5997 static void HandleToolButtons(struct GadgetInfo *gi)
5999 request_gadget_id = gi->custom_id;
6002 static struct Mapping_EM_to_RND_object
6005 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
6006 boolean is_backside; // backside of moving element
6012 em_object_mapping_list[GAME_TILE_MAX + 1] =
6015 Zborder, FALSE, FALSE,
6019 Zplayer, FALSE, FALSE,
6028 Ztank, FALSE, FALSE,
6032 Zeater, FALSE, FALSE,
6036 Zdynamite, FALSE, FALSE,
6040 Zboom, FALSE, FALSE,
6045 Xchain, FALSE, FALSE,
6046 EL_DEFAULT, ACTION_EXPLODING, -1
6049 Xboom_bug, FALSE, FALSE,
6050 EL_BUG, ACTION_EXPLODING, -1
6053 Xboom_tank, FALSE, FALSE,
6054 EL_SPACESHIP, ACTION_EXPLODING, -1
6057 Xboom_android, FALSE, FALSE,
6058 EL_EMC_ANDROID, ACTION_OTHER, -1
6061 Xboom_1, FALSE, FALSE,
6062 EL_DEFAULT, ACTION_EXPLODING, -1
6065 Xboom_2, FALSE, FALSE,
6066 EL_DEFAULT, ACTION_EXPLODING, -1
6070 Xblank, TRUE, FALSE,
6075 Xsplash_e, FALSE, FALSE,
6076 EL_ACID_SPLASH_RIGHT, -1, -1
6079 Xsplash_w, FALSE, FALSE,
6080 EL_ACID_SPLASH_LEFT, -1, -1
6084 Xplant, TRUE, FALSE,
6085 EL_EMC_PLANT, -1, -1
6088 Yplant, FALSE, FALSE,
6089 EL_EMC_PLANT, -1, -1
6093 Xacid_1, TRUE, FALSE,
6097 Xacid_2, FALSE, FALSE,
6101 Xacid_3, FALSE, FALSE,
6105 Xacid_4, FALSE, FALSE,
6109 Xacid_5, FALSE, FALSE,
6113 Xacid_6, FALSE, FALSE,
6117 Xacid_7, FALSE, FALSE,
6121 Xacid_8, FALSE, FALSE,
6126 Xfake_acid_1, TRUE, FALSE,
6127 EL_EMC_FAKE_ACID, -1, -1
6130 Xfake_acid_2, FALSE, FALSE,
6131 EL_EMC_FAKE_ACID, -1, -1
6134 Xfake_acid_3, FALSE, FALSE,
6135 EL_EMC_FAKE_ACID, -1, -1
6138 Xfake_acid_4, FALSE, FALSE,
6139 EL_EMC_FAKE_ACID, -1, -1
6142 Xfake_acid_5, FALSE, FALSE,
6143 EL_EMC_FAKE_ACID, -1, -1
6146 Xfake_acid_6, FALSE, FALSE,
6147 EL_EMC_FAKE_ACID, -1, -1
6150 Xfake_acid_7, FALSE, FALSE,
6151 EL_EMC_FAKE_ACID, -1, -1
6154 Xfake_acid_8, FALSE, FALSE,
6155 EL_EMC_FAKE_ACID, -1, -1
6159 Xfake_acid_1_player, FALSE, FALSE,
6160 EL_EMC_FAKE_ACID, -1, -1
6163 Xfake_acid_2_player, FALSE, FALSE,
6164 EL_EMC_FAKE_ACID, -1, -1
6167 Xfake_acid_3_player, FALSE, FALSE,
6168 EL_EMC_FAKE_ACID, -1, -1
6171 Xfake_acid_4_player, FALSE, FALSE,
6172 EL_EMC_FAKE_ACID, -1, -1
6175 Xfake_acid_5_player, FALSE, FALSE,
6176 EL_EMC_FAKE_ACID, -1, -1
6179 Xfake_acid_6_player, FALSE, FALSE,
6180 EL_EMC_FAKE_ACID, -1, -1
6183 Xfake_acid_7_player, FALSE, FALSE,
6184 EL_EMC_FAKE_ACID, -1, -1
6187 Xfake_acid_8_player, FALSE, FALSE,
6188 EL_EMC_FAKE_ACID, -1, -1
6192 Xgrass, TRUE, FALSE,
6193 EL_EMC_GRASS, -1, -1
6196 Ygrass_nB, FALSE, FALSE,
6197 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6200 Ygrass_eB, FALSE, FALSE,
6201 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6204 Ygrass_sB, FALSE, FALSE,
6205 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6208 Ygrass_wB, FALSE, FALSE,
6209 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6217 Ydirt_nB, FALSE, FALSE,
6218 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6221 Ydirt_eB, FALSE, FALSE,
6222 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6225 Ydirt_sB, FALSE, FALSE,
6226 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6229 Ydirt_wB, FALSE, FALSE,
6230 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6234 Xandroid, TRUE, FALSE,
6235 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6238 Xandroid_1_n, FALSE, FALSE,
6239 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6242 Xandroid_2_n, FALSE, FALSE,
6243 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6246 Xandroid_1_e, FALSE, FALSE,
6247 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6250 Xandroid_2_e, FALSE, FALSE,
6251 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6254 Xandroid_1_w, FALSE, FALSE,
6255 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6258 Xandroid_2_w, FALSE, FALSE,
6259 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6262 Xandroid_1_s, FALSE, FALSE,
6263 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6266 Xandroid_2_s, FALSE, FALSE,
6267 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6270 Yandroid_n, FALSE, FALSE,
6271 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6274 Yandroid_nB, FALSE, TRUE,
6275 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6278 Yandroid_ne, FALSE, FALSE,
6279 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6282 Yandroid_neB, FALSE, TRUE,
6283 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6286 Yandroid_e, FALSE, FALSE,
6287 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6290 Yandroid_eB, FALSE, TRUE,
6291 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6294 Yandroid_se, FALSE, FALSE,
6295 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6298 Yandroid_seB, FALSE, TRUE,
6299 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6302 Yandroid_s, FALSE, FALSE,
6303 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6306 Yandroid_sB, FALSE, TRUE,
6307 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6310 Yandroid_sw, FALSE, FALSE,
6311 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6314 Yandroid_swB, FALSE, TRUE,
6315 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6318 Yandroid_w, FALSE, FALSE,
6319 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6322 Yandroid_wB, FALSE, TRUE,
6323 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6326 Yandroid_nw, FALSE, FALSE,
6327 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6330 Yandroid_nwB, FALSE, TRUE,
6331 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6335 Xeater_n, TRUE, FALSE,
6336 EL_YAMYAM_UP, -1, -1
6339 Xeater_e, TRUE, FALSE,
6340 EL_YAMYAM_RIGHT, -1, -1
6343 Xeater_w, TRUE, FALSE,
6344 EL_YAMYAM_LEFT, -1, -1
6347 Xeater_s, TRUE, FALSE,
6348 EL_YAMYAM_DOWN, -1, -1
6351 Yeater_n, FALSE, FALSE,
6352 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6355 Yeater_nB, FALSE, TRUE,
6356 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6359 Yeater_e, FALSE, FALSE,
6360 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6363 Yeater_eB, FALSE, TRUE,
6364 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6367 Yeater_s, FALSE, FALSE,
6368 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6371 Yeater_sB, FALSE, TRUE,
6372 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6375 Yeater_w, FALSE, FALSE,
6376 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6379 Yeater_wB, FALSE, TRUE,
6380 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6383 Yeater_stone, FALSE, FALSE,
6384 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6387 Yeater_spring, FALSE, FALSE,
6388 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6392 Xalien, TRUE, FALSE,
6396 Xalien_pause, FALSE, FALSE,
6400 Yalien_n, FALSE, FALSE,
6401 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6404 Yalien_nB, FALSE, TRUE,
6405 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6408 Yalien_e, FALSE, FALSE,
6409 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6412 Yalien_eB, FALSE, TRUE,
6413 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6416 Yalien_s, FALSE, FALSE,
6417 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6420 Yalien_sB, FALSE, TRUE,
6421 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6424 Yalien_w, FALSE, FALSE,
6425 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6428 Yalien_wB, FALSE, TRUE,
6429 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6432 Yalien_stone, FALSE, FALSE,
6433 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6436 Yalien_spring, FALSE, FALSE,
6437 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6441 Xbug_1_n, TRUE, FALSE,
6445 Xbug_1_e, TRUE, FALSE,
6446 EL_BUG_RIGHT, -1, -1
6449 Xbug_1_s, TRUE, FALSE,
6453 Xbug_1_w, TRUE, FALSE,
6457 Xbug_2_n, FALSE, FALSE,
6461 Xbug_2_e, FALSE, FALSE,
6462 EL_BUG_RIGHT, -1, -1
6465 Xbug_2_s, FALSE, FALSE,
6469 Xbug_2_w, FALSE, FALSE,
6473 Ybug_n, FALSE, FALSE,
6474 EL_BUG, ACTION_MOVING, MV_BIT_UP
6477 Ybug_nB, FALSE, TRUE,
6478 EL_BUG, ACTION_MOVING, MV_BIT_UP
6481 Ybug_e, FALSE, FALSE,
6482 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6485 Ybug_eB, FALSE, TRUE,
6486 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6489 Ybug_s, FALSE, FALSE,
6490 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6493 Ybug_sB, FALSE, TRUE,
6494 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6497 Ybug_w, FALSE, FALSE,
6498 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6501 Ybug_wB, FALSE, TRUE,
6502 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6505 Ybug_w_n, FALSE, FALSE,
6506 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6509 Ybug_n_e, FALSE, FALSE,
6510 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6513 Ybug_e_s, FALSE, FALSE,
6514 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6517 Ybug_s_w, FALSE, FALSE,
6518 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6521 Ybug_e_n, FALSE, FALSE,
6522 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6525 Ybug_s_e, FALSE, FALSE,
6526 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6529 Ybug_w_s, FALSE, FALSE,
6530 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6533 Ybug_n_w, FALSE, FALSE,
6534 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6537 Ybug_stone, FALSE, FALSE,
6538 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6541 Ybug_spring, FALSE, FALSE,
6542 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6546 Xtank_1_n, TRUE, FALSE,
6547 EL_SPACESHIP_UP, -1, -1
6550 Xtank_1_e, TRUE, FALSE,
6551 EL_SPACESHIP_RIGHT, -1, -1
6554 Xtank_1_s, TRUE, FALSE,
6555 EL_SPACESHIP_DOWN, -1, -1
6558 Xtank_1_w, TRUE, FALSE,
6559 EL_SPACESHIP_LEFT, -1, -1
6562 Xtank_2_n, FALSE, FALSE,
6563 EL_SPACESHIP_UP, -1, -1
6566 Xtank_2_e, FALSE, FALSE,
6567 EL_SPACESHIP_RIGHT, -1, -1
6570 Xtank_2_s, FALSE, FALSE,
6571 EL_SPACESHIP_DOWN, -1, -1
6574 Xtank_2_w, FALSE, FALSE,
6575 EL_SPACESHIP_LEFT, -1, -1
6578 Ytank_n, FALSE, FALSE,
6579 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6582 Ytank_nB, FALSE, TRUE,
6583 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6586 Ytank_e, FALSE, FALSE,
6587 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6590 Ytank_eB, FALSE, TRUE,
6591 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6594 Ytank_s, FALSE, FALSE,
6595 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6598 Ytank_sB, FALSE, TRUE,
6599 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6602 Ytank_w, FALSE, FALSE,
6603 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6606 Ytank_wB, FALSE, TRUE,
6607 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6610 Ytank_w_n, FALSE, FALSE,
6611 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6614 Ytank_n_e, FALSE, FALSE,
6615 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6618 Ytank_e_s, FALSE, FALSE,
6619 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6622 Ytank_s_w, FALSE, FALSE,
6623 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6626 Ytank_e_n, FALSE, FALSE,
6627 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6630 Ytank_s_e, FALSE, FALSE,
6631 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6634 Ytank_w_s, FALSE, FALSE,
6635 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6638 Ytank_n_w, FALSE, FALSE,
6639 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6642 Ytank_stone, FALSE, FALSE,
6643 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6646 Ytank_spring, FALSE, FALSE,
6647 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6651 Xemerald, TRUE, FALSE,
6655 Xemerald_pause, FALSE, FALSE,
6659 Xemerald_fall, FALSE, FALSE,
6663 Xemerald_shine, FALSE, FALSE,
6664 EL_EMERALD, ACTION_TWINKLING, -1
6667 Yemerald_s, FALSE, FALSE,
6668 EL_EMERALD, ACTION_FALLING, -1
6671 Yemerald_sB, FALSE, TRUE,
6672 EL_EMERALD, ACTION_FALLING, -1
6675 Yemerald_e, FALSE, FALSE,
6676 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6679 Yemerald_eB, FALSE, TRUE,
6680 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6683 Yemerald_w, FALSE, FALSE,
6684 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6687 Yemerald_wB, FALSE, TRUE,
6688 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6691 Yemerald_blank, FALSE, FALSE,
6692 EL_EMERALD, ACTION_COLLECTING, -1
6696 Xdiamond, TRUE, FALSE,
6700 Xdiamond_pause, FALSE, FALSE,
6704 Xdiamond_fall, FALSE, FALSE,
6708 Xdiamond_shine, FALSE, FALSE,
6709 EL_DIAMOND, ACTION_TWINKLING, -1
6712 Ydiamond_s, FALSE, FALSE,
6713 EL_DIAMOND, ACTION_FALLING, -1
6716 Ydiamond_sB, FALSE, TRUE,
6717 EL_DIAMOND, ACTION_FALLING, -1
6720 Ydiamond_e, FALSE, FALSE,
6721 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6724 Ydiamond_eB, FALSE, TRUE,
6725 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6728 Ydiamond_w, FALSE, FALSE,
6729 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6732 Ydiamond_wB, FALSE, TRUE,
6733 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6736 Ydiamond_blank, FALSE, FALSE,
6737 EL_DIAMOND, ACTION_COLLECTING, -1
6740 Ydiamond_stone, FALSE, FALSE,
6741 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6745 Xstone, TRUE, FALSE,
6749 Xstone_pause, FALSE, FALSE,
6753 Xstone_fall, FALSE, FALSE,
6757 Ystone_s, FALSE, FALSE,
6758 EL_ROCK, ACTION_FALLING, -1
6761 Ystone_sB, FALSE, TRUE,
6762 EL_ROCK, ACTION_FALLING, -1
6765 Ystone_e, FALSE, FALSE,
6766 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6769 Ystone_eB, FALSE, TRUE,
6770 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6773 Ystone_w, FALSE, FALSE,
6774 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6777 Ystone_wB, FALSE, TRUE,
6778 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6786 Xbomb_pause, FALSE, FALSE,
6790 Xbomb_fall, FALSE, FALSE,
6794 Ybomb_s, FALSE, FALSE,
6795 EL_BOMB, ACTION_FALLING, -1
6798 Ybomb_sB, FALSE, TRUE,
6799 EL_BOMB, ACTION_FALLING, -1
6802 Ybomb_e, FALSE, FALSE,
6803 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6806 Ybomb_eB, FALSE, TRUE,
6807 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6810 Ybomb_w, FALSE, FALSE,
6811 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6814 Ybomb_wB, FALSE, TRUE,
6815 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6818 Ybomb_blank, FALSE, FALSE,
6819 EL_BOMB, ACTION_ACTIVATING, -1
6827 Xnut_pause, FALSE, FALSE,
6831 Xnut_fall, FALSE, FALSE,
6835 Ynut_s, FALSE, FALSE,
6836 EL_NUT, ACTION_FALLING, -1
6839 Ynut_sB, FALSE, TRUE,
6840 EL_NUT, ACTION_FALLING, -1
6843 Ynut_e, FALSE, FALSE,
6844 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6847 Ynut_eB, FALSE, TRUE,
6848 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6851 Ynut_w, FALSE, FALSE,
6852 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6855 Ynut_wB, FALSE, TRUE,
6856 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6859 Ynut_stone, FALSE, FALSE,
6860 EL_NUT, ACTION_BREAKING, -1
6864 Xspring, TRUE, FALSE,
6868 Xspring_pause, FALSE, FALSE,
6872 Xspring_e, TRUE, FALSE,
6873 EL_SPRING_RIGHT, -1, -1
6876 Xspring_w, TRUE, FALSE,
6877 EL_SPRING_LEFT, -1, -1
6880 Xspring_fall, FALSE, FALSE,
6884 Yspring_s, FALSE, FALSE,
6885 EL_SPRING, ACTION_FALLING, -1
6888 Yspring_sB, FALSE, TRUE,
6889 EL_SPRING, ACTION_FALLING, -1
6892 Yspring_e, FALSE, FALSE,
6893 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6896 Yspring_eB, FALSE, TRUE,
6897 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6900 Yspring_w, FALSE, FALSE,
6901 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6904 Yspring_wB, FALSE, TRUE,
6905 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6908 Yspring_alien_e, FALSE, FALSE,
6909 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6912 Yspring_alien_eB, FALSE, TRUE,
6913 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6916 Yspring_alien_w, FALSE, FALSE,
6917 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6920 Yspring_alien_wB, FALSE, TRUE,
6921 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6925 Xpush_emerald_e, FALSE, FALSE,
6926 EL_EMERALD, -1, MV_BIT_RIGHT
6929 Xpush_emerald_w, FALSE, FALSE,
6930 EL_EMERALD, -1, MV_BIT_LEFT
6933 Xpush_diamond_e, FALSE, FALSE,
6934 EL_DIAMOND, -1, MV_BIT_RIGHT
6937 Xpush_diamond_w, FALSE, FALSE,
6938 EL_DIAMOND, -1, MV_BIT_LEFT
6941 Xpush_stone_e, FALSE, FALSE,
6942 EL_ROCK, -1, MV_BIT_RIGHT
6945 Xpush_stone_w, FALSE, FALSE,
6946 EL_ROCK, -1, MV_BIT_LEFT
6949 Xpush_bomb_e, FALSE, FALSE,
6950 EL_BOMB, -1, MV_BIT_RIGHT
6953 Xpush_bomb_w, FALSE, FALSE,
6954 EL_BOMB, -1, MV_BIT_LEFT
6957 Xpush_nut_e, FALSE, FALSE,
6958 EL_NUT, -1, MV_BIT_RIGHT
6961 Xpush_nut_w, FALSE, FALSE,
6962 EL_NUT, -1, MV_BIT_LEFT
6965 Xpush_spring_e, FALSE, FALSE,
6966 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6969 Xpush_spring_w, FALSE, FALSE,
6970 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6974 Xdynamite, TRUE, FALSE,
6975 EL_EM_DYNAMITE, -1, -1
6978 Ydynamite_blank, FALSE, FALSE,
6979 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6982 Xdynamite_1, TRUE, FALSE,
6983 EL_EM_DYNAMITE_ACTIVE, -1, -1
6986 Xdynamite_2, FALSE, FALSE,
6987 EL_EM_DYNAMITE_ACTIVE, -1, -1
6990 Xdynamite_3, FALSE, FALSE,
6991 EL_EM_DYNAMITE_ACTIVE, -1, -1
6994 Xdynamite_4, FALSE, FALSE,
6995 EL_EM_DYNAMITE_ACTIVE, -1, -1
6999 Xkey_1, TRUE, FALSE,
7003 Xkey_2, TRUE, FALSE,
7007 Xkey_3, TRUE, FALSE,
7011 Xkey_4, TRUE, FALSE,
7015 Xkey_5, TRUE, FALSE,
7016 EL_EMC_KEY_5, -1, -1
7019 Xkey_6, TRUE, FALSE,
7020 EL_EMC_KEY_6, -1, -1
7023 Xkey_7, TRUE, FALSE,
7024 EL_EMC_KEY_7, -1, -1
7027 Xkey_8, TRUE, FALSE,
7028 EL_EMC_KEY_8, -1, -1
7032 Xdoor_1, TRUE, FALSE,
7033 EL_EM_GATE_1, -1, -1
7036 Xdoor_2, TRUE, FALSE,
7037 EL_EM_GATE_2, -1, -1
7040 Xdoor_3, TRUE, FALSE,
7041 EL_EM_GATE_3, -1, -1
7044 Xdoor_4, TRUE, FALSE,
7045 EL_EM_GATE_4, -1, -1
7048 Xdoor_5, TRUE, FALSE,
7049 EL_EMC_GATE_5, -1, -1
7052 Xdoor_6, TRUE, FALSE,
7053 EL_EMC_GATE_6, -1, -1
7056 Xdoor_7, TRUE, FALSE,
7057 EL_EMC_GATE_7, -1, -1
7060 Xdoor_8, TRUE, FALSE,
7061 EL_EMC_GATE_8, -1, -1
7065 Xfake_door_1, TRUE, FALSE,
7066 EL_EM_GATE_1_GRAY, -1, -1
7069 Xfake_door_2, TRUE, FALSE,
7070 EL_EM_GATE_2_GRAY, -1, -1
7073 Xfake_door_3, TRUE, FALSE,
7074 EL_EM_GATE_3_GRAY, -1, -1
7077 Xfake_door_4, TRUE, FALSE,
7078 EL_EM_GATE_4_GRAY, -1, -1
7081 Xfake_door_5, TRUE, FALSE,
7082 EL_EMC_GATE_5_GRAY, -1, -1
7085 Xfake_door_6, TRUE, FALSE,
7086 EL_EMC_GATE_6_GRAY, -1, -1
7089 Xfake_door_7, TRUE, FALSE,
7090 EL_EMC_GATE_7_GRAY, -1, -1
7093 Xfake_door_8, TRUE, FALSE,
7094 EL_EMC_GATE_8_GRAY, -1, -1
7098 Xballoon, TRUE, FALSE,
7102 Yballoon_n, FALSE, FALSE,
7103 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7106 Yballoon_nB, FALSE, TRUE,
7107 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
7110 Yballoon_e, FALSE, FALSE,
7111 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7114 Yballoon_eB, FALSE, TRUE,
7115 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
7118 Yballoon_s, FALSE, FALSE,
7119 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7122 Yballoon_sB, FALSE, TRUE,
7123 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
7126 Yballoon_w, FALSE, FALSE,
7127 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7130 Yballoon_wB, FALSE, TRUE,
7131 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
7135 Xball_1, TRUE, FALSE,
7136 EL_EMC_MAGIC_BALL, -1, -1
7139 Yball_1, FALSE, FALSE,
7140 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7143 Xball_2, FALSE, FALSE,
7144 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7147 Yball_2, FALSE, FALSE,
7148 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
7151 Yball_blank, FALSE, FALSE,
7152 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
7156 Xamoeba_1, TRUE, FALSE,
7157 EL_AMOEBA_DRY, ACTION_OTHER, -1
7160 Xamoeba_2, FALSE, FALSE,
7161 EL_AMOEBA_DRY, ACTION_OTHER, -1
7164 Xamoeba_3, FALSE, FALSE,
7165 EL_AMOEBA_DRY, ACTION_OTHER, -1
7168 Xamoeba_4, FALSE, FALSE,
7169 EL_AMOEBA_DRY, ACTION_OTHER, -1
7172 Xamoeba_5, TRUE, FALSE,
7173 EL_AMOEBA_WET, ACTION_OTHER, -1
7176 Xamoeba_6, FALSE, FALSE,
7177 EL_AMOEBA_WET, ACTION_OTHER, -1
7180 Xamoeba_7, FALSE, FALSE,
7181 EL_AMOEBA_WET, ACTION_OTHER, -1
7184 Xamoeba_8, FALSE, FALSE,
7185 EL_AMOEBA_WET, ACTION_OTHER, -1
7190 EL_AMOEBA_DROP, ACTION_GROWING, -1
7193 Xdrip_fall, FALSE, FALSE,
7194 EL_AMOEBA_DROP, -1, -1
7197 Xdrip_stretch, FALSE, FALSE,
7198 EL_AMOEBA_DROP, ACTION_FALLING, -1
7201 Xdrip_stretchB, FALSE, TRUE,
7202 EL_AMOEBA_DROP, ACTION_FALLING, -1
7205 Ydrip_1_s, FALSE, FALSE,
7206 EL_AMOEBA_DROP, ACTION_FALLING, -1
7209 Ydrip_1_sB, FALSE, TRUE,
7210 EL_AMOEBA_DROP, ACTION_FALLING, -1
7213 Ydrip_2_s, FALSE, FALSE,
7214 EL_AMOEBA_DROP, ACTION_FALLING, -1
7217 Ydrip_2_sB, FALSE, TRUE,
7218 EL_AMOEBA_DROP, ACTION_FALLING, -1
7222 Xwonderwall, TRUE, FALSE,
7223 EL_MAGIC_WALL, -1, -1
7226 Ywonderwall, FALSE, FALSE,
7227 EL_MAGIC_WALL, ACTION_ACTIVE, -1
7231 Xwheel, TRUE, FALSE,
7232 EL_ROBOT_WHEEL, -1, -1
7235 Ywheel, FALSE, FALSE,
7236 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
7240 Xswitch, TRUE, FALSE,
7241 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
7244 Yswitch, FALSE, FALSE,
7245 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
7249 Xbumper, TRUE, FALSE,
7250 EL_EMC_SPRING_BUMPER, -1, -1
7253 Ybumper, FALSE, FALSE,
7254 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
7258 Xacid_nw, TRUE, FALSE,
7259 EL_ACID_POOL_TOPLEFT, -1, -1
7262 Xacid_ne, TRUE, FALSE,
7263 EL_ACID_POOL_TOPRIGHT, -1, -1
7266 Xacid_sw, TRUE, FALSE,
7267 EL_ACID_POOL_BOTTOMLEFT, -1, -1
7270 Xacid_s, TRUE, FALSE,
7271 EL_ACID_POOL_BOTTOM, -1, -1
7274 Xacid_se, TRUE, FALSE,
7275 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
7279 Xfake_blank, TRUE, FALSE,
7280 EL_INVISIBLE_WALL, -1, -1
7283 Yfake_blank, FALSE, FALSE,
7284 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7288 Xfake_grass, TRUE, FALSE,
7289 EL_EMC_FAKE_GRASS, -1, -1
7292 Yfake_grass, FALSE, FALSE,
7293 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7297 Xfake_amoeba, TRUE, FALSE,
7298 EL_EMC_DRIPPER, -1, -1
7301 Yfake_amoeba, FALSE, FALSE,
7302 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7306 Xlenses, TRUE, FALSE,
7307 EL_EMC_LENSES, -1, -1
7311 Xmagnify, TRUE, FALSE,
7312 EL_EMC_MAGNIFIER, -1, -1
7317 EL_QUICKSAND_EMPTY, -1, -1
7320 Xsand_stone, TRUE, FALSE,
7321 EL_QUICKSAND_FULL, -1, -1
7324 Xsand_stonein_1, FALSE, TRUE,
7325 EL_ROCK, ACTION_FILLING, -1
7328 Xsand_stonein_2, FALSE, TRUE,
7329 EL_ROCK, ACTION_FILLING, -1
7332 Xsand_stonein_3, FALSE, TRUE,
7333 EL_ROCK, ACTION_FILLING, -1
7336 Xsand_stonein_4, FALSE, TRUE,
7337 EL_ROCK, ACTION_FILLING, -1
7340 Xsand_sandstone_1, FALSE, FALSE,
7341 EL_QUICKSAND_FILLING, -1, -1
7344 Xsand_sandstone_2, FALSE, FALSE,
7345 EL_QUICKSAND_FILLING, -1, -1
7348 Xsand_sandstone_3, FALSE, FALSE,
7349 EL_QUICKSAND_FILLING, -1, -1
7352 Xsand_sandstone_4, FALSE, FALSE,
7353 EL_QUICKSAND_FILLING, -1, -1
7356 Xsand_stonesand_1, FALSE, FALSE,
7357 EL_QUICKSAND_EMPTYING, -1, -1
7360 Xsand_stonesand_2, FALSE, FALSE,
7361 EL_QUICKSAND_EMPTYING, -1, -1
7364 Xsand_stonesand_3, FALSE, FALSE,
7365 EL_QUICKSAND_EMPTYING, -1, -1
7368 Xsand_stonesand_4, FALSE, FALSE,
7369 EL_QUICKSAND_EMPTYING, -1, -1
7372 Xsand_stoneout_1, FALSE, FALSE,
7373 EL_ROCK, ACTION_EMPTYING, -1
7376 Xsand_stoneout_2, FALSE, FALSE,
7377 EL_ROCK, ACTION_EMPTYING, -1
7380 Xsand_stonesand_quickout_1, FALSE, FALSE,
7381 EL_QUICKSAND_EMPTYING, -1, -1
7384 Xsand_stonesand_quickout_2, FALSE, FALSE,
7385 EL_QUICKSAND_EMPTYING, -1, -1
7389 Xslide_ns, TRUE, FALSE,
7390 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7393 Yslide_ns_blank, FALSE, FALSE,
7394 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7397 Xslide_ew, TRUE, FALSE,
7398 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7401 Yslide_ew_blank, FALSE, FALSE,
7402 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7406 Xwind_n, TRUE, FALSE,
7407 EL_BALLOON_SWITCH_UP, -1, -1
7410 Xwind_e, TRUE, FALSE,
7411 EL_BALLOON_SWITCH_RIGHT, -1, -1
7414 Xwind_s, TRUE, FALSE,
7415 EL_BALLOON_SWITCH_DOWN, -1, -1
7418 Xwind_w, TRUE, FALSE,
7419 EL_BALLOON_SWITCH_LEFT, -1, -1
7422 Xwind_any, TRUE, FALSE,
7423 EL_BALLOON_SWITCH_ANY, -1, -1
7426 Xwind_stop, TRUE, FALSE,
7427 EL_BALLOON_SWITCH_NONE, -1, -1
7432 EL_EM_EXIT_CLOSED, -1, -1
7435 Xexit_1, TRUE, FALSE,
7436 EL_EM_EXIT_OPEN, -1, -1
7439 Xexit_2, FALSE, FALSE,
7440 EL_EM_EXIT_OPEN, -1, -1
7443 Xexit_3, FALSE, FALSE,
7444 EL_EM_EXIT_OPEN, -1, -1
7448 Xpause, FALSE, FALSE,
7453 Xwall_1, TRUE, FALSE,
7457 Xwall_2, TRUE, FALSE,
7458 EL_EMC_WALL_14, -1, -1
7461 Xwall_3, TRUE, FALSE,
7462 EL_EMC_WALL_15, -1, -1
7465 Xwall_4, TRUE, FALSE,
7466 EL_EMC_WALL_16, -1, -1
7470 Xroundwall_1, TRUE, FALSE,
7471 EL_WALL_SLIPPERY, -1, -1
7474 Xroundwall_2, TRUE, FALSE,
7475 EL_EMC_WALL_SLIPPERY_2, -1, -1
7478 Xroundwall_3, TRUE, FALSE,
7479 EL_EMC_WALL_SLIPPERY_3, -1, -1
7482 Xroundwall_4, TRUE, FALSE,
7483 EL_EMC_WALL_SLIPPERY_4, -1, -1
7487 Xsteel_1, TRUE, FALSE,
7488 EL_STEELWALL, -1, -1
7491 Xsteel_2, TRUE, FALSE,
7492 EL_EMC_STEELWALL_2, -1, -1
7495 Xsteel_3, TRUE, FALSE,
7496 EL_EMC_STEELWALL_3, -1, -1
7499 Xsteel_4, TRUE, FALSE,
7500 EL_EMC_STEELWALL_4, -1, -1
7504 Xdecor_1, TRUE, FALSE,
7505 EL_EMC_WALL_8, -1, -1
7508 Xdecor_2, TRUE, FALSE,
7509 EL_EMC_WALL_6, -1, -1
7512 Xdecor_3, TRUE, FALSE,
7513 EL_EMC_WALL_4, -1, -1
7516 Xdecor_4, TRUE, FALSE,
7517 EL_EMC_WALL_7, -1, -1
7520 Xdecor_5, TRUE, FALSE,
7521 EL_EMC_WALL_5, -1, -1
7524 Xdecor_6, TRUE, FALSE,
7525 EL_EMC_WALL_9, -1, -1
7528 Xdecor_7, TRUE, FALSE,
7529 EL_EMC_WALL_10, -1, -1
7532 Xdecor_8, TRUE, FALSE,
7533 EL_EMC_WALL_1, -1, -1
7536 Xdecor_9, TRUE, FALSE,
7537 EL_EMC_WALL_2, -1, -1
7540 Xdecor_10, TRUE, FALSE,
7541 EL_EMC_WALL_3, -1, -1
7544 Xdecor_11, TRUE, FALSE,
7545 EL_EMC_WALL_11, -1, -1
7548 Xdecor_12, TRUE, FALSE,
7549 EL_EMC_WALL_12, -1, -1
7553 Xalpha_0, TRUE, FALSE,
7554 EL_CHAR('0'), -1, -1
7557 Xalpha_1, TRUE, FALSE,
7558 EL_CHAR('1'), -1, -1
7561 Xalpha_2, TRUE, FALSE,
7562 EL_CHAR('2'), -1, -1
7565 Xalpha_3, TRUE, FALSE,
7566 EL_CHAR('3'), -1, -1
7569 Xalpha_4, TRUE, FALSE,
7570 EL_CHAR('4'), -1, -1
7573 Xalpha_5, TRUE, FALSE,
7574 EL_CHAR('5'), -1, -1
7577 Xalpha_6, TRUE, FALSE,
7578 EL_CHAR('6'), -1, -1
7581 Xalpha_7, TRUE, FALSE,
7582 EL_CHAR('7'), -1, -1
7585 Xalpha_8, TRUE, FALSE,
7586 EL_CHAR('8'), -1, -1
7589 Xalpha_9, TRUE, FALSE,
7590 EL_CHAR('9'), -1, -1
7593 Xalpha_excla, TRUE, FALSE,
7594 EL_CHAR('!'), -1, -1
7597 Xalpha_apost, TRUE, FALSE,
7598 EL_CHAR('\''), -1, -1
7601 Xalpha_comma, TRUE, FALSE,
7602 EL_CHAR(','), -1, -1
7605 Xalpha_minus, TRUE, FALSE,
7606 EL_CHAR('-'), -1, -1
7609 Xalpha_perio, TRUE, FALSE,
7610 EL_CHAR('.'), -1, -1
7613 Xalpha_colon, TRUE, FALSE,
7614 EL_CHAR(':'), -1, -1
7617 Xalpha_quest, TRUE, FALSE,
7618 EL_CHAR('?'), -1, -1
7621 Xalpha_a, TRUE, FALSE,
7622 EL_CHAR('A'), -1, -1
7625 Xalpha_b, TRUE, FALSE,
7626 EL_CHAR('B'), -1, -1
7629 Xalpha_c, TRUE, FALSE,
7630 EL_CHAR('C'), -1, -1
7633 Xalpha_d, TRUE, FALSE,
7634 EL_CHAR('D'), -1, -1
7637 Xalpha_e, TRUE, FALSE,
7638 EL_CHAR('E'), -1, -1
7641 Xalpha_f, TRUE, FALSE,
7642 EL_CHAR('F'), -1, -1
7645 Xalpha_g, TRUE, FALSE,
7646 EL_CHAR('G'), -1, -1
7649 Xalpha_h, TRUE, FALSE,
7650 EL_CHAR('H'), -1, -1
7653 Xalpha_i, TRUE, FALSE,
7654 EL_CHAR('I'), -1, -1
7657 Xalpha_j, TRUE, FALSE,
7658 EL_CHAR('J'), -1, -1
7661 Xalpha_k, TRUE, FALSE,
7662 EL_CHAR('K'), -1, -1
7665 Xalpha_l, TRUE, FALSE,
7666 EL_CHAR('L'), -1, -1
7669 Xalpha_m, TRUE, FALSE,
7670 EL_CHAR('M'), -1, -1
7673 Xalpha_n, TRUE, FALSE,
7674 EL_CHAR('N'), -1, -1
7677 Xalpha_o, TRUE, FALSE,
7678 EL_CHAR('O'), -1, -1
7681 Xalpha_p, TRUE, FALSE,
7682 EL_CHAR('P'), -1, -1
7685 Xalpha_q, TRUE, FALSE,
7686 EL_CHAR('Q'), -1, -1
7689 Xalpha_r, TRUE, FALSE,
7690 EL_CHAR('R'), -1, -1
7693 Xalpha_s, TRUE, FALSE,
7694 EL_CHAR('S'), -1, -1
7697 Xalpha_t, TRUE, FALSE,
7698 EL_CHAR('T'), -1, -1
7701 Xalpha_u, TRUE, FALSE,
7702 EL_CHAR('U'), -1, -1
7705 Xalpha_v, TRUE, FALSE,
7706 EL_CHAR('V'), -1, -1
7709 Xalpha_w, TRUE, FALSE,
7710 EL_CHAR('W'), -1, -1
7713 Xalpha_x, TRUE, FALSE,
7714 EL_CHAR('X'), -1, -1
7717 Xalpha_y, TRUE, FALSE,
7718 EL_CHAR('Y'), -1, -1
7721 Xalpha_z, TRUE, FALSE,
7722 EL_CHAR('Z'), -1, -1
7725 Xalpha_arrow_e, TRUE, FALSE,
7726 EL_CHAR('>'), -1, -1
7729 Xalpha_arrow_w, TRUE, FALSE,
7730 EL_CHAR('<'), -1, -1
7733 Xalpha_copyr, TRUE, FALSE,
7734 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7738 Ykey_1_blank, FALSE, FALSE,
7739 EL_EM_KEY_1, ACTION_COLLECTING, -1
7742 Ykey_2_blank, FALSE, FALSE,
7743 EL_EM_KEY_2, ACTION_COLLECTING, -1
7746 Ykey_3_blank, FALSE, FALSE,
7747 EL_EM_KEY_3, ACTION_COLLECTING, -1
7750 Ykey_4_blank, FALSE, FALSE,
7751 EL_EM_KEY_4, ACTION_COLLECTING, -1
7754 Ykey_5_blank, FALSE, FALSE,
7755 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7758 Ykey_6_blank, FALSE, FALSE,
7759 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7762 Ykey_7_blank, FALSE, FALSE,
7763 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7766 Ykey_8_blank, FALSE, FALSE,
7767 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7770 Ylenses_blank, FALSE, FALSE,
7771 EL_EMC_LENSES, ACTION_COLLECTING, -1
7774 Ymagnify_blank, FALSE, FALSE,
7775 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7778 Ygrass_blank, FALSE, FALSE,
7779 EL_EMC_GRASS, ACTION_SNAPPING, -1
7782 Ydirt_blank, FALSE, FALSE,
7783 EL_SAND, ACTION_SNAPPING, -1
7792 static struct Mapping_EM_to_RND_player
7801 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7805 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7809 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7813 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7817 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7821 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7825 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7829 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7833 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7837 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7841 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7845 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7849 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7853 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7857 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7861 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7865 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7869 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7873 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7877 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7881 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7885 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7889 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7893 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7897 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7901 EL_PLAYER_1, ACTION_DEFAULT, -1,
7905 EL_PLAYER_2, ACTION_DEFAULT, -1,
7909 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7913 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7917 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7921 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7925 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7929 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7933 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7937 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7941 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7945 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7949 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7953 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7957 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7961 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7965 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7969 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7973 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7977 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7981 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7985 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7989 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7993 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7997 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
8001 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
8005 EL_PLAYER_3, ACTION_DEFAULT, -1,
8009 EL_PLAYER_4, ACTION_DEFAULT, -1,
8018 int map_element_RND_to_EM_cave(int element_rnd)
8020 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
8021 static boolean mapping_initialized = FALSE;
8023 if (!mapping_initialized)
8027 // return "Xalpha_quest" for all undefined elements in mapping array
8028 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8029 mapping_RND_to_EM[i] = Xalpha_quest;
8031 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8032 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
8033 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
8034 em_object_mapping_list[i].element_em;
8036 mapping_initialized = TRUE;
8039 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
8041 Warn("invalid RND level element %d", element_rnd);
8046 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
8049 int map_element_EM_to_RND_cave(int element_em_cave)
8051 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8052 static boolean mapping_initialized = FALSE;
8054 if (!mapping_initialized)
8058 // return "EL_UNKNOWN" for all undefined elements in mapping array
8059 for (i = 0; i < GAME_TILE_MAX; i++)
8060 mapping_EM_to_RND[i] = EL_UNKNOWN;
8062 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8063 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8064 em_object_mapping_list[i].element_rnd;
8066 mapping_initialized = TRUE;
8069 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8071 Warn("invalid EM cave element %d", element_em_cave);
8076 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8079 int map_element_EM_to_RND_game(int element_em_game)
8081 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8082 static boolean mapping_initialized = FALSE;
8084 if (!mapping_initialized)
8088 // return "EL_UNKNOWN" for all undefined elements in mapping array
8089 for (i = 0; i < GAME_TILE_MAX; i++)
8090 mapping_EM_to_RND[i] = EL_UNKNOWN;
8092 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8093 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8094 em_object_mapping_list[i].element_rnd;
8096 mapping_initialized = TRUE;
8099 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8101 Warn("invalid EM game element %d", element_em_game);
8106 return mapping_EM_to_RND[element_em_game];
8109 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8111 struct LevelInfo_EM *level_em = level->native_em_level;
8112 struct CAVE *cav = level_em->cav;
8115 for (i = 0; i < GAME_TILE_MAX; i++)
8116 cav->android_array[i] = Cblank;
8118 for (i = 0; i < level->num_android_clone_elements; i++)
8120 int element_rnd = level->android_clone_element[i];
8121 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8123 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8124 if (em_object_mapping_list[j].element_rnd == element_rnd)
8125 cav->android_array[em_object_mapping_list[j].element_em] =
8130 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8132 struct LevelInfo_EM *level_em = level->native_em_level;
8133 struct CAVE *cav = level_em->cav;
8136 level->num_android_clone_elements = 0;
8138 for (i = 0; i < GAME_TILE_MAX; i++)
8140 int element_em_cave = cav->android_array[i];
8142 boolean element_found = FALSE;
8144 if (element_em_cave == Cblank)
8147 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8149 for (j = 0; j < level->num_android_clone_elements; j++)
8150 if (level->android_clone_element[j] == element_rnd)
8151 element_found = TRUE;
8155 level->android_clone_element[level->num_android_clone_elements++] =
8158 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8163 if (level->num_android_clone_elements == 0)
8165 level->num_android_clone_elements = 1;
8166 level->android_clone_element[0] = EL_EMPTY;
8170 int map_direction_RND_to_EM(int direction)
8172 return (direction == MV_UP ? 0 :
8173 direction == MV_RIGHT ? 1 :
8174 direction == MV_DOWN ? 2 :
8175 direction == MV_LEFT ? 3 :
8179 int map_direction_EM_to_RND(int direction)
8181 return (direction == 0 ? MV_UP :
8182 direction == 1 ? MV_RIGHT :
8183 direction == 2 ? MV_DOWN :
8184 direction == 3 ? MV_LEFT :
8188 int map_element_RND_to_SP(int element_rnd)
8190 int element_sp = 0x20; // map unknown elements to yellow "hardware"
8192 if (element_rnd >= EL_SP_START &&
8193 element_rnd <= EL_SP_END)
8194 element_sp = element_rnd - EL_SP_START;
8195 else if (element_rnd == EL_EMPTY_SPACE)
8197 else if (element_rnd == EL_INVISIBLE_WALL)
8203 int map_element_SP_to_RND(int element_sp)
8205 int element_rnd = EL_UNKNOWN;
8207 if (element_sp >= 0x00 &&
8209 element_rnd = EL_SP_START + element_sp;
8210 else if (element_sp == 0x28)
8211 element_rnd = EL_INVISIBLE_WALL;
8216 int map_action_SP_to_RND(int action_sp)
8220 case actActive: return ACTION_ACTIVE;
8221 case actImpact: return ACTION_IMPACT;
8222 case actExploding: return ACTION_EXPLODING;
8223 case actDigging: return ACTION_DIGGING;
8224 case actSnapping: return ACTION_SNAPPING;
8225 case actCollecting: return ACTION_COLLECTING;
8226 case actPassing: return ACTION_PASSING;
8227 case actPushing: return ACTION_PUSHING;
8228 case actDropping: return ACTION_DROPPING;
8230 default: return ACTION_DEFAULT;
8234 int map_element_RND_to_MM(int element_rnd)
8236 return (element_rnd >= EL_MM_START_1 &&
8237 element_rnd <= EL_MM_END_1 ?
8238 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8240 element_rnd >= EL_MM_START_2 &&
8241 element_rnd <= EL_MM_END_2 ?
8242 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8244 element_rnd >= EL_CHAR_START &&
8245 element_rnd <= EL_CHAR_END ?
8246 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8248 element_rnd >= EL_MM_RUNTIME_START &&
8249 element_rnd <= EL_MM_RUNTIME_END ?
8250 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8252 EL_MM_EMPTY_NATIVE);
8255 int map_element_MM_to_RND(int element_mm)
8257 return (element_mm == EL_MM_EMPTY_NATIVE ||
8258 element_mm == EL_DF_EMPTY_NATIVE ?
8261 element_mm >= EL_MM_START_1_NATIVE &&
8262 element_mm <= EL_MM_END_1_NATIVE ?
8263 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8265 element_mm >= EL_MM_START_2_NATIVE &&
8266 element_mm <= EL_MM_END_2_NATIVE ?
8267 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8269 element_mm >= EL_MM_CHAR_START_NATIVE &&
8270 element_mm <= EL_MM_CHAR_END_NATIVE ?
8271 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8273 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8274 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8275 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8280 int map_action_MM_to_RND(int action_mm)
8282 // all MM actions are defined to exactly match their RND counterparts
8286 int map_sound_MM_to_RND(int sound_mm)
8290 case SND_MM_GAME_LEVELTIME_CHARGING:
8291 return SND_GAME_LEVELTIME_CHARGING;
8293 case SND_MM_GAME_HEALTH_CHARGING:
8294 return SND_GAME_HEALTH_CHARGING;
8297 return SND_UNDEFINED;
8301 int map_mm_wall_element(int element)
8303 return (element >= EL_MM_STEEL_WALL_START &&
8304 element <= EL_MM_STEEL_WALL_END ?
8307 element >= EL_MM_WOODEN_WALL_START &&
8308 element <= EL_MM_WOODEN_WALL_END ?
8311 element >= EL_MM_ICE_WALL_START &&
8312 element <= EL_MM_ICE_WALL_END ?
8315 element >= EL_MM_AMOEBA_WALL_START &&
8316 element <= EL_MM_AMOEBA_WALL_END ?
8319 element >= EL_DF_STEEL_WALL_START &&
8320 element <= EL_DF_STEEL_WALL_END ?
8323 element >= EL_DF_WOODEN_WALL_START &&
8324 element <= EL_DF_WOODEN_WALL_END ?
8330 int map_mm_wall_element_editor(int element)
8334 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8335 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8336 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8337 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8338 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8339 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8341 default: return element;
8345 int get_next_element(int element)
8349 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8350 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8351 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8352 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8353 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8354 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8355 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8356 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8357 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8358 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8359 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8361 default: return element;
8365 int el2img_mm(int element_mm)
8367 return el2img(map_element_MM_to_RND(element_mm));
8370 int el_act2img_mm(int element_mm, int action)
8372 return el_act2img(map_element_MM_to_RND(element_mm), action);
8375 int el_act_dir2img(int element, int action, int direction)
8377 element = GFX_ELEMENT(element);
8378 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8380 // direction_graphic[][] == graphic[] for undefined direction graphics
8381 return element_info[element].direction_graphic[action][direction];
8384 static int el_act_dir2crm(int element, int action, int direction)
8386 element = GFX_ELEMENT(element);
8387 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8389 // direction_graphic[][] == graphic[] for undefined direction graphics
8390 return element_info[element].direction_crumbled[action][direction];
8393 int el_act2img(int element, int action)
8395 element = GFX_ELEMENT(element);
8397 return element_info[element].graphic[action];
8400 int el_act2crm(int element, int action)
8402 element = GFX_ELEMENT(element);
8404 return element_info[element].crumbled[action];
8407 int el_dir2img(int element, int direction)
8409 element = GFX_ELEMENT(element);
8411 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8414 int el2baseimg(int element)
8416 return element_info[element].graphic[ACTION_DEFAULT];
8419 int el2img(int element)
8421 element = GFX_ELEMENT(element);
8423 return element_info[element].graphic[ACTION_DEFAULT];
8426 int el2edimg(int element)
8428 element = GFX_ELEMENT(element);
8430 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8433 int el2preimg(int element)
8435 element = GFX_ELEMENT(element);
8437 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8440 int el2panelimg(int element)
8442 element = GFX_ELEMENT(element);
8444 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8447 int font2baseimg(int font_nr)
8449 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8452 int getBeltNrFromBeltElement(int element)
8454 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8455 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8456 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8459 int getBeltNrFromBeltActiveElement(int element)
8461 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8462 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8463 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8466 int getBeltNrFromBeltSwitchElement(int element)
8468 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8469 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8470 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8473 int getBeltDirNrFromBeltElement(int element)
8475 static int belt_base_element[4] =
8477 EL_CONVEYOR_BELT_1_LEFT,
8478 EL_CONVEYOR_BELT_2_LEFT,
8479 EL_CONVEYOR_BELT_3_LEFT,
8480 EL_CONVEYOR_BELT_4_LEFT
8483 int belt_nr = getBeltNrFromBeltElement(element);
8484 int belt_dir_nr = element - belt_base_element[belt_nr];
8486 return (belt_dir_nr % 3);
8489 int getBeltDirNrFromBeltSwitchElement(int element)
8491 static int belt_base_element[4] =
8493 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8494 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8495 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8496 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8499 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8500 int belt_dir_nr = element - belt_base_element[belt_nr];
8502 return (belt_dir_nr % 3);
8505 int getBeltDirFromBeltElement(int element)
8507 static int belt_move_dir[3] =
8514 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8516 return belt_move_dir[belt_dir_nr];
8519 int getBeltDirFromBeltSwitchElement(int element)
8521 static int belt_move_dir[3] =
8528 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8530 return belt_move_dir[belt_dir_nr];
8533 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8535 static int belt_base_element[4] =
8537 EL_CONVEYOR_BELT_1_LEFT,
8538 EL_CONVEYOR_BELT_2_LEFT,
8539 EL_CONVEYOR_BELT_3_LEFT,
8540 EL_CONVEYOR_BELT_4_LEFT
8543 return belt_base_element[belt_nr] + belt_dir_nr;
8546 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8548 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8550 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8553 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8555 static int belt_base_element[4] =
8557 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8558 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8559 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8560 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8563 return belt_base_element[belt_nr] + belt_dir_nr;
8566 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8568 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8570 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8573 boolean swapTiles_EM(boolean is_pre_emc_cave)
8575 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8578 boolean getTeamMode_EM(void)
8580 return game.team_mode || network_playing;
8583 boolean isActivePlayer_EM(int player_nr)
8585 return stored_player[player_nr].active;
8588 unsigned int InitRND(int seed)
8590 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8591 return InitEngineRandom_EM(seed);
8592 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8593 return InitEngineRandom_SP(seed);
8594 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8595 return InitEngineRandom_MM(seed);
8597 return InitEngineRandom_RND(seed);
8600 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8601 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8603 static int get_effective_element_EM(int tile, int frame_em)
8605 int element = object_mapping[tile].element_rnd;
8606 int action = object_mapping[tile].action;
8607 boolean is_backside = object_mapping[tile].is_backside;
8608 boolean action_removing = (action == ACTION_DIGGING ||
8609 action == ACTION_SNAPPING ||
8610 action == ACTION_COLLECTING);
8618 return (frame_em > 5 ? EL_EMPTY : element);
8624 else // frame_em == 7
8635 case Ydiamond_stone:
8639 case Xdrip_stretchB:
8655 case Ymagnify_blank:
8658 case Xsand_stonein_1:
8659 case Xsand_stonein_2:
8660 case Xsand_stonein_3:
8661 case Xsand_stonein_4:
8665 return (is_backside || action_removing ? EL_EMPTY : element);
8670 static boolean check_linear_animation_EM(int tile)
8674 case Xsand_stonesand_1:
8675 case Xsand_stonesand_quickout_1:
8676 case Xsand_sandstone_1:
8677 case Xsand_stonein_1:
8678 case Xsand_stoneout_1:
8706 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8707 boolean has_crumbled_graphics,
8708 int crumbled, int sync_frame)
8710 // if element can be crumbled, but certain action graphics are just empty
8711 // space (like instantly snapping sand to empty space in 1 frame), do not
8712 // treat these empty space graphics as crumbled graphics in EMC engine
8713 if (crumbled == IMG_EMPTY_SPACE)
8714 has_crumbled_graphics = FALSE;
8716 if (has_crumbled_graphics)
8718 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8719 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8720 g_crumbled->anim_delay,
8721 g_crumbled->anim_mode,
8722 g_crumbled->anim_start_frame,
8725 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8726 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8728 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8729 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8731 g_em->has_crumbled_graphics = TRUE;
8735 g_em->crumbled_bitmap = NULL;
8736 g_em->crumbled_src_x = 0;
8737 g_em->crumbled_src_y = 0;
8738 g_em->crumbled_border_size = 0;
8739 g_em->crumbled_tile_size = 0;
8741 g_em->has_crumbled_graphics = FALSE;
8746 void ResetGfxAnimation_EM(int x, int y, int tile)
8752 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8753 int tile, int frame_em, int x, int y)
8755 int action = object_mapping[tile].action;
8756 int direction = object_mapping[tile].direction;
8757 int effective_element = get_effective_element_EM(tile, frame_em);
8758 int graphic = (direction == MV_NONE ?
8759 el_act2img(effective_element, action) :
8760 el_act_dir2img(effective_element, action, direction));
8761 struct GraphicInfo *g = &graphic_info[graphic];
8763 boolean action_removing = (action == ACTION_DIGGING ||
8764 action == ACTION_SNAPPING ||
8765 action == ACTION_COLLECTING);
8766 boolean action_moving = (action == ACTION_FALLING ||
8767 action == ACTION_MOVING ||
8768 action == ACTION_PUSHING ||
8769 action == ACTION_EATING ||
8770 action == ACTION_FILLING ||
8771 action == ACTION_EMPTYING);
8772 boolean action_falling = (action == ACTION_FALLING ||
8773 action == ACTION_FILLING ||
8774 action == ACTION_EMPTYING);
8776 // special case: graphic uses "2nd movement tile" and has defined
8777 // 7 frames for movement animation (or less) => use default graphic
8778 // for last (8th) frame which ends the movement animation
8779 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8781 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8782 graphic = (direction == MV_NONE ?
8783 el_act2img(effective_element, action) :
8784 el_act_dir2img(effective_element, action, direction));
8786 g = &graphic_info[graphic];
8789 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8793 else if (action_moving)
8795 boolean is_backside = object_mapping[tile].is_backside;
8799 int direction = object_mapping[tile].direction;
8800 int move_dir = (action_falling ? MV_DOWN : direction);
8805 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8806 if (g->double_movement && frame_em == 0)
8810 if (move_dir == MV_LEFT)
8811 GfxFrame[x - 1][y] = GfxFrame[x][y];
8812 else if (move_dir == MV_RIGHT)
8813 GfxFrame[x + 1][y] = GfxFrame[x][y];
8814 else if (move_dir == MV_UP)
8815 GfxFrame[x][y - 1] = GfxFrame[x][y];
8816 else if (move_dir == MV_DOWN)
8817 GfxFrame[x][y + 1] = GfxFrame[x][y];
8824 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8825 if (tile == Xsand_stonesand_quickout_1 ||
8826 tile == Xsand_stonesand_quickout_2)
8830 if (graphic_info[graphic].anim_global_sync)
8831 sync_frame = FrameCounter;
8832 else if (graphic_info[graphic].anim_global_anim_sync)
8833 sync_frame = getGlobalAnimSyncFrame();
8834 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8835 sync_frame = GfxFrame[x][y];
8837 sync_frame = 0; // playfield border (pseudo steel)
8839 SetRandomAnimationValue(x, y);
8841 int frame = getAnimationFrame(g->anim_frames,
8844 g->anim_start_frame,
8847 g_em->unique_identifier =
8848 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8851 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8852 int tile, int frame_em, int x, int y)
8854 int action = object_mapping[tile].action;
8855 int direction = object_mapping[tile].direction;
8856 boolean is_backside = object_mapping[tile].is_backside;
8857 int effective_element = get_effective_element_EM(tile, frame_em);
8858 int effective_action = action;
8859 int graphic = (direction == MV_NONE ?
8860 el_act2img(effective_element, effective_action) :
8861 el_act_dir2img(effective_element, effective_action,
8863 int crumbled = (direction == MV_NONE ?
8864 el_act2crm(effective_element, effective_action) :
8865 el_act_dir2crm(effective_element, effective_action,
8867 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8868 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8869 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8870 struct GraphicInfo *g = &graphic_info[graphic];
8873 // special case: graphic uses "2nd movement tile" and has defined
8874 // 7 frames for movement animation (or less) => use default graphic
8875 // for last (8th) frame which ends the movement animation
8876 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8878 effective_action = ACTION_DEFAULT;
8879 graphic = (direction == MV_NONE ?
8880 el_act2img(effective_element, effective_action) :
8881 el_act_dir2img(effective_element, effective_action,
8883 crumbled = (direction == MV_NONE ?
8884 el_act2crm(effective_element, effective_action) :
8885 el_act_dir2crm(effective_element, effective_action,
8888 g = &graphic_info[graphic];
8891 if (graphic_info[graphic].anim_global_sync)
8892 sync_frame = FrameCounter;
8893 else if (graphic_info[graphic].anim_global_anim_sync)
8894 sync_frame = getGlobalAnimSyncFrame();
8895 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8896 sync_frame = GfxFrame[x][y];
8898 sync_frame = 0; // playfield border (pseudo steel)
8900 SetRandomAnimationValue(x, y);
8902 int frame = getAnimationFrame(g->anim_frames,
8905 g->anim_start_frame,
8908 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8909 g->double_movement && is_backside);
8911 // (updating the "crumbled" graphic definitions is probably not really needed,
8912 // as animations for crumbled graphics can't be longer than one EMC cycle)
8913 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8917 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8918 int player_nr, int anim, int frame_em)
8920 int element = player_mapping[player_nr][anim].element_rnd;
8921 int action = player_mapping[player_nr][anim].action;
8922 int direction = player_mapping[player_nr][anim].direction;
8923 int graphic = (direction == MV_NONE ?
8924 el_act2img(element, action) :
8925 el_act_dir2img(element, action, direction));
8926 struct GraphicInfo *g = &graphic_info[graphic];
8929 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8931 stored_player[player_nr].StepFrame = frame_em;
8933 sync_frame = stored_player[player_nr].Frame;
8935 int frame = getAnimationFrame(g->anim_frames,
8938 g->anim_start_frame,
8941 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8942 &g_em->src_x, &g_em->src_y, FALSE);
8945 void InitGraphicInfo_EM(void)
8949 // always start with reliable default values
8950 for (i = 0; i < GAME_TILE_MAX; i++)
8952 object_mapping[i].element_rnd = EL_UNKNOWN;
8953 object_mapping[i].is_backside = FALSE;
8954 object_mapping[i].action = ACTION_DEFAULT;
8955 object_mapping[i].direction = MV_NONE;
8958 // always start with reliable default values
8959 for (p = 0; p < MAX_PLAYERS; p++)
8961 for (i = 0; i < PLY_MAX; i++)
8963 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8964 player_mapping[p][i].action = ACTION_DEFAULT;
8965 player_mapping[p][i].direction = MV_NONE;
8969 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8971 int e = em_object_mapping_list[i].element_em;
8973 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8974 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8976 if (em_object_mapping_list[i].action != -1)
8977 object_mapping[e].action = em_object_mapping_list[i].action;
8979 if (em_object_mapping_list[i].direction != -1)
8980 object_mapping[e].direction =
8981 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8984 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8986 int a = em_player_mapping_list[i].action_em;
8987 int p = em_player_mapping_list[i].player_nr;
8989 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8991 if (em_player_mapping_list[i].action != -1)
8992 player_mapping[p][a].action = em_player_mapping_list[i].action;
8994 if (em_player_mapping_list[i].direction != -1)
8995 player_mapping[p][a].direction =
8996 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8999 for (i = 0; i < GAME_TILE_MAX; i++)
9001 int element = object_mapping[i].element_rnd;
9002 int action = object_mapping[i].action;
9003 int direction = object_mapping[i].direction;
9004 boolean is_backside = object_mapping[i].is_backside;
9005 boolean action_exploding = ((action == ACTION_EXPLODING ||
9006 action == ACTION_SMASHED_BY_ROCK ||
9007 action == ACTION_SMASHED_BY_SPRING) &&
9008 element != EL_DIAMOND);
9009 boolean action_active = (action == ACTION_ACTIVE);
9010 boolean action_other = (action == ACTION_OTHER);
9012 for (j = 0; j < 8; j++)
9014 int effective_element = get_effective_element_EM(i, j);
9015 int effective_action = (j < 7 ? action :
9016 i == Xdrip_stretch ? action :
9017 i == Xdrip_stretchB ? action :
9018 i == Ydrip_1_s ? action :
9019 i == Ydrip_1_sB ? action :
9020 i == Yball_1 ? action :
9021 i == Xball_2 ? action :
9022 i == Yball_2 ? action :
9023 i == Yball_blank ? action :
9024 i == Ykey_1_blank ? action :
9025 i == Ykey_2_blank ? action :
9026 i == Ykey_3_blank ? action :
9027 i == Ykey_4_blank ? action :
9028 i == Ykey_5_blank ? action :
9029 i == Ykey_6_blank ? action :
9030 i == Ykey_7_blank ? action :
9031 i == Ykey_8_blank ? action :
9032 i == Ylenses_blank ? action :
9033 i == Ymagnify_blank ? action :
9034 i == Ygrass_blank ? action :
9035 i == Ydirt_blank ? action :
9036 i == Xsand_stonein_1 ? action :
9037 i == Xsand_stonein_2 ? action :
9038 i == Xsand_stonein_3 ? action :
9039 i == Xsand_stonein_4 ? action :
9040 i == Xsand_stoneout_1 ? action :
9041 i == Xsand_stoneout_2 ? action :
9042 i == Xboom_android ? ACTION_EXPLODING :
9043 action_exploding ? ACTION_EXPLODING :
9044 action_active ? action :
9045 action_other ? action :
9047 int graphic = (el_act_dir2img(effective_element, effective_action,
9049 int crumbled = (el_act_dir2crm(effective_element, effective_action,
9051 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
9052 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
9053 boolean has_action_graphics = (graphic != base_graphic);
9054 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9055 struct GraphicInfo *g = &graphic_info[graphic];
9056 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9059 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9060 boolean special_animation = (action != ACTION_DEFAULT &&
9061 g->anim_frames == 3 &&
9062 g->anim_delay == 2 &&
9063 g->anim_mode & ANIM_LINEAR);
9064 int sync_frame = (i == Xdrip_stretch ? 7 :
9065 i == Xdrip_stretchB ? 7 :
9066 i == Ydrip_2_s ? j + 8 :
9067 i == Ydrip_2_sB ? j + 8 :
9076 i == Xfake_acid_1 ? 0 :
9077 i == Xfake_acid_2 ? 10 :
9078 i == Xfake_acid_3 ? 20 :
9079 i == Xfake_acid_4 ? 30 :
9080 i == Xfake_acid_5 ? 40 :
9081 i == Xfake_acid_6 ? 50 :
9082 i == Xfake_acid_7 ? 60 :
9083 i == Xfake_acid_8 ? 70 :
9084 i == Xfake_acid_1_player ? 0 :
9085 i == Xfake_acid_2_player ? 10 :
9086 i == Xfake_acid_3_player ? 20 :
9087 i == Xfake_acid_4_player ? 30 :
9088 i == Xfake_acid_5_player ? 40 :
9089 i == Xfake_acid_6_player ? 50 :
9090 i == Xfake_acid_7_player ? 60 :
9091 i == Xfake_acid_8_player ? 70 :
9093 i == Yball_2 ? j + 8 :
9094 i == Yball_blank ? j + 1 :
9095 i == Ykey_1_blank ? j + 1 :
9096 i == Ykey_2_blank ? j + 1 :
9097 i == Ykey_3_blank ? j + 1 :
9098 i == Ykey_4_blank ? j + 1 :
9099 i == Ykey_5_blank ? j + 1 :
9100 i == Ykey_6_blank ? j + 1 :
9101 i == Ykey_7_blank ? j + 1 :
9102 i == Ykey_8_blank ? j + 1 :
9103 i == Ylenses_blank ? j + 1 :
9104 i == Ymagnify_blank ? j + 1 :
9105 i == Ygrass_blank ? j + 1 :
9106 i == Ydirt_blank ? j + 1 :
9107 i == Xamoeba_1 ? 0 :
9108 i == Xamoeba_2 ? 1 :
9109 i == Xamoeba_3 ? 2 :
9110 i == Xamoeba_4 ? 3 :
9111 i == Xamoeba_5 ? 0 :
9112 i == Xamoeba_6 ? 1 :
9113 i == Xamoeba_7 ? 2 :
9114 i == Xamoeba_8 ? 3 :
9115 i == Xexit_2 ? j + 8 :
9116 i == Xexit_3 ? j + 16 :
9117 i == Xdynamite_1 ? 0 :
9118 i == Xdynamite_2 ? 8 :
9119 i == Xdynamite_3 ? 16 :
9120 i == Xdynamite_4 ? 24 :
9121 i == Xsand_stonein_1 ? j + 1 :
9122 i == Xsand_stonein_2 ? j + 9 :
9123 i == Xsand_stonein_3 ? j + 17 :
9124 i == Xsand_stonein_4 ? j + 25 :
9125 i == Xsand_stoneout_1 && j == 0 ? 0 :
9126 i == Xsand_stoneout_1 && j == 1 ? 0 :
9127 i == Xsand_stoneout_1 && j == 2 ? 1 :
9128 i == Xsand_stoneout_1 && j == 3 ? 2 :
9129 i == Xsand_stoneout_1 && j == 4 ? 2 :
9130 i == Xsand_stoneout_1 && j == 5 ? 3 :
9131 i == Xsand_stoneout_1 && j == 6 ? 4 :
9132 i == Xsand_stoneout_1 && j == 7 ? 4 :
9133 i == Xsand_stoneout_2 && j == 0 ? 5 :
9134 i == Xsand_stoneout_2 && j == 1 ? 6 :
9135 i == Xsand_stoneout_2 && j == 2 ? 7 :
9136 i == Xsand_stoneout_2 && j == 3 ? 8 :
9137 i == Xsand_stoneout_2 && j == 4 ? 9 :
9138 i == Xsand_stoneout_2 && j == 5 ? 11 :
9139 i == Xsand_stoneout_2 && j == 6 ? 13 :
9140 i == Xsand_stoneout_2 && j == 7 ? 15 :
9141 i == Xboom_bug && j == 1 ? 2 :
9142 i == Xboom_bug && j == 2 ? 2 :
9143 i == Xboom_bug && j == 3 ? 4 :
9144 i == Xboom_bug && j == 4 ? 4 :
9145 i == Xboom_bug && j == 5 ? 2 :
9146 i == Xboom_bug && j == 6 ? 2 :
9147 i == Xboom_bug && j == 7 ? 0 :
9148 i == Xboom_tank && j == 1 ? 2 :
9149 i == Xboom_tank && j == 2 ? 2 :
9150 i == Xboom_tank && j == 3 ? 4 :
9151 i == Xboom_tank && j == 4 ? 4 :
9152 i == Xboom_tank && j == 5 ? 2 :
9153 i == Xboom_tank && j == 6 ? 2 :
9154 i == Xboom_tank && j == 7 ? 0 :
9155 i == Xboom_android && j == 7 ? 6 :
9156 i == Xboom_1 && j == 1 ? 2 :
9157 i == Xboom_1 && j == 2 ? 2 :
9158 i == Xboom_1 && j == 3 ? 4 :
9159 i == Xboom_1 && j == 4 ? 4 :
9160 i == Xboom_1 && j == 5 ? 6 :
9161 i == Xboom_1 && j == 6 ? 6 :
9162 i == Xboom_1 && j == 7 ? 8 :
9163 i == Xboom_2 && j == 0 ? 8 :
9164 i == Xboom_2 && j == 1 ? 8 :
9165 i == Xboom_2 && j == 2 ? 10 :
9166 i == Xboom_2 && j == 3 ? 10 :
9167 i == Xboom_2 && j == 4 ? 10 :
9168 i == Xboom_2 && j == 5 ? 12 :
9169 i == Xboom_2 && j == 6 ? 12 :
9170 i == Xboom_2 && j == 7 ? 12 :
9171 special_animation && j == 4 ? 3 :
9172 effective_action != action ? 0 :
9174 int frame = getAnimationFrame(g->anim_frames,
9177 g->anim_start_frame,
9180 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9181 g->double_movement && is_backside);
9183 g_em->bitmap = src_bitmap;
9184 g_em->src_x = src_x;
9185 g_em->src_y = src_y;
9186 g_em->src_offset_x = 0;
9187 g_em->src_offset_y = 0;
9188 g_em->dst_offset_x = 0;
9189 g_em->dst_offset_y = 0;
9190 g_em->width = TILEX;
9191 g_em->height = TILEY;
9193 g_em->preserve_background = FALSE;
9195 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9198 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9199 effective_action == ACTION_MOVING ||
9200 effective_action == ACTION_PUSHING ||
9201 effective_action == ACTION_EATING)) ||
9202 (!has_action_graphics && (effective_action == ACTION_FILLING ||
9203 effective_action == ACTION_EMPTYING)))
9206 (effective_action == ACTION_FALLING ||
9207 effective_action == ACTION_FILLING ||
9208 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9209 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9210 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
9211 int num_steps = (i == Ydrip_1_s ? 16 :
9212 i == Ydrip_1_sB ? 16 :
9213 i == Ydrip_2_s ? 16 :
9214 i == Ydrip_2_sB ? 16 :
9215 i == Xsand_stonein_1 ? 32 :
9216 i == Xsand_stonein_2 ? 32 :
9217 i == Xsand_stonein_3 ? 32 :
9218 i == Xsand_stonein_4 ? 32 :
9219 i == Xsand_stoneout_1 ? 16 :
9220 i == Xsand_stoneout_2 ? 16 : 8);
9221 int cx = ABS(dx) * (TILEX / num_steps);
9222 int cy = ABS(dy) * (TILEY / num_steps);
9223 int step_frame = (i == Ydrip_2_s ? j + 8 :
9224 i == Ydrip_2_sB ? j + 8 :
9225 i == Xsand_stonein_2 ? j + 8 :
9226 i == Xsand_stonein_3 ? j + 16 :
9227 i == Xsand_stonein_4 ? j + 24 :
9228 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9229 int step = (is_backside ? step_frame : num_steps - step_frame);
9231 if (is_backside) // tile where movement starts
9233 if (dx < 0 || dy < 0)
9235 g_em->src_offset_x = cx * step;
9236 g_em->src_offset_y = cy * step;
9240 g_em->dst_offset_x = cx * step;
9241 g_em->dst_offset_y = cy * step;
9244 else // tile where movement ends
9246 if (dx < 0 || dy < 0)
9248 g_em->dst_offset_x = cx * step;
9249 g_em->dst_offset_y = cy * step;
9253 g_em->src_offset_x = cx * step;
9254 g_em->src_offset_y = cy * step;
9258 g_em->width = TILEX - cx * step;
9259 g_em->height = TILEY - cy * step;
9262 // create unique graphic identifier to decide if tile must be redrawn
9263 /* bit 31 - 16 (16 bit): EM style graphic
9264 bit 15 - 12 ( 4 bit): EM style frame
9265 bit 11 - 6 ( 6 bit): graphic width
9266 bit 5 - 0 ( 6 bit): graphic height */
9267 g_em->unique_identifier =
9268 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9272 for (i = 0; i < GAME_TILE_MAX; i++)
9274 for (j = 0; j < 8; j++)
9276 int element = object_mapping[i].element_rnd;
9277 int action = object_mapping[i].action;
9278 int direction = object_mapping[i].direction;
9279 boolean is_backside = object_mapping[i].is_backside;
9280 int graphic_action = el_act_dir2img(element, action, direction);
9281 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9283 if ((action == ACTION_SMASHED_BY_ROCK ||
9284 action == ACTION_SMASHED_BY_SPRING ||
9285 action == ACTION_EATING) &&
9286 graphic_action == graphic_default)
9288 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9289 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9290 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9291 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9294 // no separate animation for "smashed by rock" -- use rock instead
9295 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9296 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9298 g_em->bitmap = g_xx->bitmap;
9299 g_em->src_x = g_xx->src_x;
9300 g_em->src_y = g_xx->src_y;
9301 g_em->src_offset_x = g_xx->src_offset_x;
9302 g_em->src_offset_y = g_xx->src_offset_y;
9303 g_em->dst_offset_x = g_xx->dst_offset_x;
9304 g_em->dst_offset_y = g_xx->dst_offset_y;
9305 g_em->width = g_xx->width;
9306 g_em->height = g_xx->height;
9307 g_em->unique_identifier = g_xx->unique_identifier;
9310 g_em->preserve_background = TRUE;
9315 for (p = 0; p < MAX_PLAYERS; p++)
9317 for (i = 0; i < PLY_MAX; i++)
9319 int element = player_mapping[p][i].element_rnd;
9320 int action = player_mapping[p][i].action;
9321 int direction = player_mapping[p][i].direction;
9323 for (j = 0; j < 8; j++)
9325 int effective_element = element;
9326 int effective_action = action;
9327 int graphic = (direction == MV_NONE ?
9328 el_act2img(effective_element, effective_action) :
9329 el_act_dir2img(effective_element, effective_action,
9331 struct GraphicInfo *g = &graphic_info[graphic];
9332 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9336 int frame = getAnimationFrame(g->anim_frames,
9339 g->anim_start_frame,
9342 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9344 g_em->bitmap = src_bitmap;
9345 g_em->src_x = src_x;
9346 g_em->src_y = src_y;
9347 g_em->src_offset_x = 0;
9348 g_em->src_offset_y = 0;
9349 g_em->dst_offset_x = 0;
9350 g_em->dst_offset_y = 0;
9351 g_em->width = TILEX;
9352 g_em->height = TILEY;
9358 static void CheckSaveEngineSnapshot_EM(int frame,
9359 boolean any_player_moving,
9360 boolean any_player_snapping,
9361 boolean any_player_dropping)
9363 if (frame == 7 && !any_player_dropping)
9365 if (!local_player->was_waiting)
9367 if (!CheckSaveEngineSnapshotToList())
9370 local_player->was_waiting = TRUE;
9373 else if (any_player_moving || any_player_snapping || any_player_dropping)
9375 local_player->was_waiting = FALSE;
9379 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9380 boolean murphy_is_dropping)
9382 if (murphy_is_waiting)
9384 if (!local_player->was_waiting)
9386 if (!CheckSaveEngineSnapshotToList())
9389 local_player->was_waiting = TRUE;
9394 local_player->was_waiting = FALSE;
9398 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9399 boolean button_released)
9401 if (button_released)
9403 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9404 CheckSaveEngineSnapshotToList();
9406 else if (element_clicked)
9408 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9409 CheckSaveEngineSnapshotToList();
9411 game.snapshot.changed_action = TRUE;
9415 boolean CheckSingleStepMode_EM(int frame,
9416 boolean any_player_moving,
9417 boolean any_player_snapping,
9418 boolean any_player_dropping)
9420 if (tape.single_step && tape.recording && !tape.pausing)
9421 if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9422 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9424 CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9425 any_player_snapping, any_player_dropping);
9427 return tape.pausing;
9430 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9431 boolean murphy_is_dropping)
9433 boolean murphy_starts_dropping = FALSE;
9436 for (i = 0; i < MAX_PLAYERS; i++)
9437 if (stored_player[i].force_dropping)
9438 murphy_starts_dropping = TRUE;
9440 if (tape.single_step && tape.recording && !tape.pausing)
9441 if (murphy_is_waiting && !murphy_starts_dropping)
9442 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9444 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9447 void CheckSingleStepMode_MM(boolean element_clicked,
9448 boolean button_released)
9450 if (tape.single_step && tape.recording && !tape.pausing)
9451 if (button_released)
9452 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9454 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9457 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9458 int graphic, int sync_frame)
9460 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9462 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9465 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9467 return (IS_NEXT_FRAME(sync_frame, graphic));
9470 int getGraphicInfo_Delay(int graphic)
9472 return graphic_info[graphic].anim_delay;
9475 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9477 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9480 if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9486 void PlayMenuSoundExt(int sound)
9488 if (sound == SND_UNDEFINED)
9491 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9492 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9495 if (IS_LOOP_SOUND(sound))
9496 PlaySoundLoop(sound);
9501 void PlayMenuSound(void)
9503 PlayMenuSoundExt(menu.sound[game_status]);
9506 void PlayMenuSoundStereo(int sound, int stereo_position)
9508 if (sound == SND_UNDEFINED)
9511 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9512 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9515 if (IS_LOOP_SOUND(sound))
9516 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9518 PlaySoundStereo(sound, stereo_position);
9521 void PlayMenuSoundIfLoopExt(int sound)
9523 if (sound == SND_UNDEFINED)
9526 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9527 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9530 if (IS_LOOP_SOUND(sound))
9531 PlaySoundLoop(sound);
9534 void PlayMenuSoundIfLoop(void)
9536 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9539 void PlayMenuMusicExt(int music)
9541 if (music == MUS_UNDEFINED)
9544 if (!setup.sound_music)
9547 if (IS_LOOP_MUSIC(music))
9548 PlayMusicLoop(music);
9553 void PlayMenuMusic(void)
9555 char *curr_music = getCurrentlyPlayingMusicFilename();
9556 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9558 if (!strEqual(curr_music, next_music))
9559 PlayMenuMusicExt(menu.music[game_status]);
9562 void PlayMenuSoundsAndMusic(void)
9568 static void FadeMenuSounds(void)
9573 static void FadeMenuMusic(void)
9575 char *curr_music = getCurrentlyPlayingMusicFilename();
9576 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9578 if (!strEqual(curr_music, next_music))
9582 void FadeMenuSoundsAndMusic(void)
9588 void PlaySoundActivating(void)
9591 PlaySound(SND_MENU_ITEM_ACTIVATING);
9595 void PlaySoundSelecting(void)
9598 PlaySound(SND_MENU_ITEM_SELECTING);
9602 void ToggleFullscreenIfNeeded(void)
9604 // if setup and video fullscreen state are already matching, nothing do do
9605 if (setup.fullscreen == video.fullscreen_enabled ||
9606 !video.fullscreen_available)
9609 SDLSetWindowFullscreen(setup.fullscreen);
9611 // set setup value according to successfully changed fullscreen mode
9612 setup.fullscreen = video.fullscreen_enabled;
9615 void ChangeWindowScalingIfNeeded(void)
9617 // if setup and video window scaling are already matching, nothing do do
9618 if (setup.window_scaling_percent == video.window_scaling_percent ||
9619 video.fullscreen_enabled)
9622 SDLSetWindowScaling(setup.window_scaling_percent);
9624 // set setup value according to successfully changed window scaling
9625 setup.window_scaling_percent = video.window_scaling_percent;
9628 void ChangeVsyncModeIfNeeded(void)
9630 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9631 int video_vsync_mode = video.vsync_mode;
9633 // if setup and video vsync mode are already matching, nothing do do
9634 if (setup_vsync_mode == video_vsync_mode)
9637 // if renderer is using OpenGL, vsync mode can directly be changed
9638 SDLSetScreenVsyncMode(setup.vsync_mode);
9640 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9641 if (video.vsync_mode == video_vsync_mode)
9643 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9645 // save backbuffer content which gets lost when re-creating screen
9646 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9648 // force re-creating screen and renderer to set new vsync mode
9649 video.fullscreen_enabled = !setup.fullscreen;
9651 // when creating new renderer, destroy textures linked to old renderer
9652 FreeAllImageTextures(); // needs old renderer to free the textures
9654 // re-create screen and renderer (including change of vsync mode)
9655 ChangeVideoModeIfNeeded(setup.fullscreen);
9657 // set setup value according to successfully changed fullscreen mode
9658 setup.fullscreen = video.fullscreen_enabled;
9660 // restore backbuffer content from temporary backbuffer backup bitmap
9661 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9662 FreeBitmap(tmp_backbuffer);
9664 // update visible window/screen
9665 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9667 // when changing vsync mode, re-create textures for new renderer
9668 InitImageTextures();
9671 // set setup value according to successfully changed vsync mode
9672 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9675 static void JoinRectangles(int *x, int *y, int *width, int *height,
9676 int x2, int y2, int width2, int height2)
9678 // do not join with "off-screen" rectangle
9679 if (x2 == -1 || y2 == -1)
9684 *width = MAX(*width, width2);
9685 *height = MAX(*height, height2);
9688 void SetAnimStatus(int anim_status_new)
9690 if (anim_status_new == GAME_MODE_MAIN)
9691 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9692 else if (anim_status_new == GAME_MODE_NAMES)
9693 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9694 else if (anim_status_new == GAME_MODE_SCORES)
9695 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9697 global.anim_status_next = anim_status_new;
9699 // directly set screen modes that are entered without fading
9700 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9701 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9702 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9703 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9704 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9705 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9706 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9707 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9708 global.anim_status = global.anim_status_next;
9711 void SetGameStatus(int game_status_new)
9713 if (game_status_new != game_status)
9714 game_status_last_screen = game_status;
9716 game_status = game_status_new;
9718 SetAnimStatus(game_status_new);
9721 void SetFontStatus(int game_status_new)
9723 static int last_game_status = -1;
9725 if (game_status_new != -1)
9727 // set game status for font use after storing last game status
9728 last_game_status = game_status;
9729 game_status = game_status_new;
9733 // reset game status after font use from last stored game status
9734 game_status = last_game_status;
9738 void ResetFontStatus(void)
9743 void SetLevelSetInfo(char *identifier, int level_nr)
9745 setString(&levelset.identifier, identifier);
9747 levelset.level_nr = level_nr;
9750 boolean CheckIfAllViewportsHaveChanged(void)
9752 // if game status has not changed, viewports have not changed either
9753 if (game_status == game_status_last)
9756 // check if all viewports have changed with current game status
9758 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9759 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9760 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9761 int new_real_sx = vp_playfield->x;
9762 int new_real_sy = vp_playfield->y;
9763 int new_full_sxsize = vp_playfield->width;
9764 int new_full_sysize = vp_playfield->height;
9765 int new_dx = vp_door_1->x;
9766 int new_dy = vp_door_1->y;
9767 int new_dxsize = vp_door_1->width;
9768 int new_dysize = vp_door_1->height;
9769 int new_vx = vp_door_2->x;
9770 int new_vy = vp_door_2->y;
9771 int new_vxsize = vp_door_2->width;
9772 int new_vysize = vp_door_2->height;
9774 boolean playfield_viewport_has_changed =
9775 (new_real_sx != REAL_SX ||
9776 new_real_sy != REAL_SY ||
9777 new_full_sxsize != FULL_SXSIZE ||
9778 new_full_sysize != FULL_SYSIZE);
9780 boolean door_1_viewport_has_changed =
9783 new_dxsize != DXSIZE ||
9784 new_dysize != DYSIZE);
9786 boolean door_2_viewport_has_changed =
9789 new_vxsize != VXSIZE ||
9790 new_vysize != VYSIZE ||
9791 game_status_last == GAME_MODE_EDITOR);
9793 return (playfield_viewport_has_changed &&
9794 door_1_viewport_has_changed &&
9795 door_2_viewport_has_changed);
9798 boolean CheckFadeAll(void)
9800 return (CheckIfGlobalBorderHasChanged() ||
9801 CheckIfAllViewportsHaveChanged());
9804 void ChangeViewportPropertiesIfNeeded(void)
9806 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9807 FALSE : setup.small_game_graphics);
9808 int gfx_game_mode = getGlobalGameStatus(game_status);
9809 int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9811 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9812 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9813 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9814 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9815 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9816 int new_win_xsize = vp_window->width;
9817 int new_win_ysize = vp_window->height;
9818 int border_left = vp_playfield->border_left;
9819 int border_right = vp_playfield->border_right;
9820 int border_top = vp_playfield->border_top;
9821 int border_bottom = vp_playfield->border_bottom;
9822 int new_sx = vp_playfield->x + border_left;
9823 int new_sy = vp_playfield->y + border_top;
9824 int new_sxsize = vp_playfield->width - border_left - border_right;
9825 int new_sysize = vp_playfield->height - border_top - border_bottom;
9826 int new_real_sx = vp_playfield->x;
9827 int new_real_sy = vp_playfield->y;
9828 int new_full_sxsize = vp_playfield->width;
9829 int new_full_sysize = vp_playfield->height;
9830 int new_dx = vp_door_1->x;
9831 int new_dy = vp_door_1->y;
9832 int new_dxsize = vp_door_1->width;
9833 int new_dysize = vp_door_1->height;
9834 int new_vx = vp_door_2->x;
9835 int new_vy = vp_door_2->y;
9836 int new_vxsize = vp_door_2->width;
9837 int new_vysize = vp_door_2->height;
9838 int new_ex = vp_door_3->x;
9839 int new_ey = vp_door_3->y;
9840 int new_exsize = vp_door_3->width;
9841 int new_eysize = vp_door_3->height;
9842 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9843 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9844 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9845 int new_scr_fieldx = new_sxsize / tilesize;
9846 int new_scr_fieldy = new_sysize / tilesize;
9847 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9848 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9849 boolean init_gfx_buffers = FALSE;
9850 boolean init_video_buffer = FALSE;
9851 boolean init_gadgets_and_anims = FALSE;
9852 boolean init_em_graphics = FALSE;
9854 if (new_win_xsize != WIN_XSIZE ||
9855 new_win_ysize != WIN_YSIZE)
9857 WIN_XSIZE = new_win_xsize;
9858 WIN_YSIZE = new_win_ysize;
9860 init_video_buffer = TRUE;
9861 init_gfx_buffers = TRUE;
9862 init_gadgets_and_anims = TRUE;
9864 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9867 if (new_scr_fieldx != SCR_FIELDX ||
9868 new_scr_fieldy != SCR_FIELDY)
9870 // this always toggles between MAIN and GAME when using small tile size
9872 SCR_FIELDX = new_scr_fieldx;
9873 SCR_FIELDY = new_scr_fieldy;
9875 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9886 new_sxsize != SXSIZE ||
9887 new_sysize != SYSIZE ||
9888 new_dxsize != DXSIZE ||
9889 new_dysize != DYSIZE ||
9890 new_vxsize != VXSIZE ||
9891 new_vysize != VYSIZE ||
9892 new_exsize != EXSIZE ||
9893 new_eysize != EYSIZE ||
9894 new_real_sx != REAL_SX ||
9895 new_real_sy != REAL_SY ||
9896 new_full_sxsize != FULL_SXSIZE ||
9897 new_full_sysize != FULL_SYSIZE ||
9898 new_tilesize_var != TILESIZE_VAR
9901 // ------------------------------------------------------------------------
9902 // determine next fading area for changed viewport definitions
9903 // ------------------------------------------------------------------------
9905 // start with current playfield area (default fading area)
9908 FADE_SXSIZE = FULL_SXSIZE;
9909 FADE_SYSIZE = FULL_SYSIZE;
9911 // add new playfield area if position or size has changed
9912 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9913 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9915 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9916 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9919 // add current and new door 1 area if position or size has changed
9920 if (new_dx != DX || new_dy != DY ||
9921 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9923 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9924 DX, DY, DXSIZE, DYSIZE);
9925 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9926 new_dx, new_dy, new_dxsize, new_dysize);
9929 // add current and new door 2 area if position or size has changed
9930 if (new_vx != VX || new_vy != VY ||
9931 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9933 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9934 VX, VY, VXSIZE, VYSIZE);
9935 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9936 new_vx, new_vy, new_vxsize, new_vysize);
9939 // ------------------------------------------------------------------------
9940 // handle changed tile size
9941 // ------------------------------------------------------------------------
9943 if (new_tilesize_var != TILESIZE_VAR)
9945 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9947 // changing tile size invalidates scroll values of engine snapshots
9948 FreeEngineSnapshotSingle();
9950 // changing tile size requires update of graphic mapping for EM engine
9951 init_em_graphics = TRUE;
9962 SXSIZE = new_sxsize;
9963 SYSIZE = new_sysize;
9964 DXSIZE = new_dxsize;
9965 DYSIZE = new_dysize;
9966 VXSIZE = new_vxsize;
9967 VYSIZE = new_vysize;
9968 EXSIZE = new_exsize;
9969 EYSIZE = new_eysize;
9970 REAL_SX = new_real_sx;
9971 REAL_SY = new_real_sy;
9972 FULL_SXSIZE = new_full_sxsize;
9973 FULL_SYSIZE = new_full_sysize;
9974 TILESIZE_VAR = new_tilesize_var;
9976 init_gfx_buffers = TRUE;
9977 init_gadgets_and_anims = TRUE;
9979 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9980 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9983 if (init_gfx_buffers)
9985 // Debug("tools:viewport", "init_gfx_buffers");
9987 SCR_FIELDX = new_scr_fieldx_buffers;
9988 SCR_FIELDY = new_scr_fieldy_buffers;
9992 SCR_FIELDX = new_scr_fieldx;
9993 SCR_FIELDY = new_scr_fieldy;
9995 SetDrawDeactivationMask(REDRAW_NONE);
9996 SetDrawBackgroundMask(REDRAW_FIELD);
9999 if (init_video_buffer)
10001 // Debug("tools:viewport", "init_video_buffer");
10003 FreeAllImageTextures(); // needs old renderer to free the textures
10005 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
10006 InitImageTextures();
10009 if (init_gadgets_and_anims)
10011 // Debug("tools:viewport", "init_gadgets_and_anims");
10014 InitGlobalAnimations();
10017 if (init_em_graphics)
10019 InitGraphicInfo_EM();
10023 void OpenURL(char *url)
10025 #if SDL_VERSION_ATLEAST(2,0,14)
10028 Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
10029 url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
10030 Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
10034 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
10036 OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
10040 // ============================================================================
10042 // ============================================================================
10044 #if defined(PLATFORM_WINDOWS)
10045 /* FILETIME of Jan 1 1970 00:00:00. */
10046 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
10049 * timezone information is stored outside the kernel so tzp isn't used anymore.
10051 * Note: this function is not for Win32 high precision timing purpose. See
10054 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
10056 FILETIME file_time;
10057 SYSTEMTIME system_time;
10058 ULARGE_INTEGER ularge;
10060 GetSystemTime(&system_time);
10061 SystemTimeToFileTime(&system_time, &file_time);
10062 ularge.LowPart = file_time.dwLowDateTime;
10063 ularge.HighPart = file_time.dwHighDateTime;
10065 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10066 tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10072 static char *test_init_uuid_random_function_simple(void)
10074 static char seed_text[100];
10075 unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10077 sprintf(seed_text, "%d", seed);
10082 static char *test_init_uuid_random_function_better(void)
10084 static char seed_text[100];
10085 struct timeval current_time;
10087 gettimeofday(¤t_time, NULL);
10089 prng_seed_bytes(¤t_time, sizeof(current_time));
10091 sprintf(seed_text, "%ld.%ld",
10092 (long)current_time.tv_sec,
10093 (long)current_time.tv_usec);
10098 #if defined(PLATFORM_WINDOWS)
10099 static char *test_init_uuid_random_function_better_windows(void)
10101 static char seed_text[100];
10102 struct timeval current_time;
10104 gettimeofday_windows(¤t_time, NULL);
10106 prng_seed_bytes(¤t_time, sizeof(current_time));
10108 sprintf(seed_text, "%ld.%ld",
10109 (long)current_time.tv_sec,
10110 (long)current_time.tv_usec);
10116 static unsigned int test_uuid_random_function_simple(int max)
10118 return GetSimpleRandom(max);
10121 static unsigned int test_uuid_random_function_better(int max)
10123 return (max > 0 ? prng_get_uint() % max : 0);
10126 #if defined(PLATFORM_WINDOWS)
10127 #define NUM_UUID_TESTS 3
10129 #define NUM_UUID_TESTS 2
10132 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10134 struct hashtable *hash_seeds =
10135 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10136 struct hashtable *hash_uuids =
10137 create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10138 static char message[100];
10141 char *random_name = (nr == 0 ? "simple" : "better");
10142 char *random_type = (always_seed ? "always" : "only once");
10143 char *(*init_random_function)(void) =
10145 test_init_uuid_random_function_simple :
10146 test_init_uuid_random_function_better);
10147 unsigned int (*random_function)(int) =
10149 test_uuid_random_function_simple :
10150 test_uuid_random_function_better);
10153 #if defined(PLATFORM_WINDOWS)
10156 random_name = "windows";
10157 init_random_function = test_init_uuid_random_function_better_windows;
10163 DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10164 DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10166 DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10167 DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10168 DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10170 DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10174 // always initialize random number generator at least once
10175 init_random_function();
10177 unsigned int time_start = SDL_GetTicks();
10179 for (i = 0; i < num_uuids; i++)
10183 char *seed = getStringCopy(init_random_function());
10185 hashtable_remove(hash_seeds, seed);
10186 hashtable_insert(hash_seeds, seed, "1");
10189 char *uuid = getStringCopy(getUUIDExt(random_function));
10191 hashtable_remove(hash_uuids, uuid);
10192 hashtable_insert(hash_uuids, uuid, "1");
10195 int num_unique_seeds = hashtable_count(hash_seeds);
10196 int num_unique_uuids = hashtable_count(hash_uuids);
10198 unsigned int time_needed = SDL_GetTicks() - time_start;
10200 DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10202 DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10205 DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10207 if (nr == NUM_UUID_TESTS - 1 && always_seed)
10208 DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10210 DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10212 sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10214 Request(message, REQ_CONFIRM);
10216 hashtable_destroy(hash_seeds, 0);
10217 hashtable_destroy(hash_uuids, 0);
10220 void TestGeneratingUUIDs(void)
10222 int num_uuids = 1000000;
10225 for (i = 0; i < NUM_UUID_TESTS; i++)
10226 for (j = 0; j < 2; j++)
10227 TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10229 CloseAllAndExit(0);