1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 /* select level set with EMC X11 graphics before activating EM GFX debugging */
27 #define DEBUG_EM_GFX FALSE
28 #define DEBUG_FRAME_TIME FALSE
30 /* tool button identifiers */
31 #define TOOL_CTRL_ID_YES 0
32 #define TOOL_CTRL_ID_NO 1
33 #define TOOL_CTRL_ID_CONFIRM 2
34 #define TOOL_CTRL_ID_PLAYER_1 3
35 #define TOOL_CTRL_ID_PLAYER_2 4
36 #define TOOL_CTRL_ID_PLAYER_3 5
37 #define TOOL_CTRL_ID_PLAYER_4 6
39 #define NUM_TOOL_BUTTONS 7
41 /* constants for number of doors and door parts */
43 #define NUM_PANELS NUM_DOORS
44 // #define NUM_PANELS 0
45 #define MAX_PARTS_PER_DOOR 8
46 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
47 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
50 struct DoorPartOrderInfo
56 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
58 struct DoorPartControlInfo
62 struct DoorPartPosInfo *pos;
65 static struct DoorPartControlInfo door_part_controls[] =
69 IMG_GFX_DOOR_1_PART_1,
74 IMG_GFX_DOOR_1_PART_2,
79 IMG_GFX_DOOR_1_PART_3,
84 IMG_GFX_DOOR_1_PART_4,
89 IMG_GFX_DOOR_1_PART_5,
94 IMG_GFX_DOOR_1_PART_6,
99 IMG_GFX_DOOR_1_PART_7,
104 IMG_GFX_DOOR_1_PART_8,
110 IMG_GFX_DOOR_2_PART_1,
115 IMG_GFX_DOOR_2_PART_2,
120 IMG_GFX_DOOR_2_PART_3,
125 IMG_GFX_DOOR_2_PART_4,
130 IMG_GFX_DOOR_2_PART_5,
135 IMG_GFX_DOOR_2_PART_6,
140 IMG_GFX_DOOR_2_PART_7,
145 IMG_GFX_DOOR_2_PART_8,
151 IMG_BACKGROUND_PANEL,
168 /* forward declaration for internal use */
169 static void UnmapToolButtons();
170 static void HandleToolButtons(struct GadgetInfo *);
171 static int el_act_dir2crm(int, int, int);
172 static int el_act2crm(int, int);
174 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
175 static int request_gadget_id = -1;
177 static char *print_if_not_empty(int element)
179 static char *s = NULL;
180 char *token_name = element_info[element].token_name;
185 s = checked_malloc(strlen(token_name) + 10 + 1);
187 if (element != EL_EMPTY)
188 sprintf(s, "%d\t['%s']", element, token_name);
190 sprintf(s, "%d", element);
195 static int getFieldbufferOffsetX_RND()
197 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
198 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
199 int dx_var = dx * TILESIZE_VAR / TILESIZE;
202 if (EVEN(SCR_FIELDX))
204 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
206 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
207 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
209 fx += (dx_var > 0 ? TILEX_VAR : 0);
216 if (full_lev_fieldx <= SCR_FIELDX)
218 if (EVEN(SCR_FIELDX))
219 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
221 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
227 static int getFieldbufferOffsetY_RND()
229 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
230 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
231 int dy_var = dy * TILESIZE_VAR / TILESIZE;
234 if (EVEN(SCR_FIELDY))
236 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
238 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
239 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
241 fy += (dy_var > 0 ? TILEY_VAR : 0);
248 if (full_lev_fieldy <= SCR_FIELDY)
250 if (EVEN(SCR_FIELDY))
251 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
253 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
259 static int getLevelFromScreenX_RND(int sx)
261 int fx = getFieldbufferOffsetX_RND();
264 int lx = LEVELX((px + dx) / TILESIZE_VAR);
269 static int getLevelFromScreenY_RND(int sy)
271 int fy = getFieldbufferOffsetY_RND();
274 int ly = LEVELY((py + dy) / TILESIZE_VAR);
279 static int getLevelFromScreenX_EM(int sx)
281 int level_xsize = level.native_em_level->lev->width;
282 int full_xsize = level_xsize * TILESIZE_VAR;
284 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
286 int fx = getFieldbufferOffsetX_EM();
289 int lx = LEVELX((px + dx) / TILESIZE_VAR);
292 lx -= (BorderElement != EL_EMPTY ? 1 : 0);
297 static int getLevelFromScreenY_EM(int sy)
299 int level_ysize = level.native_em_level->lev->height;
300 int full_ysize = level_ysize * TILESIZE_VAR;
302 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
304 int fy = getFieldbufferOffsetY_EM();
307 int ly = LEVELY((py + dy) / TILESIZE_VAR);
310 ly -= (BorderElement != EL_EMPTY ? 1 : 0);
315 static int getLevelFromScreenX_SP(int sx)
317 int menBorder = setup.sp_show_border_elements;
318 int level_xsize = level.native_sp_level->width;
319 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
321 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
323 int fx = getFieldbufferOffsetX_SP();
326 int lx = LEVELX((px + dx) / TILESIZE_VAR);
331 static int getLevelFromScreenY_SP(int sy)
333 int menBorder = setup.sp_show_border_elements;
334 int level_ysize = level.native_sp_level->height;
335 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
337 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
339 int fy = getFieldbufferOffsetY_SP();
342 int ly = LEVELY((py + dy) / TILESIZE_VAR);
347 int getLevelFromScreenX(int x)
349 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
350 return getLevelFromScreenX_EM(x);
351 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
352 return getLevelFromScreenX_SP(x);
354 return getLevelFromScreenX_RND(x);
357 int getLevelFromScreenY(int y)
359 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
360 return getLevelFromScreenY_EM(y);
361 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
362 return getLevelFromScreenY_SP(y);
364 return getLevelFromScreenY_RND(y);
367 void DumpTile(int x, int y)
372 printf_line("-", 79);
373 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
374 printf_line("-", 79);
376 if (!IN_LEV_FIELD(x, y))
378 printf("(not in level field)\n");
384 printf(" Feld: %d\t['%s']\n", Feld[x][y],
385 element_info[Feld[x][y]].token_name);
386 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
387 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
388 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
389 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
390 printf(" MovPos: %d\n", MovPos[x][y]);
391 printf(" MovDir: %d\n", MovDir[x][y]);
392 printf(" MovDelay: %d\n", MovDelay[x][y]);
393 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
394 printf(" CustomValue: %d\n", CustomValue[x][y]);
395 printf(" GfxElement: %d\n", GfxElement[x][y]);
396 printf(" GfxAction: %d\n", GfxAction[x][y]);
397 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
401 void DumpTileFromScreen(int sx, int sy)
403 int lx = getLevelFromScreenX(sx);
404 int ly = getLevelFromScreenY(sy);
409 void SetDrawtoField(int mode)
411 if (mode == DRAW_TO_FIELDBUFFER)
417 BX2 = SCR_FIELDX + 1;
418 BY2 = SCR_FIELDY + 1;
420 drawto_field = fieldbuffer;
422 else /* DRAW_TO_BACKBUFFER */
428 BX2 = SCR_FIELDX - 1;
429 BY2 = SCR_FIELDY - 1;
431 drawto_field = backbuffer;
435 static void RedrawPlayfield_RND()
437 if (game.envelope_active)
440 DrawLevel(REDRAW_ALL);
444 void RedrawPlayfield()
446 if (game_status != GAME_MODE_PLAYING)
449 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
450 RedrawPlayfield_EM(TRUE);
451 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
452 RedrawPlayfield_SP(TRUE);
453 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
454 RedrawPlayfield_RND();
456 BlitScreenToBitmap(backbuffer);
458 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
462 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
465 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
466 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
468 if (x == -1 && y == -1)
471 if (draw_target == DRAW_TO_SCREEN)
472 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
474 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
477 static void DrawMaskedBorderExt_FIELD(int draw_target)
479 if (global.border_status >= GAME_MODE_MAIN &&
480 global.border_status <= GAME_MODE_PLAYING &&
481 border.draw_masked[global.border_status])
482 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
486 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
488 // when drawing to backbuffer, never draw border over open doors
489 if (draw_target == DRAW_TO_BACKBUFFER &&
490 (GetDoorState() & DOOR_OPEN_1))
493 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
494 (global.border_status != GAME_MODE_EDITOR ||
495 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
496 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
499 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
501 // when drawing to backbuffer, never draw border over open doors
502 if (draw_target == DRAW_TO_BACKBUFFER &&
503 (GetDoorState() & DOOR_OPEN_2))
506 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
507 global.border_status != GAME_MODE_EDITOR)
508 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
511 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
513 /* currently not available */
516 static void DrawMaskedBorderExt_ALL(int draw_target)
518 DrawMaskedBorderExt_FIELD(draw_target);
519 DrawMaskedBorderExt_DOOR_1(draw_target);
520 DrawMaskedBorderExt_DOOR_2(draw_target);
521 DrawMaskedBorderExt_DOOR_3(draw_target);
524 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
526 /* never draw masked screen borders on borderless screens */
527 if (global.border_status == GAME_MODE_LOADING ||
528 global.border_status == GAME_MODE_TITLE)
531 if (redraw_mask & REDRAW_ALL)
532 DrawMaskedBorderExt_ALL(draw_target);
535 if (redraw_mask & REDRAW_FIELD)
536 DrawMaskedBorderExt_FIELD(draw_target);
537 if (redraw_mask & REDRAW_DOOR_1)
538 DrawMaskedBorderExt_DOOR_1(draw_target);
539 if (redraw_mask & REDRAW_DOOR_2)
540 DrawMaskedBorderExt_DOOR_2(draw_target);
541 if (redraw_mask & REDRAW_DOOR_3)
542 DrawMaskedBorderExt_DOOR_3(draw_target);
546 void DrawMaskedBorder_FIELD()
548 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
551 void DrawMaskedBorder(int redraw_mask)
553 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
556 void DrawMaskedBorderToTarget(int draw_target)
558 if (draw_target == DRAW_TO_BACKBUFFER ||
559 draw_target == DRAW_TO_SCREEN)
561 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
565 int last_border_status = global.border_status;
567 if (draw_target == DRAW_TO_FADE_SOURCE)
569 global.border_status = gfx.fade_border_source_status;
570 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
572 else if (draw_target == DRAW_TO_FADE_TARGET)
574 global.border_status = gfx.fade_border_target_status;
575 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
578 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
580 global.border_status = last_border_status;
581 gfx.masked_border_bitmap_ptr = backbuffer;
585 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
587 int fx = getFieldbufferOffsetX_RND();
588 int fy = getFieldbufferOffsetY_RND();
590 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
593 void BlitScreenToBitmap(Bitmap *target_bitmap)
595 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
596 BlitScreenToBitmap_EM(target_bitmap);
597 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
598 BlitScreenToBitmap_SP(target_bitmap);
599 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
600 BlitScreenToBitmap_RND(target_bitmap);
602 redraw_mask |= REDRAW_FIELD;
605 void DrawFramesPerSecond()
608 int font_nr = FONT_TEXT_2;
609 int font_width = getFontWidth(font_nr);
611 sprintf(text, "%04.1f fps", global.frames_per_second);
613 DrawTextExt(backbuffer, WIN_XSIZE - font_width * strlen(text), 0, text,
614 font_nr, BLIT_OPAQUE);
618 static void PrintFrameTimeDebugging()
620 static unsigned int last_counter = 0;
621 unsigned int counter = Counter();
622 int diff_1 = counter - last_counter;
623 int diff_2 = diff_1 - GAME_FRAME_DELAY;
625 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
626 char diff_bar[2 * diff_2_max + 5];
630 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
632 for (i = 0; i < diff_2_max; i++)
633 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
634 i >= diff_2_max - diff_2_cut ? '-' : ' ');
636 diff_bar[pos++] = '|';
638 for (i = 0; i < diff_2_max; i++)
639 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
641 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
643 diff_bar[pos++] = '\0';
645 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
648 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
651 last_counter = counter;
655 static int unifiedRedrawMask(int mask)
657 if (mask & REDRAW_ALL)
660 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
666 static boolean equalRedrawMasks(int mask_1, int mask_2)
668 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
673 static int last_redraw_mask = REDRAW_NONE;
675 // force screen redraw in every frame to continue drawing global animations
676 // (but always use the last redraw mask to prevent unwanted side effects)
677 if (redraw_mask == REDRAW_NONE)
678 redraw_mask = last_redraw_mask;
680 last_redraw_mask = redraw_mask;
683 // masked border now drawn immediately when blitting backbuffer to window
685 // draw masked border to all viewports, if defined
686 DrawMaskedBorder(redraw_mask);
689 // draw frames per second (only if debug mode is enabled)
690 if (redraw_mask & REDRAW_FPS)
691 DrawFramesPerSecond();
693 // remove playfield redraw before potentially merging with doors redraw
694 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
695 redraw_mask &= ~REDRAW_FIELD;
697 // redraw complete window if both playfield and (some) doors need redraw
698 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
699 redraw_mask = REDRAW_ALL;
701 /* although redrawing the whole window would be fine for normal gameplay,
702 being able to only redraw the playfield is required for deactivating
703 certain drawing areas (mainly playfield) to work, which is needed for
704 warp-forward to be fast enough (by skipping redraw of most frames) */
706 if (redraw_mask & REDRAW_ALL)
708 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
710 else if (redraw_mask & REDRAW_FIELD)
712 BlitBitmap(backbuffer, window,
713 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
715 else if (redraw_mask & REDRAW_DOORS)
717 // merge door areas to prevent calling screen redraw more than once
723 if (redraw_mask & REDRAW_DOOR_1)
727 x2 = MAX(x2, DX + DXSIZE);
728 y2 = MAX(y2, DY + DYSIZE);
731 if (redraw_mask & REDRAW_DOOR_2)
735 x2 = MAX(x2, VX + VXSIZE);
736 y2 = MAX(y2, VY + VYSIZE);
739 if (redraw_mask & REDRAW_DOOR_3)
743 x2 = MAX(x2, EX + EXSIZE);
744 y2 = MAX(y2, EY + EYSIZE);
747 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
750 redraw_mask = REDRAW_NONE;
753 PrintFrameTimeDebugging();
757 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
759 unsigned int frame_delay_value_old = GetVideoFrameDelay();
761 SetVideoFrameDelay(frame_delay_value);
765 SetVideoFrameDelay(frame_delay_value_old);
768 static int fade_type_skip = FADE_TYPE_NONE;
770 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
772 void (*draw_border_function)(void) = NULL;
773 int x, y, width, height;
774 int fade_delay, post_delay;
776 if (fade_type == FADE_TYPE_FADE_OUT)
778 if (fade_type_skip != FADE_TYPE_NONE)
780 /* skip all fade operations until specified fade operation */
781 if (fade_type & fade_type_skip)
782 fade_type_skip = FADE_TYPE_NONE;
787 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
791 redraw_mask |= fade_mask;
793 if (fade_type == FADE_TYPE_SKIP)
795 fade_type_skip = fade_mode;
800 fade_delay = fading.fade_delay;
801 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
803 if (fade_type_skip != FADE_TYPE_NONE)
805 /* skip all fade operations until specified fade operation */
806 if (fade_type & fade_type_skip)
807 fade_type_skip = FADE_TYPE_NONE;
812 if (global.autoplay_leveldir)
817 if (fade_mask == REDRAW_FIELD)
822 height = FADE_SYSIZE;
824 if (border.draw_masked_when_fading)
825 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
827 DrawMaskedBorder_FIELD(); /* draw once */
829 else /* REDRAW_ALL */
837 if (!setup.fade_screens ||
839 fading.fade_mode == FADE_MODE_NONE)
841 if (fade_mode == FADE_MODE_FADE_OUT)
844 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
846 redraw_mask &= ~fade_mask;
851 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
852 draw_border_function);
854 redraw_mask &= ~fade_mask;
857 static void SetScreenStates_BeforeFadingIn()
859 // temporarily set screen mode for animations to screen after fading in
860 global.anim_status = global.anim_status_next;
862 // store backbuffer with all animations that will be started after fading in
863 if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
864 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
866 // set screen mode for animations back to fading
867 global.anim_status = GAME_MODE_PSEUDO_FADING;
870 static void SetScreenStates_AfterFadingIn()
872 // store new source screen (to use correct masked border for fading)
873 gfx.fade_border_source_status = global.border_status;
875 global.anim_status = global.anim_status_next;
878 static void SetScreenStates_BeforeFadingOut()
880 // store new target screen (to use correct masked border for fading)
881 gfx.fade_border_target_status = game_status;
883 // set screen mode for animations to fading
884 global.anim_status = GAME_MODE_PSEUDO_FADING;
886 // store backbuffer with all animations that will be stopped for fading out
887 if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
888 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
891 static void SetScreenStates_AfterFadingOut()
893 global.border_status = game_status;
896 void FadeIn(int fade_mask)
898 SetScreenStates_BeforeFadingIn();
901 DrawMaskedBorder(REDRAW_ALL);
904 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
905 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
907 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
911 FADE_SXSIZE = FULL_SXSIZE;
912 FADE_SYSIZE = FULL_SYSIZE;
914 SetScreenStates_AfterFadingIn();
916 // force update of global animation status in case of rapid screen changes
917 redraw_mask = REDRAW_ALL;
921 void FadeOut(int fade_mask)
923 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
924 if (!equalRedrawMasks(fade_mask, redraw_mask))
927 SetScreenStates_BeforeFadingOut();
930 DrawMaskedBorder(REDRAW_ALL);
933 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
934 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
936 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
938 SetScreenStates_AfterFadingOut();
941 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
943 static struct TitleFadingInfo fading_leave_stored;
946 fading_leave_stored = fading_leave;
948 fading = fading_leave_stored;
951 void FadeSetEnterMenu()
953 fading = menu.enter_menu;
955 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
958 void FadeSetLeaveMenu()
960 fading = menu.leave_menu;
962 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
965 void FadeSetEnterScreen()
967 fading = menu.enter_screen[game_status];
969 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
972 void FadeSetNextScreen()
974 fading = menu.next_screen[game_status];
976 // (do not overwrite fade mode set by FadeSetEnterScreen)
977 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
980 void FadeSetLeaveScreen()
982 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
985 void FadeSetFromType(int type)
987 if (type & TYPE_ENTER_SCREEN)
988 FadeSetEnterScreen();
989 else if (type & TYPE_ENTER)
991 else if (type & TYPE_LEAVE)
995 void FadeSetDisabled()
997 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
999 fading = fading_none;
1002 void FadeSkipNextFadeIn()
1004 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1007 void FadeSkipNextFadeOut()
1009 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1012 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1014 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1016 return (graphic == IMG_UNDEFINED ? NULL :
1017 graphic_info[graphic].bitmap != NULL || redefined ?
1018 graphic_info[graphic].bitmap :
1019 graphic_info[default_graphic].bitmap);
1022 Bitmap *getBackgroundBitmap(int graphic)
1024 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1027 Bitmap *getGlobalBorderBitmap(int graphic)
1029 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1032 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1035 (status == GAME_MODE_MAIN ||
1036 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1037 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1038 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1039 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1042 return getGlobalBorderBitmap(graphic);
1045 void SetWindowBackgroundImageIfDefined(int graphic)
1047 if (graphic_info[graphic].bitmap)
1048 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1051 void SetMainBackgroundImageIfDefined(int graphic)
1053 if (graphic_info[graphic].bitmap)
1054 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1057 void SetDoorBackgroundImageIfDefined(int graphic)
1059 if (graphic_info[graphic].bitmap)
1060 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1063 void SetWindowBackgroundImage(int graphic)
1065 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1068 void SetMainBackgroundImage(int graphic)
1070 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1073 void SetDoorBackgroundImage(int graphic)
1075 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1078 void SetPanelBackground()
1080 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1082 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1083 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1085 SetDoorBackgroundBitmap(bitmap_db_panel);
1088 void DrawBackground(int x, int y, int width, int height)
1090 /* "drawto" might still point to playfield buffer here (hall of fame) */
1091 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1093 if (IN_GFX_FIELD_FULL(x, y))
1094 redraw_mask |= REDRAW_FIELD;
1095 else if (IN_GFX_DOOR_1(x, y))
1096 redraw_mask |= REDRAW_DOOR_1;
1097 else if (IN_GFX_DOOR_2(x, y))
1098 redraw_mask |= REDRAW_DOOR_2;
1099 else if (IN_GFX_DOOR_3(x, y))
1100 redraw_mask |= REDRAW_DOOR_3;
1103 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1105 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1107 if (font->bitmap == NULL)
1110 DrawBackground(x, y, width, height);
1113 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1115 struct GraphicInfo *g = &graphic_info[graphic];
1117 if (g->bitmap == NULL)
1120 DrawBackground(x, y, width, height);
1123 static int game_status_last = -1;
1124 static Bitmap *global_border_bitmap_last = NULL;
1125 static Bitmap *global_border_bitmap = NULL;
1126 static int real_sx_last = -1, real_sy_last = -1;
1127 static int full_sxsize_last = -1, full_sysize_last = -1;
1128 static int dx_last = -1, dy_last = -1;
1129 static int dxsize_last = -1, dysize_last = -1;
1130 static int vx_last = -1, vy_last = -1;
1131 static int vxsize_last = -1, vysize_last = -1;
1133 boolean CheckIfGlobalBorderHasChanged()
1135 // if game status has not changed, global border has not changed either
1136 if (game_status == game_status_last)
1139 // determine and store new global border bitmap for current game status
1140 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1142 return (global_border_bitmap_last != global_border_bitmap);
1145 boolean CheckIfGlobalBorderRedrawIsNeeded()
1147 // if game status has not changed, nothing has to be redrawn
1148 if (game_status == game_status_last)
1151 // redraw if last screen was title screen
1152 if (game_status_last == GAME_MODE_TITLE)
1155 // redraw if global screen border has changed
1156 if (CheckIfGlobalBorderHasChanged())
1159 // redraw if position or size of playfield area has changed
1160 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1161 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1164 // redraw if position or size of door area has changed
1165 if (dx_last != DX || dy_last != DY ||
1166 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1169 // redraw if position or size of tape area has changed
1170 if (vx_last != VX || vy_last != VY ||
1171 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1177 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1180 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1182 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1185 void RedrawGlobalBorder()
1187 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1189 RedrawGlobalBorderFromBitmap(bitmap);
1191 redraw_mask = REDRAW_ALL;
1194 static void RedrawGlobalBorderIfNeeded()
1196 if (game_status == game_status_last)
1199 // copy current draw buffer to later copy back areas that have not changed
1200 if (game_status_last != GAME_MODE_TITLE)
1201 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1203 if (CheckIfGlobalBorderRedrawIsNeeded())
1205 // redraw global screen border (or clear, if defined to be empty)
1206 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1208 // copy previous playfield and door areas, if they are defined on both
1209 // previous and current screen and if they still have the same size
1211 if (real_sx_last != -1 && real_sy_last != -1 &&
1212 REAL_SX != -1 && REAL_SY != -1 &&
1213 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1214 BlitBitmap(bitmap_db_store_1, backbuffer,
1215 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1218 if (dx_last != -1 && dy_last != -1 &&
1219 DX != -1 && DY != -1 &&
1220 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1221 BlitBitmap(bitmap_db_store_1, backbuffer,
1222 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1224 if (vx_last != -1 && vy_last != -1 &&
1225 VX != -1 && VY != -1 &&
1226 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1227 BlitBitmap(bitmap_db_store_1, backbuffer,
1228 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1230 redraw_mask = REDRAW_ALL;
1233 game_status_last = game_status;
1235 global_border_bitmap_last = global_border_bitmap;
1237 real_sx_last = REAL_SX;
1238 real_sy_last = REAL_SY;
1239 full_sxsize_last = FULL_SXSIZE;
1240 full_sysize_last = FULL_SYSIZE;
1243 dxsize_last = DXSIZE;
1244 dysize_last = DYSIZE;
1247 vxsize_last = VXSIZE;
1248 vysize_last = VYSIZE;
1253 RedrawGlobalBorderIfNeeded();
1255 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1256 /* (when entering hall of fame after playing) */
1257 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1259 /* !!! maybe this should be done before clearing the background !!! */
1260 if (game_status == GAME_MODE_PLAYING)
1262 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1263 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1267 SetDrawtoField(DRAW_TO_BACKBUFFER);
1271 void MarkTileDirty(int x, int y)
1273 redraw_mask |= REDRAW_FIELD;
1276 void SetBorderElement()
1280 BorderElement = EL_EMPTY;
1282 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1284 for (x = 0; x < lev_fieldx; x++)
1286 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1287 BorderElement = EL_STEELWALL;
1289 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1295 void FloodFillLevel(int from_x, int from_y, int fill_element,
1296 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1297 int max_fieldx, int max_fieldy)
1301 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1302 static int safety = 0;
1304 /* check if starting field still has the desired content */
1305 if (field[from_x][from_y] == fill_element)
1310 if (safety > max_fieldx * max_fieldy)
1311 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1313 old_element = field[from_x][from_y];
1314 field[from_x][from_y] = fill_element;
1316 for (i = 0; i < 4; i++)
1318 x = from_x + check[i][0];
1319 y = from_y + check[i][1];
1321 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1322 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1328 void SetRandomAnimationValue(int x, int y)
1330 gfx.anim_random_frame = GfxRandom[x][y];
1333 int getGraphicAnimationFrame(int graphic, int sync_frame)
1335 /* animation synchronized with global frame counter, not move position */
1336 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1337 sync_frame = FrameCounter;
1339 return getAnimationFrame(graphic_info[graphic].anim_frames,
1340 graphic_info[graphic].anim_delay,
1341 graphic_info[graphic].anim_mode,
1342 graphic_info[graphic].anim_start_frame,
1346 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1348 struct GraphicInfo *g = &graphic_info[graphic];
1349 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1351 if (tilesize == gfx.standard_tile_size)
1352 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1353 else if (tilesize == game.tile_size)
1354 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1356 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1359 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1360 boolean get_backside)
1362 struct GraphicInfo *g = &graphic_info[graphic];
1363 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1364 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1366 if (g->offset_y == 0) /* frames are ordered horizontally */
1368 int max_width = g->anim_frames_per_line * g->width;
1369 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1371 *x = pos % max_width;
1372 *y = src_y % g->height + pos / max_width * g->height;
1374 else if (g->offset_x == 0) /* frames are ordered vertically */
1376 int max_height = g->anim_frames_per_line * g->height;
1377 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1379 *x = src_x % g->width + pos / max_height * g->width;
1380 *y = pos % max_height;
1382 else /* frames are ordered diagonally */
1384 *x = src_x + frame * g->offset_x;
1385 *y = src_y + frame * g->offset_y;
1389 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1390 Bitmap **bitmap, int *x, int *y,
1391 boolean get_backside)
1393 struct GraphicInfo *g = &graphic_info[graphic];
1395 // if no in-game graphics defined, always use standard graphic size
1396 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1397 tilesize = TILESIZE;
1399 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1400 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1402 *x = *x * tilesize / g->tile_size;
1403 *y = *y * tilesize / g->tile_size;
1406 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1407 int *x, int *y, boolean get_backside)
1409 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1413 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1414 Bitmap **bitmap, int *x, int *y)
1416 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1419 void getFixedGraphicSource(int graphic, int frame,
1420 Bitmap **bitmap, int *x, int *y)
1422 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1425 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1427 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1430 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1431 int *x, int *y, boolean get_backside)
1433 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1437 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1439 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1442 void DrawGraphic(int x, int y, int graphic, int frame)
1445 if (!IN_SCR_FIELD(x, y))
1447 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1448 printf("DrawGraphic(): This should never happen!\n");
1453 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1456 MarkTileDirty(x, y);
1459 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1462 if (!IN_SCR_FIELD(x, y))
1464 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1465 printf("DrawGraphic(): This should never happen!\n");
1470 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1472 MarkTileDirty(x, y);
1475 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1481 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1483 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1486 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1492 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1493 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1496 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1499 if (!IN_SCR_FIELD(x, y))
1501 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1502 printf("DrawGraphicThruMask(): This should never happen!\n");
1507 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1510 MarkTileDirty(x, y);
1513 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1516 if (!IN_SCR_FIELD(x, y))
1518 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1519 printf("DrawGraphicThruMask(): This should never happen!\n");
1524 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1526 MarkTileDirty(x, y);
1529 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1535 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1537 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1541 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1542 int graphic, int frame)
1547 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1549 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1553 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1555 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1557 MarkTileDirty(x / tilesize, y / tilesize);
1560 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1566 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1567 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1570 void DrawMiniGraphic(int x, int y, int graphic)
1572 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1573 MarkTileDirty(x / 2, y / 2);
1576 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1581 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1582 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1585 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1586 int graphic, int frame,
1587 int cut_mode, int mask_mode)
1592 int width = TILEX, height = TILEY;
1595 if (dx || dy) /* shifted graphic */
1597 if (x < BX1) /* object enters playfield from the left */
1604 else if (x > BX2) /* object enters playfield from the right */
1610 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1616 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1618 else if (dx) /* general horizontal movement */
1619 MarkTileDirty(x + SIGN(dx), y);
1621 if (y < BY1) /* object enters playfield from the top */
1623 if (cut_mode == CUT_BELOW) /* object completely above top border */
1631 else if (y > BY2) /* object enters playfield from the bottom */
1637 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1643 else if (dy > 0 && cut_mode == CUT_ABOVE)
1645 if (y == BY2) /* object completely above bottom border */
1651 MarkTileDirty(x, y + 1);
1652 } /* object leaves playfield to the bottom */
1653 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1655 else if (dy) /* general vertical movement */
1656 MarkTileDirty(x, y + SIGN(dy));
1660 if (!IN_SCR_FIELD(x, y))
1662 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1663 printf("DrawGraphicShifted(): This should never happen!\n");
1668 width = width * TILESIZE_VAR / TILESIZE;
1669 height = height * TILESIZE_VAR / TILESIZE;
1670 cx = cx * TILESIZE_VAR / TILESIZE;
1671 cy = cy * TILESIZE_VAR / TILESIZE;
1672 dx = dx * TILESIZE_VAR / TILESIZE;
1673 dy = dy * TILESIZE_VAR / TILESIZE;
1675 if (width > 0 && height > 0)
1677 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1682 dst_x = FX + x * TILEX_VAR + dx;
1683 dst_y = FY + y * TILEY_VAR + dy;
1685 if (mask_mode == USE_MASKING)
1686 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1689 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1692 MarkTileDirty(x, y);
1696 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1697 int graphic, int frame,
1698 int cut_mode, int mask_mode)
1703 int width = TILEX_VAR, height = TILEY_VAR;
1706 int x2 = x + SIGN(dx);
1707 int y2 = y + SIGN(dy);
1709 /* movement with two-tile animations must be sync'ed with movement position,
1710 not with current GfxFrame (which can be higher when using slow movement) */
1711 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1712 int anim_frames = graphic_info[graphic].anim_frames;
1714 /* (we also need anim_delay here for movement animations with less frames) */
1715 int anim_delay = graphic_info[graphic].anim_delay;
1716 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1718 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1719 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1721 /* re-calculate animation frame for two-tile movement animation */
1722 frame = getGraphicAnimationFrame(graphic, sync_frame);
1724 /* check if movement start graphic inside screen area and should be drawn */
1725 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1727 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1729 dst_x = FX + x1 * TILEX_VAR;
1730 dst_y = FY + y1 * TILEY_VAR;
1732 if (mask_mode == USE_MASKING)
1733 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1736 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1739 MarkTileDirty(x1, y1);
1742 /* check if movement end graphic inside screen area and should be drawn */
1743 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1745 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1747 dst_x = FX + x2 * TILEX_VAR;
1748 dst_y = FY + y2 * TILEY_VAR;
1750 if (mask_mode == USE_MASKING)
1751 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1754 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1757 MarkTileDirty(x2, y2);
1761 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1762 int graphic, int frame,
1763 int cut_mode, int mask_mode)
1767 DrawGraphic(x, y, graphic, frame);
1772 if (graphic_info[graphic].double_movement) /* EM style movement images */
1773 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1775 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1778 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1779 int frame, int cut_mode)
1781 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1784 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1785 int cut_mode, int mask_mode)
1787 int lx = LEVELX(x), ly = LEVELY(y);
1791 if (IN_LEV_FIELD(lx, ly))
1793 SetRandomAnimationValue(lx, ly);
1795 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1796 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1798 /* do not use double (EM style) movement graphic when not moving */
1799 if (graphic_info[graphic].double_movement && !dx && !dy)
1801 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1802 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1805 else /* border element */
1807 graphic = el2img(element);
1808 frame = getGraphicAnimationFrame(graphic, -1);
1811 if (element == EL_EXPANDABLE_WALL)
1813 boolean left_stopped = FALSE, right_stopped = FALSE;
1815 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1816 left_stopped = TRUE;
1817 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1818 right_stopped = TRUE;
1820 if (left_stopped && right_stopped)
1822 else if (left_stopped)
1824 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1825 frame = graphic_info[graphic].anim_frames - 1;
1827 else if (right_stopped)
1829 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1830 frame = graphic_info[graphic].anim_frames - 1;
1835 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1836 else if (mask_mode == USE_MASKING)
1837 DrawGraphicThruMask(x, y, graphic, frame);
1839 DrawGraphic(x, y, graphic, frame);
1842 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1843 int cut_mode, int mask_mode)
1845 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1846 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1847 cut_mode, mask_mode);
1850 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1853 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1856 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1859 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1862 void DrawLevelElementThruMask(int x, int y, int element)
1864 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1867 void DrawLevelFieldThruMask(int x, int y)
1869 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1872 /* !!! implementation of quicksand is totally broken !!! */
1873 #define IS_CRUMBLED_TILE(x, y, e) \
1874 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1875 !IS_MOVING(x, y) || \
1876 (e) == EL_QUICKSAND_EMPTYING || \
1877 (e) == EL_QUICKSAND_FAST_EMPTYING))
1879 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1884 int width, height, cx, cy;
1885 int sx = SCREENX(x), sy = SCREENY(y);
1886 int crumbled_border_size = graphic_info[graphic].border_size;
1887 int crumbled_tile_size = graphic_info[graphic].tile_size;
1888 int crumbled_border_size_var =
1889 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
1892 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1894 for (i = 1; i < 4; i++)
1896 int dxx = (i & 1 ? dx : 0);
1897 int dyy = (i & 2 ? dy : 0);
1900 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1903 /* check if neighbour field is of same crumble type */
1904 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1905 graphic_info[graphic].class ==
1906 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1908 /* return if check prevents inner corner */
1909 if (same == (dxx == dx && dyy == dy))
1913 /* if we reach this point, we have an inner corner */
1915 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1917 width = crumbled_border_size_var;
1918 height = crumbled_border_size_var;
1919 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1920 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1922 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1923 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1926 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1931 int width, height, bx, by, cx, cy;
1932 int sx = SCREENX(x), sy = SCREENY(y);
1933 int crumbled_border_size = graphic_info[graphic].border_size;
1934 int crumbled_tile_size = graphic_info[graphic].tile_size;
1935 int crumbled_border_size_var =
1936 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
1937 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1940 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1942 /* draw simple, sloppy, non-corner-accurate crumbled border */
1944 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1945 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1946 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1947 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1949 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1950 FX + sx * TILEX_VAR + cx,
1951 FY + sy * TILEY_VAR + cy);
1953 /* (remaining middle border part must be at least as big as corner part) */
1954 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1955 crumbled_border_size_var >= TILESIZE_VAR / 3)
1958 /* correct corners of crumbled border, if needed */
1960 for (i = -1; i <= 1; i += 2)
1962 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1963 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1964 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1967 /* check if neighbour field is of same crumble type */
1968 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1969 graphic_info[graphic].class ==
1970 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1972 /* no crumbled corner, but continued crumbled border */
1974 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1975 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1976 int b1 = (i == 1 ? crumbled_border_size_var :
1977 TILESIZE_VAR - 2 * crumbled_border_size_var);
1979 width = crumbled_border_size_var;
1980 height = crumbled_border_size_var;
1982 if (dir == 1 || dir == 2)
1997 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1999 FX + sx * TILEX_VAR + cx,
2000 FY + sy * TILEY_VAR + cy);
2005 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2007 int sx = SCREENX(x), sy = SCREENY(y);
2010 static int xy[4][2] =
2018 if (!IN_LEV_FIELD(x, y))
2021 element = TILE_GFX_ELEMENT(x, y);
2023 if (IS_CRUMBLED_TILE(x, y, element)) /* crumble field itself */
2025 if (!IN_SCR_FIELD(sx, sy))
2028 /* crumble field borders towards direct neighbour fields */
2029 for (i = 0; i < 4; i++)
2031 int xx = x + xy[i][0];
2032 int yy = y + xy[i][1];
2034 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2037 /* check if neighbour field is of same crumble type */
2038 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2039 graphic_info[graphic].class ==
2040 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2043 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2046 /* crumble inner field corners towards corner neighbour fields */
2047 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2048 graphic_info[graphic].anim_frames == 2)
2050 for (i = 0; i < 4; i++)
2052 int dx = (i & 1 ? +1 : -1);
2053 int dy = (i & 2 ? +1 : -1);
2055 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2059 MarkTileDirty(sx, sy);
2061 else /* center field is not crumbled -- crumble neighbour fields */
2063 /* crumble field borders of direct neighbour fields */
2064 for (i = 0; i < 4; i++)
2066 int xx = x + xy[i][0];
2067 int yy = y + xy[i][1];
2068 int sxx = sx + xy[i][0];
2069 int syy = sy + xy[i][1];
2071 if (!IN_LEV_FIELD(xx, yy) ||
2072 !IN_SCR_FIELD(sxx, syy))
2075 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2078 element = TILE_GFX_ELEMENT(xx, yy);
2080 if (!IS_CRUMBLED_TILE(xx, yy, element))
2083 graphic = el_act2crm(element, ACTION_DEFAULT);
2085 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2087 MarkTileDirty(sxx, syy);
2090 /* crumble inner field corners of corner neighbour fields */
2091 for (i = 0; i < 4; i++)
2093 int dx = (i & 1 ? +1 : -1);
2094 int dy = (i & 2 ? +1 : -1);
2100 if (!IN_LEV_FIELD(xx, yy) ||
2101 !IN_SCR_FIELD(sxx, syy))
2104 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2107 element = TILE_GFX_ELEMENT(xx, yy);
2109 if (!IS_CRUMBLED_TILE(xx, yy, element))
2112 graphic = el_act2crm(element, ACTION_DEFAULT);
2114 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2115 graphic_info[graphic].anim_frames == 2)
2116 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2118 MarkTileDirty(sxx, syy);
2123 void DrawLevelFieldCrumbled(int x, int y)
2127 if (!IN_LEV_FIELD(x, y))
2130 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2131 GfxElement[x][y] != EL_UNDEFINED &&
2132 GFX_CRUMBLED(GfxElement[x][y]))
2134 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2139 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2141 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2144 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2147 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2148 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2149 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2150 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2151 int sx = SCREENX(x), sy = SCREENY(y);
2153 DrawGraphic(sx, sy, graphic1, frame1);
2154 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2157 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2159 int sx = SCREENX(x), sy = SCREENY(y);
2160 static int xy[4][2] =
2169 /* crumble direct neighbour fields (required for field borders) */
2170 for (i = 0; i < 4; i++)
2172 int xx = x + xy[i][0];
2173 int yy = y + xy[i][1];
2174 int sxx = sx + xy[i][0];
2175 int syy = sy + xy[i][1];
2177 if (!IN_LEV_FIELD(xx, yy) ||
2178 !IN_SCR_FIELD(sxx, syy) ||
2179 !GFX_CRUMBLED(Feld[xx][yy]) ||
2183 DrawLevelField(xx, yy);
2186 /* crumble corner neighbour fields (required for inner field corners) */
2187 for (i = 0; i < 4; i++)
2189 int dx = (i & 1 ? +1 : -1);
2190 int dy = (i & 2 ? +1 : -1);
2196 if (!IN_LEV_FIELD(xx, yy) ||
2197 !IN_SCR_FIELD(sxx, syy) ||
2198 !GFX_CRUMBLED(Feld[xx][yy]) ||
2202 int element = TILE_GFX_ELEMENT(xx, yy);
2203 int graphic = el_act2crm(element, ACTION_DEFAULT);
2205 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2206 graphic_info[graphic].anim_frames == 2)
2207 DrawLevelField(xx, yy);
2211 static int getBorderElement(int x, int y)
2215 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2216 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2217 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2218 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2219 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2220 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2221 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2223 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2224 int steel_position = (x == -1 && y == -1 ? 0 :
2225 x == lev_fieldx && y == -1 ? 1 :
2226 x == -1 && y == lev_fieldy ? 2 :
2227 x == lev_fieldx && y == lev_fieldy ? 3 :
2228 x == -1 || x == lev_fieldx ? 4 :
2229 y == -1 || y == lev_fieldy ? 5 : 6);
2231 return border[steel_position][steel_type];
2234 void DrawScreenElement(int x, int y, int element)
2236 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2237 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2240 void DrawLevelElement(int x, int y, int element)
2242 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2243 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2246 void DrawScreenField(int x, int y)
2248 int lx = LEVELX(x), ly = LEVELY(y);
2249 int element, content;
2251 if (!IN_LEV_FIELD(lx, ly))
2253 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2256 element = getBorderElement(lx, ly);
2258 DrawScreenElement(x, y, element);
2263 element = Feld[lx][ly];
2264 content = Store[lx][ly];
2266 if (IS_MOVING(lx, ly))
2268 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2269 boolean cut_mode = NO_CUTTING;
2271 if (element == EL_QUICKSAND_EMPTYING ||
2272 element == EL_QUICKSAND_FAST_EMPTYING ||
2273 element == EL_MAGIC_WALL_EMPTYING ||
2274 element == EL_BD_MAGIC_WALL_EMPTYING ||
2275 element == EL_DC_MAGIC_WALL_EMPTYING ||
2276 element == EL_AMOEBA_DROPPING)
2277 cut_mode = CUT_ABOVE;
2278 else if (element == EL_QUICKSAND_FILLING ||
2279 element == EL_QUICKSAND_FAST_FILLING ||
2280 element == EL_MAGIC_WALL_FILLING ||
2281 element == EL_BD_MAGIC_WALL_FILLING ||
2282 element == EL_DC_MAGIC_WALL_FILLING)
2283 cut_mode = CUT_BELOW;
2285 if (cut_mode == CUT_ABOVE)
2286 DrawScreenElement(x, y, element);
2288 DrawScreenElement(x, y, EL_EMPTY);
2291 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2292 else if (cut_mode == NO_CUTTING)
2293 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2296 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2298 if (cut_mode == CUT_BELOW &&
2299 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2300 DrawLevelElement(lx, ly + 1, element);
2303 if (content == EL_ACID)
2305 int dir = MovDir[lx][ly];
2306 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2307 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2309 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2312 else if (IS_BLOCKED(lx, ly))
2317 boolean cut_mode = NO_CUTTING;
2318 int element_old, content_old;
2320 Blocked2Moving(lx, ly, &oldx, &oldy);
2323 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2324 MovDir[oldx][oldy] == MV_RIGHT);
2326 element_old = Feld[oldx][oldy];
2327 content_old = Store[oldx][oldy];
2329 if (element_old == EL_QUICKSAND_EMPTYING ||
2330 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2331 element_old == EL_MAGIC_WALL_EMPTYING ||
2332 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2333 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2334 element_old == EL_AMOEBA_DROPPING)
2335 cut_mode = CUT_ABOVE;
2337 DrawScreenElement(x, y, EL_EMPTY);
2340 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2342 else if (cut_mode == NO_CUTTING)
2343 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2346 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2349 else if (IS_DRAWABLE(element))
2350 DrawScreenElement(x, y, element);
2352 DrawScreenElement(x, y, EL_EMPTY);
2355 void DrawLevelField(int x, int y)
2357 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2358 DrawScreenField(SCREENX(x), SCREENY(y));
2359 else if (IS_MOVING(x, y))
2363 Moving2Blocked(x, y, &newx, &newy);
2364 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2365 DrawScreenField(SCREENX(newx), SCREENY(newy));
2367 else if (IS_BLOCKED(x, y))
2371 Blocked2Moving(x, y, &oldx, &oldy);
2372 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2373 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2377 void DrawSizedElement(int x, int y, int element, int tilesize)
2381 graphic = el2edimg(element);
2382 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2385 void DrawMiniElement(int x, int y, int element)
2389 graphic = el2edimg(element);
2390 DrawMiniGraphic(x, y, graphic);
2393 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2396 int x = sx + scroll_x, y = sy + scroll_y;
2398 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2399 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2400 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2401 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2403 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2406 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2408 int x = sx + scroll_x, y = sy + scroll_y;
2410 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2411 DrawMiniElement(sx, sy, EL_EMPTY);
2412 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2413 DrawMiniElement(sx, sy, Feld[x][y]);
2415 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2418 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2419 int x, int y, int xsize, int ysize,
2420 int tile_width, int tile_height)
2424 int dst_x = startx + x * tile_width;
2425 int dst_y = starty + y * tile_height;
2426 int width = graphic_info[graphic].width;
2427 int height = graphic_info[graphic].height;
2428 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2429 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2430 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2431 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2432 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2433 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2434 boolean draw_masked = graphic_info[graphic].draw_masked;
2436 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2438 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2440 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2444 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2445 inner_sx + (x - 1) * tile_width % inner_width);
2446 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2447 inner_sy + (y - 1) * tile_height % inner_height);
2450 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2453 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2457 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2458 int x, int y, int xsize, int ysize, int font_nr)
2460 int font_width = getFontWidth(font_nr);
2461 int font_height = getFontHeight(font_nr);
2463 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2464 font_width, font_height);
2467 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2469 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2470 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2471 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2472 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2473 boolean no_delay = (tape.warp_forward);
2474 unsigned int anim_delay = 0;
2475 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2476 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2477 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2478 int font_width = getFontWidth(font_nr);
2479 int font_height = getFontHeight(font_nr);
2480 int max_xsize = level.envelope[envelope_nr].xsize;
2481 int max_ysize = level.envelope[envelope_nr].ysize;
2482 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2483 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2484 int xend = max_xsize;
2485 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2486 int xstep = (xstart < xend ? 1 : 0);
2487 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2489 int end = MAX(xend - xstart, yend - ystart);
2492 for (i = start; i <= end; i++)
2494 int last_frame = end; // last frame of this "for" loop
2495 int x = xstart + i * xstep;
2496 int y = ystart + i * ystep;
2497 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2498 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2499 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2500 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2503 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2505 BlitScreenToBitmap(backbuffer);
2507 SetDrawtoField(DRAW_TO_BACKBUFFER);
2509 for (yy = 0; yy < ysize; yy++)
2510 for (xx = 0; xx < xsize; xx++)
2511 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2513 DrawTextBuffer(sx + font_width, sy + font_height,
2514 level.envelope[envelope_nr].text, font_nr, max_xsize,
2515 xsize - 2, ysize - 2, 0, mask_mode,
2516 level.envelope[envelope_nr].autowrap,
2517 level.envelope[envelope_nr].centered, FALSE);
2519 redraw_mask |= REDRAW_FIELD;
2522 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2526 void ShowEnvelope(int envelope_nr)
2528 int element = EL_ENVELOPE_1 + envelope_nr;
2529 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2530 int sound_opening = element_info[element].sound[ACTION_OPENING];
2531 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2532 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2533 boolean no_delay = (tape.warp_forward);
2534 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2535 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2536 int anim_mode = graphic_info[graphic].anim_mode;
2537 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2538 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2540 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2542 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2544 if (anim_mode == ANIM_DEFAULT)
2545 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2547 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2550 Delay(wait_delay_value);
2552 WaitForEventToContinue();
2554 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2556 if (anim_mode != ANIM_NONE)
2557 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2559 if (anim_mode == ANIM_DEFAULT)
2560 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2562 game.envelope_active = FALSE;
2564 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2566 redraw_mask |= REDRAW_FIELD;
2570 static void setRequestBasePosition(int *x, int *y)
2572 int sx_base, sy_base;
2574 if (request.x != -1)
2575 sx_base = request.x;
2576 else if (request.align == ALIGN_LEFT)
2578 else if (request.align == ALIGN_RIGHT)
2579 sx_base = SX + SXSIZE;
2581 sx_base = SX + SXSIZE / 2;
2583 if (request.y != -1)
2584 sy_base = request.y;
2585 else if (request.valign == VALIGN_TOP)
2587 else if (request.valign == VALIGN_BOTTOM)
2588 sy_base = SY + SYSIZE;
2590 sy_base = SY + SYSIZE / 2;
2596 static void setRequestPositionExt(int *x, int *y, int width, int height,
2597 boolean add_border_size)
2599 int border_size = request.border_size;
2600 int sx_base, sy_base;
2603 setRequestBasePosition(&sx_base, &sy_base);
2605 if (request.align == ALIGN_LEFT)
2607 else if (request.align == ALIGN_RIGHT)
2608 sx = sx_base - width;
2610 sx = sx_base - width / 2;
2612 if (request.valign == VALIGN_TOP)
2614 else if (request.valign == VALIGN_BOTTOM)
2615 sy = sy_base - height;
2617 sy = sy_base - height / 2;
2619 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2620 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2622 if (add_border_size)
2632 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2634 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2637 void DrawEnvelopeRequest(char *text)
2639 char *text_final = text;
2640 char *text_door_style = NULL;
2641 int graphic = IMG_BACKGROUND_REQUEST;
2642 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2643 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2644 int font_nr = FONT_REQUEST;
2645 int font_width = getFontWidth(font_nr);
2646 int font_height = getFontHeight(font_nr);
2647 int border_size = request.border_size;
2648 int line_spacing = request.line_spacing;
2649 int line_height = font_height + line_spacing;
2650 int max_text_width = request.width - 2 * border_size;
2651 int max_text_height = request.height - 2 * border_size;
2652 int line_length = max_text_width / font_width;
2653 int max_lines = max_text_height / line_height;
2654 int text_width = line_length * font_width;
2655 int width = request.width;
2656 int height = request.height;
2657 int tile_size = MAX(request.step_offset, 1);
2658 int x_steps = width / tile_size;
2659 int y_steps = height / tile_size;
2660 int sx_offset = border_size;
2661 int sy_offset = border_size;
2665 if (request.centered)
2666 sx_offset = (request.width - text_width) / 2;
2668 if (request.wrap_single_words && !request.autowrap)
2670 char *src_text_ptr, *dst_text_ptr;
2672 text_door_style = checked_malloc(2 * strlen(text) + 1);
2674 src_text_ptr = text;
2675 dst_text_ptr = text_door_style;
2677 while (*src_text_ptr)
2679 if (*src_text_ptr == ' ' ||
2680 *src_text_ptr == '?' ||
2681 *src_text_ptr == '!')
2682 *dst_text_ptr++ = '\n';
2684 if (*src_text_ptr != ' ')
2685 *dst_text_ptr++ = *src_text_ptr;
2690 *dst_text_ptr = '\0';
2692 text_final = text_door_style;
2695 setRequestPosition(&sx, &sy, FALSE);
2697 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2699 for (y = 0; y < y_steps; y++)
2700 for (x = 0; x < x_steps; x++)
2701 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2702 x, y, x_steps, y_steps,
2703 tile_size, tile_size);
2705 /* force DOOR font inside door area */
2706 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2708 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2709 line_length, -1, max_lines, line_spacing, mask_mode,
2710 request.autowrap, request.centered, FALSE);
2714 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2715 RedrawGadget(tool_gadget[i]);
2717 // store readily prepared envelope request for later use when animating
2718 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2720 if (text_door_style)
2721 free(text_door_style);
2724 void AnimateEnvelopeRequest(int anim_mode, int action)
2726 int graphic = IMG_BACKGROUND_REQUEST;
2727 boolean draw_masked = graphic_info[graphic].draw_masked;
2728 int delay_value_normal = request.step_delay;
2729 int delay_value_fast = delay_value_normal / 2;
2730 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2731 boolean no_delay = (tape.warp_forward);
2732 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2733 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2734 unsigned int anim_delay = 0;
2736 int tile_size = MAX(request.step_offset, 1);
2737 int max_xsize = request.width / tile_size;
2738 int max_ysize = request.height / tile_size;
2739 int max_xsize_inner = max_xsize - 2;
2740 int max_ysize_inner = max_ysize - 2;
2742 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2743 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2744 int xend = max_xsize_inner;
2745 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2746 int xstep = (xstart < xend ? 1 : 0);
2747 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2749 int end = MAX(xend - xstart, yend - ystart);
2752 if (setup.quick_doors)
2759 for (i = start; i <= end; i++)
2761 int last_frame = end; // last frame of this "for" loop
2762 int x = xstart + i * xstep;
2763 int y = ystart + i * ystep;
2764 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2765 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2766 int xsize_size_left = (xsize - 1) * tile_size;
2767 int ysize_size_top = (ysize - 1) * tile_size;
2768 int max_xsize_pos = (max_xsize - 1) * tile_size;
2769 int max_ysize_pos = (max_ysize - 1) * tile_size;
2770 int width = xsize * tile_size;
2771 int height = ysize * tile_size;
2776 setRequestPosition(&src_x, &src_y, FALSE);
2777 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2779 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2781 for (yy = 0; yy < 2; yy++)
2783 for (xx = 0; xx < 2; xx++)
2785 int src_xx = src_x + xx * max_xsize_pos;
2786 int src_yy = src_y + yy * max_ysize_pos;
2787 int dst_xx = dst_x + xx * xsize_size_left;
2788 int dst_yy = dst_y + yy * ysize_size_top;
2789 int xx_size = (xx ? tile_size : xsize_size_left);
2790 int yy_size = (yy ? tile_size : ysize_size_top);
2793 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
2794 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2796 BlitBitmap(bitmap_db_store_2, backbuffer,
2797 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2801 redraw_mask |= REDRAW_FIELD;
2805 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2809 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2811 int graphic = IMG_BACKGROUND_REQUEST;
2812 int sound_opening = SND_REQUEST_OPENING;
2813 int sound_closing = SND_REQUEST_CLOSING;
2814 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2815 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2816 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2817 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2818 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2820 if (game_status == GAME_MODE_PLAYING)
2821 BlitScreenToBitmap(backbuffer);
2823 SetDrawtoField(DRAW_TO_BACKBUFFER);
2825 // SetDrawBackgroundMask(REDRAW_NONE);
2827 if (action == ACTION_OPENING)
2829 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2831 if (req_state & REQ_ASK)
2833 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2834 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2836 else if (req_state & REQ_CONFIRM)
2838 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2840 else if (req_state & REQ_PLAYER)
2842 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2843 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2844 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2845 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2848 DrawEnvelopeRequest(text);
2851 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2853 if (action == ACTION_OPENING)
2855 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2857 if (anim_mode == ANIM_DEFAULT)
2858 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2860 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2864 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2866 if (anim_mode != ANIM_NONE)
2867 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2869 if (anim_mode == ANIM_DEFAULT)
2870 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2873 game.envelope_active = FALSE;
2875 if (action == ACTION_CLOSING)
2876 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2878 // SetDrawBackgroundMask(last_draw_background_mask);
2880 redraw_mask |= REDRAW_FIELD;
2884 if (action == ACTION_CLOSING &&
2885 game_status == GAME_MODE_PLAYING &&
2886 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2887 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2890 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2894 int graphic = el2preimg(element);
2896 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2897 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2900 void DrawLevel(int draw_background_mask)
2904 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2905 SetDrawBackgroundMask(draw_background_mask);
2909 for (x = BX1; x <= BX2; x++)
2910 for (y = BY1; y <= BY2; y++)
2911 DrawScreenField(x, y);
2913 redraw_mask |= REDRAW_FIELD;
2916 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2921 for (x = 0; x < size_x; x++)
2922 for (y = 0; y < size_y; y++)
2923 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2925 redraw_mask |= REDRAW_FIELD;
2928 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2932 for (x = 0; x < size_x; x++)
2933 for (y = 0; y < size_y; y++)
2934 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2936 redraw_mask |= REDRAW_FIELD;
2939 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2941 boolean show_level_border = (BorderElement != EL_EMPTY);
2942 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2943 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2944 int tile_size = preview.tile_size;
2945 int preview_width = preview.xsize * tile_size;
2946 int preview_height = preview.ysize * tile_size;
2947 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2948 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2949 int real_preview_width = real_preview_xsize * tile_size;
2950 int real_preview_height = real_preview_ysize * tile_size;
2951 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2952 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2955 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2958 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2960 dst_x += (preview_width - real_preview_width) / 2;
2961 dst_y += (preview_height - real_preview_height) / 2;
2963 for (x = 0; x < real_preview_xsize; x++)
2965 for (y = 0; y < real_preview_ysize; y++)
2967 int lx = from_x + x + (show_level_border ? -1 : 0);
2968 int ly = from_y + y + (show_level_border ? -1 : 0);
2969 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2970 getBorderElement(lx, ly));
2972 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2973 element, tile_size);
2977 redraw_mask |= REDRAW_FIELD;
2980 #define MICROLABEL_EMPTY 0
2981 #define MICROLABEL_LEVEL_NAME 1
2982 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2983 #define MICROLABEL_LEVEL_AUTHOR 3
2984 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2985 #define MICROLABEL_IMPORTED_FROM 5
2986 #define MICROLABEL_IMPORTED_BY_HEAD 6
2987 #define MICROLABEL_IMPORTED_BY 7
2989 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2991 int max_text_width = SXSIZE;
2992 int font_width = getFontWidth(font_nr);
2994 if (pos->align == ALIGN_CENTER)
2995 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2996 else if (pos->align == ALIGN_RIGHT)
2997 max_text_width = pos->x;
2999 max_text_width = SXSIZE - pos->x;
3001 return max_text_width / font_width;
3004 static void DrawPreviewLevelLabelExt(int mode)
3006 struct TextPosInfo *pos = &menu.main.text.level_info_2;
3007 char label_text[MAX_OUTPUT_LINESIZE + 1];
3008 int max_len_label_text;
3009 int font_nr = pos->font;
3012 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3015 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3016 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3017 mode == MICROLABEL_IMPORTED_BY_HEAD)
3018 font_nr = pos->font_alt;
3020 max_len_label_text = getMaxTextLength(pos, font_nr);
3022 if (pos->size != -1)
3023 max_len_label_text = pos->size;
3025 for (i = 0; i < max_len_label_text; i++)
3026 label_text[i] = ' ';
3027 label_text[max_len_label_text] = '\0';
3029 if (strlen(label_text) > 0)
3030 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3033 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3034 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3035 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3036 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3037 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3038 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3039 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3040 max_len_label_text);
3041 label_text[max_len_label_text] = '\0';
3043 if (strlen(label_text) > 0)
3044 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3046 redraw_mask |= REDRAW_FIELD;
3049 static void DrawPreviewLevelExt(boolean restart)
3051 static unsigned int scroll_delay = 0;
3052 static unsigned int label_delay = 0;
3053 static int from_x, from_y, scroll_direction;
3054 static int label_state, label_counter;
3055 unsigned int scroll_delay_value = preview.step_delay;
3056 boolean show_level_border = (BorderElement != EL_EMPTY);
3057 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3058 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3065 if (preview.anim_mode == ANIM_CENTERED)
3067 if (level_xsize > preview.xsize)
3068 from_x = (level_xsize - preview.xsize) / 2;
3069 if (level_ysize > preview.ysize)
3070 from_y = (level_ysize - preview.ysize) / 2;
3073 from_x += preview.xoffset;
3074 from_y += preview.yoffset;
3076 scroll_direction = MV_RIGHT;
3080 DrawPreviewLevelPlayfieldExt(from_x, from_y);
3081 DrawPreviewLevelLabelExt(label_state);
3083 /* initialize delay counters */
3084 DelayReached(&scroll_delay, 0);
3085 DelayReached(&label_delay, 0);
3087 if (leveldir_current->name)
3089 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3090 char label_text[MAX_OUTPUT_LINESIZE + 1];
3091 int font_nr = pos->font;
3092 int max_len_label_text = getMaxTextLength(pos, font_nr);
3094 if (pos->size != -1)
3095 max_len_label_text = pos->size;
3097 strncpy(label_text, leveldir_current->name, max_len_label_text);
3098 label_text[max_len_label_text] = '\0';
3100 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3101 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3107 /* scroll preview level, if needed */
3108 if (preview.anim_mode != ANIM_NONE &&
3109 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3110 DelayReached(&scroll_delay, scroll_delay_value))
3112 switch (scroll_direction)
3117 from_x -= preview.step_offset;
3118 from_x = (from_x < 0 ? 0 : from_x);
3121 scroll_direction = MV_UP;
3125 if (from_x < level_xsize - preview.xsize)
3127 from_x += preview.step_offset;
3128 from_x = (from_x > level_xsize - preview.xsize ?
3129 level_xsize - preview.xsize : from_x);
3132 scroll_direction = MV_DOWN;
3138 from_y -= preview.step_offset;
3139 from_y = (from_y < 0 ? 0 : from_y);
3142 scroll_direction = MV_RIGHT;
3146 if (from_y < level_ysize - preview.ysize)
3148 from_y += preview.step_offset;
3149 from_y = (from_y > level_ysize - preview.ysize ?
3150 level_ysize - preview.ysize : from_y);
3153 scroll_direction = MV_LEFT;
3160 DrawPreviewLevelPlayfieldExt(from_x, from_y);
3163 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
3164 /* redraw micro level label, if needed */
3165 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3166 !strEqual(level.author, ANONYMOUS_NAME) &&
3167 !strEqual(level.author, leveldir_current->name) &&
3168 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3170 int max_label_counter = 23;
3172 if (leveldir_current->imported_from != NULL &&
3173 strlen(leveldir_current->imported_from) > 0)
3174 max_label_counter += 14;
3175 if (leveldir_current->imported_by != NULL &&
3176 strlen(leveldir_current->imported_by) > 0)
3177 max_label_counter += 14;
3179 label_counter = (label_counter + 1) % max_label_counter;
3180 label_state = (label_counter >= 0 && label_counter <= 7 ?
3181 MICROLABEL_LEVEL_NAME :
3182 label_counter >= 9 && label_counter <= 12 ?
3183 MICROLABEL_LEVEL_AUTHOR_HEAD :
3184 label_counter >= 14 && label_counter <= 21 ?
3185 MICROLABEL_LEVEL_AUTHOR :
3186 label_counter >= 23 && label_counter <= 26 ?
3187 MICROLABEL_IMPORTED_FROM_HEAD :
3188 label_counter >= 28 && label_counter <= 35 ?
3189 MICROLABEL_IMPORTED_FROM :
3190 label_counter >= 37 && label_counter <= 40 ?
3191 MICROLABEL_IMPORTED_BY_HEAD :
3192 label_counter >= 42 && label_counter <= 49 ?
3193 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3195 if (leveldir_current->imported_from == NULL &&
3196 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3197 label_state == MICROLABEL_IMPORTED_FROM))
3198 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3199 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3201 DrawPreviewLevelLabelExt(label_state);
3205 void DrawPreviewLevelInitial()
3207 DrawPreviewLevelExt(TRUE);
3210 void DrawPreviewLevelAnimation()
3212 DrawPreviewLevelExt(FALSE);
3215 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3216 int graphic, int sync_frame,
3219 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3221 if (mask_mode == USE_MASKING)
3222 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3224 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3227 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3228 int graphic, int sync_frame, int mask_mode)
3230 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3232 if (mask_mode == USE_MASKING)
3233 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3235 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3238 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3240 int lx = LEVELX(x), ly = LEVELY(y);
3242 if (!IN_SCR_FIELD(x, y))
3245 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3246 graphic, GfxFrame[lx][ly], NO_MASKING);
3248 MarkTileDirty(x, y);
3251 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3253 int lx = LEVELX(x), ly = LEVELY(y);
3255 if (!IN_SCR_FIELD(x, y))
3258 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3259 graphic, GfxFrame[lx][ly], NO_MASKING);
3260 MarkTileDirty(x, y);
3263 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3265 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3268 void DrawLevelElementAnimation(int x, int y, int element)
3270 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3272 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3275 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3277 int sx = SCREENX(x), sy = SCREENY(y);
3279 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3282 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3285 DrawGraphicAnimation(sx, sy, graphic);
3288 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3289 DrawLevelFieldCrumbled(x, y);
3291 if (GFX_CRUMBLED(Feld[x][y]))
3292 DrawLevelFieldCrumbled(x, y);
3296 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3298 int sx = SCREENX(x), sy = SCREENY(y);
3301 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3304 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3306 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3309 DrawGraphicAnimation(sx, sy, graphic);
3311 if (GFX_CRUMBLED(element))
3312 DrawLevelFieldCrumbled(x, y);
3315 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3317 if (player->use_murphy)
3319 /* this works only because currently only one player can be "murphy" ... */
3320 static int last_horizontal_dir = MV_LEFT;
3321 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3323 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3324 last_horizontal_dir = move_dir;
3326 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3328 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3330 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3336 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3339 static boolean equalGraphics(int graphic1, int graphic2)
3341 struct GraphicInfo *g1 = &graphic_info[graphic1];
3342 struct GraphicInfo *g2 = &graphic_info[graphic2];
3344 return (g1->bitmap == g2->bitmap &&
3345 g1->src_x == g2->src_x &&
3346 g1->src_y == g2->src_y &&
3347 g1->anim_frames == g2->anim_frames &&
3348 g1->anim_delay == g2->anim_delay &&
3349 g1->anim_mode == g2->anim_mode);
3352 void DrawAllPlayers()
3356 for (i = 0; i < MAX_PLAYERS; i++)
3357 if (stored_player[i].active)
3358 DrawPlayer(&stored_player[i]);
3361 void DrawPlayerField(int x, int y)
3363 if (!IS_PLAYER(x, y))
3366 DrawPlayer(PLAYERINFO(x, y));
3369 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3371 void DrawPlayer(struct PlayerInfo *player)
3373 int jx = player->jx;
3374 int jy = player->jy;
3375 int move_dir = player->MovDir;
3376 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3377 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3378 int last_jx = (player->is_moving ? jx - dx : jx);
3379 int last_jy = (player->is_moving ? jy - dy : jy);
3380 int next_jx = jx + dx;
3381 int next_jy = jy + dy;
3382 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3383 boolean player_is_opaque = FALSE;
3384 int sx = SCREENX(jx), sy = SCREENY(jy);
3385 int sxx = 0, syy = 0;
3386 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3388 int action = ACTION_DEFAULT;
3389 int last_player_graphic = getPlayerGraphic(player, move_dir);
3390 int last_player_frame = player->Frame;
3393 /* GfxElement[][] is set to the element the player is digging or collecting;
3394 remove also for off-screen player if the player is not moving anymore */
3395 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3396 GfxElement[jx][jy] = EL_UNDEFINED;
3398 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3402 if (!IN_LEV_FIELD(jx, jy))
3404 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3405 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3406 printf("DrawPlayerField(): This should never happen!\n");
3411 if (element == EL_EXPLOSION)
3414 action = (player->is_pushing ? ACTION_PUSHING :
3415 player->is_digging ? ACTION_DIGGING :
3416 player->is_collecting ? ACTION_COLLECTING :
3417 player->is_moving ? ACTION_MOVING :
3418 player->is_snapping ? ACTION_SNAPPING :
3419 player->is_dropping ? ACTION_DROPPING :
3420 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3422 if (player->is_waiting)
3423 move_dir = player->dir_waiting;
3425 InitPlayerGfxAnimation(player, action, move_dir);
3427 /* ----------------------------------------------------------------------- */
3428 /* draw things in the field the player is leaving, if needed */
3429 /* ----------------------------------------------------------------------- */
3431 if (player->is_moving)
3433 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3435 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3437 if (last_element == EL_DYNAMITE_ACTIVE ||
3438 last_element == EL_EM_DYNAMITE_ACTIVE ||
3439 last_element == EL_SP_DISK_RED_ACTIVE)
3440 DrawDynamite(last_jx, last_jy);
3442 DrawLevelFieldThruMask(last_jx, last_jy);
3444 else if (last_element == EL_DYNAMITE_ACTIVE ||
3445 last_element == EL_EM_DYNAMITE_ACTIVE ||
3446 last_element == EL_SP_DISK_RED_ACTIVE)
3447 DrawDynamite(last_jx, last_jy);
3449 /* !!! this is not enough to prevent flickering of players which are
3450 moving next to each others without a free tile between them -- this
3451 can only be solved by drawing all players layer by layer (first the
3452 background, then the foreground etc.) !!! => TODO */
3453 else if (!IS_PLAYER(last_jx, last_jy))
3454 DrawLevelField(last_jx, last_jy);
3457 DrawLevelField(last_jx, last_jy);
3460 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3461 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3464 if (!IN_SCR_FIELD(sx, sy))
3467 /* ----------------------------------------------------------------------- */
3468 /* draw things behind the player, if needed */
3469 /* ----------------------------------------------------------------------- */
3472 DrawLevelElement(jx, jy, Back[jx][jy]);
3473 else if (IS_ACTIVE_BOMB(element))
3474 DrawLevelElement(jx, jy, EL_EMPTY);
3477 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3479 int old_element = GfxElement[jx][jy];
3480 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3481 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3483 if (GFX_CRUMBLED(old_element))
3484 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3486 DrawGraphic(sx, sy, old_graphic, frame);
3488 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3489 player_is_opaque = TRUE;
3493 GfxElement[jx][jy] = EL_UNDEFINED;
3495 /* make sure that pushed elements are drawn with correct frame rate */
3496 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3498 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3499 GfxFrame[jx][jy] = player->StepFrame;
3501 DrawLevelField(jx, jy);
3505 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3506 /* ----------------------------------------------------------------------- */
3507 /* draw player himself */
3508 /* ----------------------------------------------------------------------- */
3510 graphic = getPlayerGraphic(player, move_dir);
3512 /* in the case of changed player action or direction, prevent the current
3513 animation frame from being restarted for identical animations */
3514 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3515 player->Frame = last_player_frame;
3517 frame = getGraphicAnimationFrame(graphic, player->Frame);
3521 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3522 sxx = player->GfxPos;
3524 syy = player->GfxPos;
3527 if (player_is_opaque)
3528 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3530 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3532 if (SHIELD_ON(player))
3534 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3535 IMG_SHIELD_NORMAL_ACTIVE);
3536 int frame = getGraphicAnimationFrame(graphic, -1);
3538 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3542 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3545 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3546 sxx = player->GfxPos;
3548 syy = player->GfxPos;
3552 /* ----------------------------------------------------------------------- */
3553 /* draw things the player is pushing, if needed */
3554 /* ----------------------------------------------------------------------- */
3556 if (player->is_pushing && player->is_moving)
3558 int px = SCREENX(jx), py = SCREENY(jy);
3559 int pxx = (TILEX - ABS(sxx)) * dx;
3560 int pyy = (TILEY - ABS(syy)) * dy;
3561 int gfx_frame = GfxFrame[jx][jy];
3567 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3569 element = Feld[next_jx][next_jy];
3570 gfx_frame = GfxFrame[next_jx][next_jy];
3573 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3575 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3576 frame = getGraphicAnimationFrame(graphic, sync_frame);
3578 /* draw background element under pushed element (like the Sokoban field) */
3579 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3581 /* this allows transparent pushing animation over non-black background */
3584 DrawLevelElement(jx, jy, Back[jx][jy]);
3586 DrawLevelElement(jx, jy, EL_EMPTY);
3588 if (Back[next_jx][next_jy])
3589 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3591 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3593 else if (Back[next_jx][next_jy])
3594 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3597 /* do not draw (EM style) pushing animation when pushing is finished */
3598 /* (two-tile animations usually do not contain start and end frame) */
3599 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3600 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3602 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3604 /* masked drawing is needed for EMC style (double) movement graphics */
3605 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3606 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3610 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3611 /* ----------------------------------------------------------------------- */
3612 /* draw player himself */
3613 /* ----------------------------------------------------------------------- */
3615 graphic = getPlayerGraphic(player, move_dir);
3617 /* in the case of changed player action or direction, prevent the current
3618 animation frame from being restarted for identical animations */
3619 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3620 player->Frame = last_player_frame;
3622 frame = getGraphicAnimationFrame(graphic, player->Frame);
3626 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3627 sxx = player->GfxPos;
3629 syy = player->GfxPos;
3632 if (player_is_opaque)
3633 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3635 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3637 if (SHIELD_ON(player))
3639 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3640 IMG_SHIELD_NORMAL_ACTIVE);
3641 int frame = getGraphicAnimationFrame(graphic, -1);
3643 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3647 /* ----------------------------------------------------------------------- */
3648 /* draw things in front of player (active dynamite or dynabombs) */
3649 /* ----------------------------------------------------------------------- */
3651 if (IS_ACTIVE_BOMB(element))
3653 graphic = el2img(element);
3654 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3656 if (game.emulation == EMU_SUPAPLEX)
3657 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3659 DrawGraphicThruMask(sx, sy, graphic, frame);
3662 if (player_is_moving && last_element == EL_EXPLOSION)
3664 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3665 GfxElement[last_jx][last_jy] : EL_EMPTY);
3666 int graphic = el_act2img(element, ACTION_EXPLODING);
3667 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3668 int phase = ExplodePhase[last_jx][last_jy] - 1;
3669 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3672 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3675 /* ----------------------------------------------------------------------- */
3676 /* draw elements the player is just walking/passing through/under */
3677 /* ----------------------------------------------------------------------- */
3679 if (player_is_moving)
3681 /* handle the field the player is leaving ... */
3682 if (IS_ACCESSIBLE_INSIDE(last_element))
3683 DrawLevelField(last_jx, last_jy);
3684 else if (IS_ACCESSIBLE_UNDER(last_element))
3685 DrawLevelFieldThruMask(last_jx, last_jy);
3688 /* do not redraw accessible elements if the player is just pushing them */
3689 if (!player_is_moving || !player->is_pushing)
3691 /* ... and the field the player is entering */
3692 if (IS_ACCESSIBLE_INSIDE(element))
3693 DrawLevelField(jx, jy);
3694 else if (IS_ACCESSIBLE_UNDER(element))
3695 DrawLevelFieldThruMask(jx, jy);
3698 MarkTileDirty(sx, sy);
3701 /* ------------------------------------------------------------------------- */
3703 void WaitForEventToContinue()
3705 boolean still_wait = TRUE;
3707 /* simulate releasing mouse button over last gadget, if still pressed */
3709 HandleGadgets(-1, -1, 0);
3711 button_status = MB_RELEASED;
3725 case EVENT_BUTTONPRESS:
3726 case EVENT_KEYPRESS:
3730 case EVENT_KEYRELEASE:
3731 ClearPlayerAction();
3735 HandleOtherEvents(&event);
3739 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3748 #define MAX_REQUEST_LINES 13
3749 #define MAX_REQUEST_LINE_FONT1_LEN 7
3750 #define MAX_REQUEST_LINE_FONT2_LEN 10
3752 static int RequestHandleEvents(unsigned int req_state)
3754 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3755 local_player->LevelSolved_GameEnd);
3756 int width = request.width;
3757 int height = request.height;
3761 setRequestPosition(&sx, &sy, FALSE);
3763 button_status = MB_RELEASED;
3765 request_gadget_id = -1;
3772 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3774 HandleGameActions();
3776 SetDrawtoField(DRAW_TO_BACKBUFFER);
3778 if (global.use_envelope_request)
3780 /* copy current state of request area to middle of playfield area */
3781 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
3789 while (NextValidEvent(&event))
3793 case EVENT_BUTTONPRESS:
3794 case EVENT_BUTTONRELEASE:
3795 case EVENT_MOTIONNOTIFY:
3799 if (event.type == EVENT_MOTIONNOTIFY)
3804 motion_status = TRUE;
3805 mx = ((MotionEvent *) &event)->x;
3806 my = ((MotionEvent *) &event)->y;
3810 motion_status = FALSE;
3811 mx = ((ButtonEvent *) &event)->x;
3812 my = ((ButtonEvent *) &event)->y;
3813 if (event.type == EVENT_BUTTONPRESS)
3814 button_status = ((ButtonEvent *) &event)->button;
3816 button_status = MB_RELEASED;
3819 /* this sets 'request_gadget_id' */
3820 HandleGadgets(mx, my, button_status);
3822 switch (request_gadget_id)
3824 case TOOL_CTRL_ID_YES:
3827 case TOOL_CTRL_ID_NO:
3830 case TOOL_CTRL_ID_CONFIRM:
3831 result = TRUE | FALSE;
3834 case TOOL_CTRL_ID_PLAYER_1:
3837 case TOOL_CTRL_ID_PLAYER_2:
3840 case TOOL_CTRL_ID_PLAYER_3:
3843 case TOOL_CTRL_ID_PLAYER_4:
3854 case EVENT_KEYPRESS:
3856 Key key = GetEventKey((KeyEvent *)&event, TRUE);
3861 if (req_state & REQ_CONFIRM)
3866 #if defined(TARGET_SDL2)
3873 #if defined(TARGET_SDL2)
3880 HandleKeysDebug(key);
3884 if (req_state & REQ_PLAYER)
3890 case EVENT_KEYRELEASE:
3891 ClearPlayerAction();
3895 HandleOtherEvents(&event);
3900 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3902 int joy = AnyJoystick();
3904 if (joy & JOY_BUTTON_1)
3906 else if (joy & JOY_BUTTON_2)
3912 if (global.use_envelope_request)
3914 /* copy back current state of pressed buttons inside request area */
3915 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
3925 static boolean RequestDoor(char *text, unsigned int req_state)
3927 unsigned int old_door_state;
3928 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3929 int font_nr = FONT_TEXT_2;
3934 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3936 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3937 font_nr = FONT_TEXT_1;
3940 if (game_status == GAME_MODE_PLAYING)
3941 BlitScreenToBitmap(backbuffer);
3943 /* disable deactivated drawing when quick-loading level tape recording */
3944 if (tape.playing && tape.deactivate_display)
3945 TapeDeactivateDisplayOff(TRUE);
3947 SetMouseCursor(CURSOR_DEFAULT);
3949 #if defined(NETWORK_AVALIABLE)
3950 /* pause network game while waiting for request to answer */
3951 if (options.network &&
3952 game_status == GAME_MODE_PLAYING &&
3953 req_state & REQUEST_WAIT_FOR_INPUT)
3954 SendToServer_PausePlaying();
3957 old_door_state = GetDoorState();
3959 /* simulate releasing mouse button over last gadget, if still pressed */
3961 HandleGadgets(-1, -1, 0);
3965 /* draw released gadget before proceeding */
3968 if (old_door_state & DOOR_OPEN_1)
3970 CloseDoor(DOOR_CLOSE_1);
3972 /* save old door content */
3973 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3974 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3977 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3978 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3980 /* clear door drawing field */
3981 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3983 /* force DOOR font inside door area */
3984 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3986 /* write text for request */
3987 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3989 char text_line[max_request_line_len + 1];
3995 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3997 tc = *(text_ptr + tx);
3998 // if (!tc || tc == ' ')
3999 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4003 if ((tc == '?' || tc == '!') && tl == 0)
4013 strncpy(text_line, text_ptr, tl);
4016 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4017 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4018 text_line, font_nr);
4020 text_ptr += tl + (tc == ' ' ? 1 : 0);
4021 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4026 if (req_state & REQ_ASK)
4028 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4029 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4031 else if (req_state & REQ_CONFIRM)
4033 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4035 else if (req_state & REQ_PLAYER)
4037 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4038 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4039 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4040 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4043 /* copy request gadgets to door backbuffer */
4044 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4046 OpenDoor(DOOR_OPEN_1);
4048 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4050 if (game_status == GAME_MODE_PLAYING)
4052 SetPanelBackground();
4053 SetDrawBackgroundMask(REDRAW_DOOR_1);
4057 SetDrawBackgroundMask(REDRAW_FIELD);
4063 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4065 // ---------- handle request buttons ----------
4066 result = RequestHandleEvents(req_state);
4070 if (!(req_state & REQ_STAY_OPEN))
4072 CloseDoor(DOOR_CLOSE_1);
4074 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4075 (req_state & REQ_REOPEN))
4076 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4081 if (game_status == GAME_MODE_PLAYING)
4083 SetPanelBackground();
4084 SetDrawBackgroundMask(REDRAW_DOOR_1);
4088 SetDrawBackgroundMask(REDRAW_FIELD);
4091 #if defined(NETWORK_AVALIABLE)
4092 /* continue network game after request */
4093 if (options.network &&
4094 game_status == GAME_MODE_PLAYING &&
4095 req_state & REQUEST_WAIT_FOR_INPUT)
4096 SendToServer_ContinuePlaying();
4099 /* restore deactivated drawing when quick-loading level tape recording */
4100 if (tape.playing && tape.deactivate_display)
4101 TapeDeactivateDisplayOn();
4106 static boolean RequestEnvelope(char *text, unsigned int req_state)
4110 if (game_status == GAME_MODE_PLAYING)
4111 BlitScreenToBitmap(backbuffer);
4113 /* disable deactivated drawing when quick-loading level tape recording */
4114 if (tape.playing && tape.deactivate_display)
4115 TapeDeactivateDisplayOff(TRUE);
4117 SetMouseCursor(CURSOR_DEFAULT);
4119 #if defined(NETWORK_AVALIABLE)
4120 /* pause network game while waiting for request to answer */
4121 if (options.network &&
4122 game_status == GAME_MODE_PLAYING &&
4123 req_state & REQUEST_WAIT_FOR_INPUT)
4124 SendToServer_PausePlaying();
4127 /* simulate releasing mouse button over last gadget, if still pressed */
4129 HandleGadgets(-1, -1, 0);
4133 // (replace with setting corresponding request background)
4134 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4135 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4137 /* clear door drawing field */
4138 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4140 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4142 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4144 if (game_status == GAME_MODE_PLAYING)
4146 SetPanelBackground();
4147 SetDrawBackgroundMask(REDRAW_DOOR_1);
4151 SetDrawBackgroundMask(REDRAW_FIELD);
4157 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4159 // ---------- handle request buttons ----------
4160 result = RequestHandleEvents(req_state);
4164 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4168 if (game_status == GAME_MODE_PLAYING)
4170 SetPanelBackground();
4171 SetDrawBackgroundMask(REDRAW_DOOR_1);
4175 SetDrawBackgroundMask(REDRAW_FIELD);
4178 #if defined(NETWORK_AVALIABLE)
4179 /* continue network game after request */
4180 if (options.network &&
4181 game_status == GAME_MODE_PLAYING &&
4182 req_state & REQUEST_WAIT_FOR_INPUT)
4183 SendToServer_ContinuePlaying();
4186 /* restore deactivated drawing when quick-loading level tape recording */
4187 if (tape.playing && tape.deactivate_display)
4188 TapeDeactivateDisplayOn();
4193 boolean Request(char *text, unsigned int req_state)
4195 if (global.use_envelope_request)
4196 return RequestEnvelope(text, req_state);
4198 return RequestDoor(text, req_state);
4201 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4203 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4204 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4207 if (dpo1->sort_priority != dpo2->sort_priority)
4208 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4210 compare_result = dpo1->nr - dpo2->nr;
4212 return compare_result;
4215 void InitGraphicCompatibilityInfo_Doors()
4221 struct DoorInfo *door;
4225 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4226 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4228 { -1, -1, -1, NULL }
4230 struct Rect door_rect_list[] =
4232 { DX, DY, DXSIZE, DYSIZE },
4233 { VX, VY, VXSIZE, VYSIZE }
4237 for (i = 0; doors[i].door_token != -1; i++)
4239 int door_token = doors[i].door_token;
4240 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4241 int part_1 = doors[i].part_1;
4242 int part_8 = doors[i].part_8;
4243 int part_2 = part_1 + 1;
4244 int part_3 = part_1 + 2;
4245 struct DoorInfo *door = doors[i].door;
4246 struct Rect *door_rect = &door_rect_list[door_index];
4247 boolean door_gfx_redefined = FALSE;
4249 /* check if any door part graphic definitions have been redefined */
4251 for (j = 0; door_part_controls[j].door_token != -1; j++)
4253 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4254 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4256 if (dpc->door_token == door_token && fi->redefined)
4257 door_gfx_redefined = TRUE;
4260 /* check for old-style door graphic/animation modifications */
4262 if (!door_gfx_redefined)
4264 if (door->anim_mode & ANIM_STATIC_PANEL)
4266 door->panel.step_xoffset = 0;
4267 door->panel.step_yoffset = 0;
4270 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4272 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4273 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4274 int num_door_steps, num_panel_steps;
4276 /* remove door part graphics other than the two default wings */
4278 for (j = 0; door_part_controls[j].door_token != -1; j++)
4280 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4281 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4283 if (dpc->graphic >= part_3 &&
4284 dpc->graphic <= part_8)
4288 /* set graphics and screen positions of the default wings */
4290 g_part_1->width = door_rect->width;
4291 g_part_1->height = door_rect->height;
4292 g_part_2->width = door_rect->width;
4293 g_part_2->height = door_rect->height;
4294 g_part_2->src_x = door_rect->width;
4295 g_part_2->src_y = g_part_1->src_y;
4297 door->part_2.x = door->part_1.x;
4298 door->part_2.y = door->part_1.y;
4300 if (door->width != -1)
4302 g_part_1->width = door->width;
4303 g_part_2->width = door->width;
4305 // special treatment for graphics and screen position of right wing
4306 g_part_2->src_x += door_rect->width - door->width;
4307 door->part_2.x += door_rect->width - door->width;
4310 if (door->height != -1)
4312 g_part_1->height = door->height;
4313 g_part_2->height = door->height;
4315 // special treatment for graphics and screen position of bottom wing
4316 g_part_2->src_y += door_rect->height - door->height;
4317 door->part_2.y += door_rect->height - door->height;
4320 /* set animation delays for the default wings and panels */
4322 door->part_1.step_delay = door->step_delay;
4323 door->part_2.step_delay = door->step_delay;
4324 door->panel.step_delay = door->step_delay;
4326 /* set animation draw order for the default wings */
4328 door->part_1.sort_priority = 2; /* draw left wing over ... */
4329 door->part_2.sort_priority = 1; /* ... right wing */
4331 /* set animation draw offset for the default wings */
4333 if (door->anim_mode & ANIM_HORIZONTAL)
4335 door->part_1.step_xoffset = door->step_offset;
4336 door->part_1.step_yoffset = 0;
4337 door->part_2.step_xoffset = door->step_offset * -1;
4338 door->part_2.step_yoffset = 0;
4340 num_door_steps = g_part_1->width / door->step_offset;
4342 else // ANIM_VERTICAL
4344 door->part_1.step_xoffset = 0;
4345 door->part_1.step_yoffset = door->step_offset;
4346 door->part_2.step_xoffset = 0;
4347 door->part_2.step_yoffset = door->step_offset * -1;
4349 num_door_steps = g_part_1->height / door->step_offset;
4352 /* set animation draw offset for the default panels */
4354 if (door->step_offset > 1)
4356 num_panel_steps = 2 * door_rect->height / door->step_offset;
4357 door->panel.start_step = num_panel_steps - num_door_steps;
4358 door->panel.start_step_closing = door->panel.start_step;
4362 num_panel_steps = door_rect->height / door->step_offset;
4363 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4364 door->panel.start_step_closing = door->panel.start_step;
4365 door->panel.step_delay *= 2;
4376 for (i = 0; door_part_controls[i].door_token != -1; i++)
4378 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4379 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4381 /* initialize "start_step_opening" and "start_step_closing", if needed */
4382 if (dpc->pos->start_step_opening == 0 &&
4383 dpc->pos->start_step_closing == 0)
4385 // dpc->pos->start_step_opening = dpc->pos->start_step;
4386 dpc->pos->start_step_closing = dpc->pos->start_step;
4389 /* fill structure for door part draw order (sorted below) */
4391 dpo->sort_priority = dpc->pos->sort_priority;
4394 /* sort door part controls according to sort_priority and graphic number */
4395 qsort(door_part_order, MAX_DOOR_PARTS,
4396 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4399 unsigned int OpenDoor(unsigned int door_state)
4401 if (door_state & DOOR_COPY_BACK)
4403 if (door_state & DOOR_OPEN_1)
4404 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4405 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4407 if (door_state & DOOR_OPEN_2)
4408 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4409 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4411 door_state &= ~DOOR_COPY_BACK;
4414 return MoveDoor(door_state);
4417 unsigned int CloseDoor(unsigned int door_state)
4419 unsigned int old_door_state = GetDoorState();
4421 if (!(door_state & DOOR_NO_COPY_BACK))
4423 if (old_door_state & DOOR_OPEN_1)
4424 BlitBitmap(backbuffer, bitmap_db_door_1,
4425 DX, DY, DXSIZE, DYSIZE, 0, 0);
4427 if (old_door_state & DOOR_OPEN_2)
4428 BlitBitmap(backbuffer, bitmap_db_door_2,
4429 VX, VY, VXSIZE, VYSIZE, 0, 0);
4431 door_state &= ~DOOR_NO_COPY_BACK;
4434 return MoveDoor(door_state);
4437 unsigned int GetDoorState()
4439 return MoveDoor(DOOR_GET_STATE);
4442 unsigned int SetDoorState(unsigned int door_state)
4444 return MoveDoor(door_state | DOOR_SET_STATE);
4447 int euclid(int a, int b)
4449 return (b ? euclid(b, a % b) : a);
4452 unsigned int MoveDoor(unsigned int door_state)
4454 struct Rect door_rect_list[] =
4456 { DX, DY, DXSIZE, DYSIZE },
4457 { VX, VY, VXSIZE, VYSIZE }
4459 static int door1 = DOOR_CLOSE_1;
4460 static int door2 = DOOR_CLOSE_2;
4461 unsigned int door_delay = 0;
4462 unsigned int door_delay_value;
4465 if (door_state == DOOR_GET_STATE)
4466 return (door1 | door2);
4468 if (door_state & DOOR_SET_STATE)
4470 if (door_state & DOOR_ACTION_1)
4471 door1 = door_state & DOOR_ACTION_1;
4472 if (door_state & DOOR_ACTION_2)
4473 door2 = door_state & DOOR_ACTION_2;
4475 return (door1 | door2);
4478 if (!(door_state & DOOR_FORCE_REDRAW))
4480 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4481 door_state &= ~DOOR_OPEN_1;
4482 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4483 door_state &= ~DOOR_CLOSE_1;
4484 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4485 door_state &= ~DOOR_OPEN_2;
4486 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4487 door_state &= ~DOOR_CLOSE_2;
4490 if (global.autoplay_leveldir)
4492 door_state |= DOOR_NO_DELAY;
4493 door_state &= ~DOOR_CLOSE_ALL;
4496 if (game_status == GAME_MODE_EDITOR)
4497 door_state |= DOOR_NO_DELAY;
4499 if (door_state & DOOR_ACTION)
4501 boolean door_panel_drawn[NUM_DOORS];
4502 boolean panel_has_doors[NUM_DOORS];
4503 boolean door_part_skip[MAX_DOOR_PARTS];
4504 boolean door_part_done[MAX_DOOR_PARTS];
4505 boolean door_part_done_all;
4506 int num_steps[MAX_DOOR_PARTS];
4507 int max_move_delay = 0; // delay for complete animations of all doors
4508 int max_step_delay = 0; // delay (ms) between two animation frames
4509 int num_move_steps = 0; // number of animation steps for all doors
4510 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4511 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4512 int current_move_delay = 0;
4516 for (i = 0; i < NUM_DOORS; i++)
4517 panel_has_doors[i] = FALSE;
4519 for (i = 0; i < MAX_DOOR_PARTS; i++)
4521 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4522 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4523 int door_token = dpc->door_token;
4525 door_part_done[i] = FALSE;
4526 door_part_skip[i] = (!(door_state & door_token) ||
4530 for (i = 0; i < MAX_DOOR_PARTS; i++)
4532 int nr = door_part_order[i].nr;
4533 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4534 struct DoorPartPosInfo *pos = dpc->pos;
4535 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4536 int door_token = dpc->door_token;
4537 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4538 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4539 int step_xoffset = ABS(pos->step_xoffset);
4540 int step_yoffset = ABS(pos->step_yoffset);
4541 int step_delay = pos->step_delay;
4542 int current_door_state = door_state & door_token;
4543 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4544 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4545 boolean part_opening = (is_panel ? door_closing : door_opening);
4546 int start_step = (part_opening ? pos->start_step_opening :
4547 pos->start_step_closing);
4548 float move_xsize = (step_xoffset ? g->width : 0);
4549 float move_ysize = (step_yoffset ? g->height : 0);
4550 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4551 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4552 int move_steps = (move_xsteps && move_ysteps ?
4553 MIN(move_xsteps, move_ysteps) :
4554 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4555 int move_delay = move_steps * step_delay;
4557 if (door_part_skip[nr])
4560 max_move_delay = MAX(max_move_delay, move_delay);
4561 max_step_delay = (max_step_delay == 0 ? step_delay :
4562 euclid(max_step_delay, step_delay));
4563 num_steps[nr] = move_steps;
4567 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4569 panel_has_doors[door_index] = TRUE;
4573 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4575 num_move_steps = max_move_delay / max_step_delay;
4576 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4578 door_delay_value = max_step_delay;
4580 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4582 start = num_move_steps - 1;
4586 /* opening door sound has priority over simultaneously closing door */
4587 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4588 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4589 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4590 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4593 for (k = start; k < num_move_steps; k++)
4595 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4597 door_part_done_all = TRUE;
4599 for (i = 0; i < NUM_DOORS; i++)
4600 door_panel_drawn[i] = FALSE;
4602 for (i = 0; i < MAX_DOOR_PARTS; i++)
4604 int nr = door_part_order[i].nr;
4605 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4606 struct DoorPartPosInfo *pos = dpc->pos;
4607 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4608 int door_token = dpc->door_token;
4609 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4610 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4611 boolean is_panel_and_door_has_closed = FALSE;
4612 struct Rect *door_rect = &door_rect_list[door_index];
4613 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4615 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4616 int current_door_state = door_state & door_token;
4617 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4618 boolean door_closing = !door_opening;
4619 boolean part_opening = (is_panel ? door_closing : door_opening);
4620 boolean part_closing = !part_opening;
4621 int start_step = (part_opening ? pos->start_step_opening :
4622 pos->start_step_closing);
4623 int step_delay = pos->step_delay;
4624 int step_factor = step_delay / max_step_delay;
4625 int k1 = (step_factor ? k / step_factor + 1 : k);
4626 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4627 int kk = MAX(0, k2);
4630 int src_x, src_y, src_xx, src_yy;
4631 int dst_x, dst_y, dst_xx, dst_yy;
4634 if (door_part_skip[nr])
4637 if (!(door_state & door_token))
4645 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4646 int kk_door = MAX(0, k2_door);
4647 int sync_frame = kk_door * door_delay_value;
4648 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4650 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
4651 &g_src_x, &g_src_y);
4656 if (!door_panel_drawn[door_index])
4658 ClearRectangle(drawto, door_rect->x, door_rect->y,
4659 door_rect->width, door_rect->height);
4661 door_panel_drawn[door_index] = TRUE;
4664 // draw opening or closing door parts
4666 if (pos->step_xoffset < 0) // door part on right side
4669 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4672 if (dst_xx + width > door_rect->width)
4673 width = door_rect->width - dst_xx;
4675 else // door part on left side
4678 dst_xx = pos->x - kk * pos->step_xoffset;
4682 src_xx = ABS(dst_xx);
4686 width = g->width - src_xx;
4688 if (width > door_rect->width)
4689 width = door_rect->width;
4691 // printf("::: k == %d [%d] \n", k, start_step);
4694 if (pos->step_yoffset < 0) // door part on bottom side
4697 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4700 if (dst_yy + height > door_rect->height)
4701 height = door_rect->height - dst_yy;
4703 else // door part on top side
4706 dst_yy = pos->y - kk * pos->step_yoffset;
4710 src_yy = ABS(dst_yy);
4714 height = g->height - src_yy;
4717 src_x = g_src_x + src_xx;
4718 src_y = g_src_y + src_yy;
4720 dst_x = door_rect->x + dst_xx;
4721 dst_y = door_rect->y + dst_yy;
4723 is_panel_and_door_has_closed =
4726 panel_has_doors[door_index] &&
4727 k >= num_move_steps_doors_only - 1);
4729 if (width >= 0 && width <= g->width &&
4730 height >= 0 && height <= g->height &&
4731 !is_panel_and_door_has_closed)
4733 if (is_panel || !pos->draw_masked)
4734 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4737 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4741 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4743 if ((part_opening && (width < 0 || height < 0)) ||
4744 (part_closing && (width >= g->width && height >= g->height)))
4745 door_part_done[nr] = TRUE;
4747 // continue door part animations, but not panel after door has closed
4748 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4749 door_part_done_all = FALSE;
4752 if (!(door_state & DOOR_NO_DELAY))
4756 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4758 current_move_delay += max_step_delay;
4761 if (door_part_done_all)
4766 if (door_state & DOOR_ACTION_1)
4767 door1 = door_state & DOOR_ACTION_1;
4768 if (door_state & DOOR_ACTION_2)
4769 door2 = door_state & DOOR_ACTION_2;
4771 // draw masked border over door area
4772 DrawMaskedBorder(REDRAW_DOOR_1);
4773 DrawMaskedBorder(REDRAW_DOOR_2);
4775 return (door1 | door2);
4778 static boolean useSpecialEditorDoor()
4780 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4781 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4783 // do not draw special editor door if editor border defined or redefined
4784 if (graphic_info[graphic].bitmap != NULL || redefined)
4787 // do not draw special editor door if global border defined to be empty
4788 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4791 // do not draw special editor door if viewport definitions do not match
4795 EY + EYSIZE != VY + VYSIZE)
4801 void DrawSpecialEditorDoor()
4803 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4804 int top_border_width = gfx1->width;
4805 int top_border_height = gfx1->height;
4806 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4807 int ex = EX - outer_border;
4808 int ey = EY - outer_border;
4809 int vy = VY - outer_border;
4810 int exsize = EXSIZE + 2 * outer_border;
4812 if (!useSpecialEditorDoor())
4815 /* draw bigger level editor toolbox window */
4816 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4817 top_border_width, top_border_height, ex, ey - top_border_height);
4818 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4819 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4821 redraw_mask |= REDRAW_ALL;
4824 void UndrawSpecialEditorDoor()
4826 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4827 int top_border_width = gfx1->width;
4828 int top_border_height = gfx1->height;
4829 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4830 int ex = EX - outer_border;
4831 int ey = EY - outer_border;
4832 int ey_top = ey - top_border_height;
4833 int exsize = EXSIZE + 2 * outer_border;
4834 int eysize = EYSIZE + 2 * outer_border;
4836 if (!useSpecialEditorDoor())
4839 /* draw normal tape recorder window */
4840 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4842 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4843 ex, ey_top, top_border_width, top_border_height,
4845 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4846 ex, ey, exsize, eysize, ex, ey);
4850 // if screen background is set to "[NONE]", clear editor toolbox window
4851 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4852 ClearRectangle(drawto, ex, ey, exsize, eysize);
4855 redraw_mask |= REDRAW_ALL;
4859 /* ---------- new tool button stuff ---------------------------------------- */
4864 struct TextPosInfo *pos;
4867 } toolbutton_info[NUM_TOOL_BUTTONS] =
4870 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
4871 TOOL_CTRL_ID_YES, "yes"
4874 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
4875 TOOL_CTRL_ID_NO, "no"
4878 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
4879 TOOL_CTRL_ID_CONFIRM, "confirm"
4882 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
4883 TOOL_CTRL_ID_PLAYER_1, "player 1"
4886 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
4887 TOOL_CTRL_ID_PLAYER_2, "player 2"
4890 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
4891 TOOL_CTRL_ID_PLAYER_3, "player 3"
4894 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
4895 TOOL_CTRL_ID_PLAYER_4, "player 4"
4899 void CreateToolButtons()
4903 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4905 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4906 struct TextPosInfo *pos = toolbutton_info[i].pos;
4907 struct GadgetInfo *gi;
4908 Bitmap *deco_bitmap = None;
4909 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4910 unsigned int event_mask = GD_EVENT_RELEASED;
4913 int gd_x = gfx->src_x;
4914 int gd_y = gfx->src_y;
4915 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4916 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4919 if (global.use_envelope_request)
4920 setRequestPosition(&dx, &dy, TRUE);
4922 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4924 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4926 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4927 pos->size, &deco_bitmap, &deco_x, &deco_y);
4928 deco_xpos = (gfx->width - pos->size) / 2;
4929 deco_ypos = (gfx->height - pos->size) / 2;
4932 gi = CreateGadget(GDI_CUSTOM_ID, id,
4933 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4934 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4935 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4936 GDI_WIDTH, gfx->width,
4937 GDI_HEIGHT, gfx->height,
4938 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4939 GDI_STATE, GD_BUTTON_UNPRESSED,
4940 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4941 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4942 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4943 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4944 GDI_DECORATION_SIZE, pos->size, pos->size,
4945 GDI_DECORATION_SHIFTING, 1, 1,
4946 GDI_DIRECT_DRAW, FALSE,
4947 GDI_EVENT_MASK, event_mask,
4948 GDI_CALLBACK_ACTION, HandleToolButtons,
4952 Error(ERR_EXIT, "cannot create gadget");
4954 tool_gadget[id] = gi;
4958 void FreeToolButtons()
4962 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4963 FreeGadget(tool_gadget[i]);
4966 static void UnmapToolButtons()
4970 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4971 UnmapGadget(tool_gadget[i]);
4974 static void HandleToolButtons(struct GadgetInfo *gi)
4976 request_gadget_id = gi->custom_id;
4979 static struct Mapping_EM_to_RND_object
4982 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4983 boolean is_backside; /* backside of moving element */
4989 em_object_mapping_list[] =
4992 Xblank, TRUE, FALSE,
4996 Yacid_splash_eB, FALSE, FALSE,
4997 EL_ACID_SPLASH_RIGHT, -1, -1
5000 Yacid_splash_wB, FALSE, FALSE,
5001 EL_ACID_SPLASH_LEFT, -1, -1
5004 #ifdef EM_ENGINE_BAD_ROLL
5006 Xstone_force_e, FALSE, FALSE,
5007 EL_ROCK, -1, MV_BIT_RIGHT
5010 Xstone_force_w, FALSE, FALSE,
5011 EL_ROCK, -1, MV_BIT_LEFT
5014 Xnut_force_e, FALSE, FALSE,
5015 EL_NUT, -1, MV_BIT_RIGHT
5018 Xnut_force_w, FALSE, FALSE,
5019 EL_NUT, -1, MV_BIT_LEFT
5022 Xspring_force_e, FALSE, FALSE,
5023 EL_SPRING, -1, MV_BIT_RIGHT
5026 Xspring_force_w, FALSE, FALSE,
5027 EL_SPRING, -1, MV_BIT_LEFT
5030 Xemerald_force_e, FALSE, FALSE,
5031 EL_EMERALD, -1, MV_BIT_RIGHT
5034 Xemerald_force_w, FALSE, FALSE,
5035 EL_EMERALD, -1, MV_BIT_LEFT
5038 Xdiamond_force_e, FALSE, FALSE,
5039 EL_DIAMOND, -1, MV_BIT_RIGHT
5042 Xdiamond_force_w, FALSE, FALSE,
5043 EL_DIAMOND, -1, MV_BIT_LEFT
5046 Xbomb_force_e, FALSE, FALSE,
5047 EL_BOMB, -1, MV_BIT_RIGHT
5050 Xbomb_force_w, FALSE, FALSE,
5051 EL_BOMB, -1, MV_BIT_LEFT
5053 #endif /* EM_ENGINE_BAD_ROLL */
5056 Xstone, TRUE, FALSE,
5060 Xstone_pause, FALSE, FALSE,
5064 Xstone_fall, FALSE, FALSE,
5068 Ystone_s, FALSE, FALSE,
5069 EL_ROCK, ACTION_FALLING, -1
5072 Ystone_sB, FALSE, TRUE,
5073 EL_ROCK, ACTION_FALLING, -1
5076 Ystone_e, FALSE, FALSE,
5077 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5080 Ystone_eB, FALSE, TRUE,
5081 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5084 Ystone_w, FALSE, FALSE,
5085 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5088 Ystone_wB, FALSE, TRUE,
5089 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5096 Xnut_pause, FALSE, FALSE,
5100 Xnut_fall, FALSE, FALSE,
5104 Ynut_s, FALSE, FALSE,
5105 EL_NUT, ACTION_FALLING, -1
5108 Ynut_sB, FALSE, TRUE,
5109 EL_NUT, ACTION_FALLING, -1
5112 Ynut_e, FALSE, FALSE,
5113 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5116 Ynut_eB, FALSE, TRUE,
5117 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5120 Ynut_w, FALSE, FALSE,
5121 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5124 Ynut_wB, FALSE, TRUE,
5125 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5128 Xbug_n, TRUE, FALSE,
5132 Xbug_e, TRUE, FALSE,
5133 EL_BUG_RIGHT, -1, -1
5136 Xbug_s, TRUE, FALSE,
5140 Xbug_w, TRUE, FALSE,
5144 Xbug_gon, FALSE, FALSE,
5148 Xbug_goe, FALSE, FALSE,
5149 EL_BUG_RIGHT, -1, -1
5152 Xbug_gos, FALSE, FALSE,
5156 Xbug_gow, FALSE, FALSE,
5160 Ybug_n, FALSE, FALSE,
5161 EL_BUG, ACTION_MOVING, MV_BIT_UP
5164 Ybug_nB, FALSE, TRUE,
5165 EL_BUG, ACTION_MOVING, MV_BIT_UP
5168 Ybug_e, FALSE, FALSE,
5169 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5172 Ybug_eB, FALSE, TRUE,
5173 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5176 Ybug_s, FALSE, FALSE,
5177 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5180 Ybug_sB, FALSE, TRUE,
5181 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5184 Ybug_w, FALSE, FALSE,
5185 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5188 Ybug_wB, FALSE, TRUE,
5189 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5192 Ybug_w_n, FALSE, FALSE,
5193 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5196 Ybug_n_e, FALSE, FALSE,
5197 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5200 Ybug_e_s, FALSE, FALSE,
5201 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5204 Ybug_s_w, FALSE, FALSE,
5205 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5208 Ybug_e_n, FALSE, FALSE,
5209 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5212 Ybug_s_e, FALSE, FALSE,
5213 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5216 Ybug_w_s, FALSE, FALSE,
5217 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5220 Ybug_n_w, FALSE, FALSE,
5221 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5224 Ybug_stone, FALSE, FALSE,
5225 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5228 Ybug_spring, FALSE, FALSE,
5229 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5232 Xtank_n, TRUE, FALSE,
5233 EL_SPACESHIP_UP, -1, -1
5236 Xtank_e, TRUE, FALSE,
5237 EL_SPACESHIP_RIGHT, -1, -1
5240 Xtank_s, TRUE, FALSE,
5241 EL_SPACESHIP_DOWN, -1, -1
5244 Xtank_w, TRUE, FALSE,
5245 EL_SPACESHIP_LEFT, -1, -1
5248 Xtank_gon, FALSE, FALSE,
5249 EL_SPACESHIP_UP, -1, -1
5252 Xtank_goe, FALSE, FALSE,
5253 EL_SPACESHIP_RIGHT, -1, -1
5256 Xtank_gos, FALSE, FALSE,
5257 EL_SPACESHIP_DOWN, -1, -1
5260 Xtank_gow, FALSE, FALSE,
5261 EL_SPACESHIP_LEFT, -1, -1
5264 Ytank_n, FALSE, FALSE,
5265 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5268 Ytank_nB, FALSE, TRUE,
5269 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5272 Ytank_e, FALSE, FALSE,
5273 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5276 Ytank_eB, FALSE, TRUE,
5277 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5280 Ytank_s, FALSE, FALSE,
5281 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5284 Ytank_sB, FALSE, TRUE,
5285 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5288 Ytank_w, FALSE, FALSE,
5289 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5292 Ytank_wB, FALSE, TRUE,
5293 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5296 Ytank_w_n, FALSE, FALSE,
5297 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5300 Ytank_n_e, FALSE, FALSE,
5301 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5304 Ytank_e_s, FALSE, FALSE,
5305 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5308 Ytank_s_w, FALSE, FALSE,
5309 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5312 Ytank_e_n, FALSE, FALSE,
5313 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5316 Ytank_s_e, FALSE, FALSE,
5317 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5320 Ytank_w_s, FALSE, FALSE,
5321 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5324 Ytank_n_w, FALSE, FALSE,
5325 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5328 Ytank_stone, FALSE, FALSE,
5329 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5332 Ytank_spring, FALSE, FALSE,
5333 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5336 Xandroid, TRUE, FALSE,
5337 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5340 Xandroid_1_n, FALSE, FALSE,
5341 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5344 Xandroid_2_n, FALSE, FALSE,
5345 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5348 Xandroid_1_e, FALSE, FALSE,
5349 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5352 Xandroid_2_e, FALSE, FALSE,
5353 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5356 Xandroid_1_w, FALSE, FALSE,
5357 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5360 Xandroid_2_w, FALSE, FALSE,
5361 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5364 Xandroid_1_s, FALSE, FALSE,
5365 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5368 Xandroid_2_s, FALSE, FALSE,
5369 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5372 Yandroid_n, FALSE, FALSE,
5373 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5376 Yandroid_nB, FALSE, TRUE,
5377 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5380 Yandroid_ne, FALSE, FALSE,
5381 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5384 Yandroid_neB, FALSE, TRUE,
5385 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5388 Yandroid_e, FALSE, FALSE,
5389 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5392 Yandroid_eB, FALSE, TRUE,
5393 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5396 Yandroid_se, FALSE, FALSE,
5397 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5400 Yandroid_seB, FALSE, TRUE,
5401 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5404 Yandroid_s, FALSE, FALSE,
5405 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5408 Yandroid_sB, FALSE, TRUE,
5409 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5412 Yandroid_sw, FALSE, FALSE,
5413 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5416 Yandroid_swB, FALSE, TRUE,
5417 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5420 Yandroid_w, FALSE, FALSE,
5421 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5424 Yandroid_wB, FALSE, TRUE,
5425 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5428 Yandroid_nw, FALSE, FALSE,
5429 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5432 Yandroid_nwB, FALSE, TRUE,
5433 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5436 Xspring, TRUE, FALSE,
5440 Xspring_pause, FALSE, FALSE,
5444 Xspring_e, FALSE, FALSE,
5448 Xspring_w, FALSE, FALSE,
5452 Xspring_fall, FALSE, FALSE,
5456 Yspring_s, FALSE, FALSE,
5457 EL_SPRING, ACTION_FALLING, -1
5460 Yspring_sB, FALSE, TRUE,
5461 EL_SPRING, ACTION_FALLING, -1
5464 Yspring_e, FALSE, FALSE,
5465 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5468 Yspring_eB, FALSE, TRUE,
5469 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5472 Yspring_w, FALSE, FALSE,
5473 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5476 Yspring_wB, FALSE, TRUE,
5477 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5480 Yspring_kill_e, FALSE, FALSE,
5481 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5484 Yspring_kill_eB, FALSE, TRUE,
5485 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5488 Yspring_kill_w, FALSE, FALSE,
5489 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5492 Yspring_kill_wB, FALSE, TRUE,
5493 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5496 Xeater_n, TRUE, FALSE,
5497 EL_YAMYAM_UP, -1, -1
5500 Xeater_e, TRUE, FALSE,
5501 EL_YAMYAM_RIGHT, -1, -1
5504 Xeater_w, TRUE, FALSE,
5505 EL_YAMYAM_LEFT, -1, -1
5508 Xeater_s, TRUE, FALSE,
5509 EL_YAMYAM_DOWN, -1, -1
5512 Yeater_n, FALSE, FALSE,
5513 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5516 Yeater_nB, FALSE, TRUE,
5517 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5520 Yeater_e, FALSE, FALSE,
5521 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5524 Yeater_eB, FALSE, TRUE,
5525 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5528 Yeater_s, FALSE, FALSE,
5529 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5532 Yeater_sB, FALSE, TRUE,
5533 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5536 Yeater_w, FALSE, FALSE,
5537 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5540 Yeater_wB, FALSE, TRUE,
5541 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5544 Yeater_stone, FALSE, FALSE,
5545 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5548 Yeater_spring, FALSE, FALSE,
5549 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5552 Xalien, TRUE, FALSE,
5556 Xalien_pause, FALSE, FALSE,
5560 Yalien_n, FALSE, FALSE,
5561 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5564 Yalien_nB, FALSE, TRUE,
5565 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5568 Yalien_e, FALSE, FALSE,
5569 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5572 Yalien_eB, FALSE, TRUE,
5573 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5576 Yalien_s, FALSE, FALSE,
5577 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5580 Yalien_sB, FALSE, TRUE,
5581 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5584 Yalien_w, FALSE, FALSE,
5585 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5588 Yalien_wB, FALSE, TRUE,
5589 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5592 Yalien_stone, FALSE, FALSE,
5593 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5596 Yalien_spring, FALSE, FALSE,
5597 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5600 Xemerald, TRUE, FALSE,
5604 Xemerald_pause, FALSE, FALSE,
5608 Xemerald_fall, FALSE, FALSE,
5612 Xemerald_shine, FALSE, FALSE,
5613 EL_EMERALD, ACTION_TWINKLING, -1
5616 Yemerald_s, FALSE, FALSE,
5617 EL_EMERALD, ACTION_FALLING, -1
5620 Yemerald_sB, FALSE, TRUE,
5621 EL_EMERALD, ACTION_FALLING, -1
5624 Yemerald_e, FALSE, FALSE,
5625 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5628 Yemerald_eB, FALSE, TRUE,
5629 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5632 Yemerald_w, FALSE, FALSE,
5633 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5636 Yemerald_wB, FALSE, TRUE,
5637 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5640 Yemerald_eat, FALSE, FALSE,
5641 EL_EMERALD, ACTION_COLLECTING, -1
5644 Yemerald_stone, FALSE, FALSE,
5645 EL_NUT, ACTION_BREAKING, -1
5648 Xdiamond, TRUE, FALSE,
5652 Xdiamond_pause, FALSE, FALSE,
5656 Xdiamond_fall, FALSE, FALSE,
5660 Xdiamond_shine, FALSE, FALSE,
5661 EL_DIAMOND, ACTION_TWINKLING, -1
5664 Ydiamond_s, FALSE, FALSE,
5665 EL_DIAMOND, ACTION_FALLING, -1
5668 Ydiamond_sB, FALSE, TRUE,
5669 EL_DIAMOND, ACTION_FALLING, -1
5672 Ydiamond_e, FALSE, FALSE,
5673 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5676 Ydiamond_eB, FALSE, TRUE,
5677 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5680 Ydiamond_w, FALSE, FALSE,
5681 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5684 Ydiamond_wB, FALSE, TRUE,
5685 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5688 Ydiamond_eat, FALSE, FALSE,
5689 EL_DIAMOND, ACTION_COLLECTING, -1
5692 Ydiamond_stone, FALSE, FALSE,
5693 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5696 Xdrip_fall, TRUE, FALSE,
5697 EL_AMOEBA_DROP, -1, -1
5700 Xdrip_stretch, FALSE, FALSE,
5701 EL_AMOEBA_DROP, ACTION_FALLING, -1
5704 Xdrip_stretchB, FALSE, TRUE,
5705 EL_AMOEBA_DROP, ACTION_FALLING, -1
5708 Xdrip_eat, FALSE, FALSE,
5709 EL_AMOEBA_DROP, ACTION_GROWING, -1
5712 Ydrip_s1, FALSE, FALSE,
5713 EL_AMOEBA_DROP, ACTION_FALLING, -1
5716 Ydrip_s1B, FALSE, TRUE,
5717 EL_AMOEBA_DROP, ACTION_FALLING, -1
5720 Ydrip_s2, FALSE, FALSE,
5721 EL_AMOEBA_DROP, ACTION_FALLING, -1
5724 Ydrip_s2B, FALSE, TRUE,
5725 EL_AMOEBA_DROP, ACTION_FALLING, -1
5732 Xbomb_pause, FALSE, FALSE,
5736 Xbomb_fall, FALSE, FALSE,
5740 Ybomb_s, FALSE, FALSE,
5741 EL_BOMB, ACTION_FALLING, -1
5744 Ybomb_sB, FALSE, TRUE,
5745 EL_BOMB, ACTION_FALLING, -1
5748 Ybomb_e, FALSE, FALSE,
5749 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5752 Ybomb_eB, FALSE, TRUE,
5753 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5756 Ybomb_w, FALSE, FALSE,
5757 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5760 Ybomb_wB, FALSE, TRUE,
5761 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5764 Ybomb_eat, FALSE, FALSE,
5765 EL_BOMB, ACTION_ACTIVATING, -1
5768 Xballoon, TRUE, FALSE,
5772 Yballoon_n, FALSE, FALSE,
5773 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5776 Yballoon_nB, FALSE, TRUE,
5777 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5780 Yballoon_e, FALSE, FALSE,
5781 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5784 Yballoon_eB, FALSE, TRUE,
5785 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5788 Yballoon_s, FALSE, FALSE,
5789 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5792 Yballoon_sB, FALSE, TRUE,
5793 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5796 Yballoon_w, FALSE, FALSE,
5797 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5800 Yballoon_wB, FALSE, TRUE,
5801 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5804 Xgrass, TRUE, FALSE,
5805 EL_EMC_GRASS, -1, -1
5808 Ygrass_nB, FALSE, FALSE,
5809 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5812 Ygrass_eB, FALSE, FALSE,
5813 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5816 Ygrass_sB, FALSE, FALSE,
5817 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5820 Ygrass_wB, FALSE, FALSE,
5821 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5828 Ydirt_nB, FALSE, FALSE,
5829 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5832 Ydirt_eB, FALSE, FALSE,
5833 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5836 Ydirt_sB, FALSE, FALSE,
5837 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5840 Ydirt_wB, FALSE, FALSE,
5841 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5844 Xacid_ne, TRUE, FALSE,
5845 EL_ACID_POOL_TOPRIGHT, -1, -1
5848 Xacid_se, TRUE, FALSE,
5849 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5852 Xacid_s, TRUE, FALSE,
5853 EL_ACID_POOL_BOTTOM, -1, -1
5856 Xacid_sw, TRUE, FALSE,
5857 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5860 Xacid_nw, TRUE, FALSE,
5861 EL_ACID_POOL_TOPLEFT, -1, -1
5864 Xacid_1, TRUE, FALSE,
5868 Xacid_2, FALSE, FALSE,
5872 Xacid_3, FALSE, FALSE,
5876 Xacid_4, FALSE, FALSE,
5880 Xacid_5, FALSE, FALSE,
5884 Xacid_6, FALSE, FALSE,
5888 Xacid_7, FALSE, FALSE,
5892 Xacid_8, FALSE, FALSE,
5896 Xball_1, TRUE, FALSE,
5897 EL_EMC_MAGIC_BALL, -1, -1
5900 Xball_1B, FALSE, FALSE,
5901 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5904 Xball_2, FALSE, FALSE,
5905 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5908 Xball_2B, FALSE, FALSE,
5909 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5912 Yball_eat, FALSE, FALSE,
5913 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5916 Ykey_1_eat, FALSE, FALSE,
5917 EL_EM_KEY_1, ACTION_COLLECTING, -1
5920 Ykey_2_eat, FALSE, FALSE,
5921 EL_EM_KEY_2, ACTION_COLLECTING, -1
5924 Ykey_3_eat, FALSE, FALSE,
5925 EL_EM_KEY_3, ACTION_COLLECTING, -1
5928 Ykey_4_eat, FALSE, FALSE,
5929 EL_EM_KEY_4, ACTION_COLLECTING, -1
5932 Ykey_5_eat, FALSE, FALSE,
5933 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5936 Ykey_6_eat, FALSE, FALSE,
5937 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5940 Ykey_7_eat, FALSE, FALSE,
5941 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5944 Ykey_8_eat, FALSE, FALSE,
5945 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5948 Ylenses_eat, FALSE, FALSE,
5949 EL_EMC_LENSES, ACTION_COLLECTING, -1
5952 Ymagnify_eat, FALSE, FALSE,
5953 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5956 Ygrass_eat, FALSE, FALSE,
5957 EL_EMC_GRASS, ACTION_SNAPPING, -1
5960 Ydirt_eat, FALSE, FALSE,
5961 EL_SAND, ACTION_SNAPPING, -1
5964 Xgrow_ns, TRUE, FALSE,
5965 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5968 Ygrow_ns_eat, FALSE, FALSE,
5969 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5972 Xgrow_ew, TRUE, FALSE,
5973 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5976 Ygrow_ew_eat, FALSE, FALSE,
5977 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5980 Xwonderwall, TRUE, FALSE,
5981 EL_MAGIC_WALL, -1, -1
5984 XwonderwallB, FALSE, FALSE,
5985 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5988 Xamoeba_1, TRUE, FALSE,
5989 EL_AMOEBA_DRY, ACTION_OTHER, -1
5992 Xamoeba_2, FALSE, FALSE,
5993 EL_AMOEBA_DRY, ACTION_OTHER, -1
5996 Xamoeba_3, FALSE, FALSE,
5997 EL_AMOEBA_DRY, ACTION_OTHER, -1
6000 Xamoeba_4, FALSE, FALSE,
6001 EL_AMOEBA_DRY, ACTION_OTHER, -1
6004 Xamoeba_5, TRUE, FALSE,
6005 EL_AMOEBA_WET, ACTION_OTHER, -1
6008 Xamoeba_6, FALSE, FALSE,
6009 EL_AMOEBA_WET, ACTION_OTHER, -1
6012 Xamoeba_7, FALSE, FALSE,
6013 EL_AMOEBA_WET, ACTION_OTHER, -1
6016 Xamoeba_8, FALSE, FALSE,
6017 EL_AMOEBA_WET, ACTION_OTHER, -1
6020 Xdoor_1, TRUE, FALSE,
6021 EL_EM_GATE_1, -1, -1
6024 Xdoor_2, TRUE, FALSE,
6025 EL_EM_GATE_2, -1, -1
6028 Xdoor_3, TRUE, FALSE,
6029 EL_EM_GATE_3, -1, -1
6032 Xdoor_4, TRUE, FALSE,
6033 EL_EM_GATE_4, -1, -1
6036 Xdoor_5, TRUE, FALSE,
6037 EL_EMC_GATE_5, -1, -1
6040 Xdoor_6, TRUE, FALSE,
6041 EL_EMC_GATE_6, -1, -1
6044 Xdoor_7, TRUE, FALSE,
6045 EL_EMC_GATE_7, -1, -1
6048 Xdoor_8, TRUE, FALSE,
6049 EL_EMC_GATE_8, -1, -1
6052 Xkey_1, TRUE, FALSE,
6056 Xkey_2, TRUE, FALSE,
6060 Xkey_3, TRUE, FALSE,
6064 Xkey_4, TRUE, FALSE,
6068 Xkey_5, TRUE, FALSE,
6069 EL_EMC_KEY_5, -1, -1
6072 Xkey_6, TRUE, FALSE,
6073 EL_EMC_KEY_6, -1, -1
6076 Xkey_7, TRUE, FALSE,
6077 EL_EMC_KEY_7, -1, -1
6080 Xkey_8, TRUE, FALSE,
6081 EL_EMC_KEY_8, -1, -1
6084 Xwind_n, TRUE, FALSE,
6085 EL_BALLOON_SWITCH_UP, -1, -1
6088 Xwind_e, TRUE, FALSE,
6089 EL_BALLOON_SWITCH_RIGHT, -1, -1
6092 Xwind_s, TRUE, FALSE,
6093 EL_BALLOON_SWITCH_DOWN, -1, -1
6096 Xwind_w, TRUE, FALSE,
6097 EL_BALLOON_SWITCH_LEFT, -1, -1
6100 Xwind_nesw, TRUE, FALSE,
6101 EL_BALLOON_SWITCH_ANY, -1, -1
6104 Xwind_stop, TRUE, FALSE,
6105 EL_BALLOON_SWITCH_NONE, -1, -1
6109 EL_EM_EXIT_CLOSED, -1, -1
6112 Xexit_1, TRUE, FALSE,
6113 EL_EM_EXIT_OPEN, -1, -1
6116 Xexit_2, FALSE, FALSE,
6117 EL_EM_EXIT_OPEN, -1, -1
6120 Xexit_3, FALSE, FALSE,
6121 EL_EM_EXIT_OPEN, -1, -1
6124 Xdynamite, TRUE, FALSE,
6125 EL_EM_DYNAMITE, -1, -1
6128 Ydynamite_eat, FALSE, FALSE,
6129 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6132 Xdynamite_1, TRUE, FALSE,
6133 EL_EM_DYNAMITE_ACTIVE, -1, -1
6136 Xdynamite_2, FALSE, FALSE,
6137 EL_EM_DYNAMITE_ACTIVE, -1, -1
6140 Xdynamite_3, FALSE, FALSE,
6141 EL_EM_DYNAMITE_ACTIVE, -1, -1
6144 Xdynamite_4, FALSE, FALSE,
6145 EL_EM_DYNAMITE_ACTIVE, -1, -1
6148 Xbumper, TRUE, FALSE,
6149 EL_EMC_SPRING_BUMPER, -1, -1
6152 XbumperB, FALSE, FALSE,
6153 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6156 Xwheel, TRUE, FALSE,
6157 EL_ROBOT_WHEEL, -1, -1
6160 XwheelB, FALSE, FALSE,
6161 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6164 Xswitch, TRUE, FALSE,
6165 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6168 XswitchB, FALSE, FALSE,
6169 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6173 EL_QUICKSAND_EMPTY, -1, -1
6176 Xsand_stone, TRUE, FALSE,
6177 EL_QUICKSAND_FULL, -1, -1
6180 Xsand_stonein_1, FALSE, TRUE,
6181 EL_ROCK, ACTION_FILLING, -1
6184 Xsand_stonein_2, FALSE, TRUE,
6185 EL_ROCK, ACTION_FILLING, -1
6188 Xsand_stonein_3, FALSE, TRUE,
6189 EL_ROCK, ACTION_FILLING, -1
6192 Xsand_stonein_4, FALSE, TRUE,
6193 EL_ROCK, ACTION_FILLING, -1
6196 Xsand_stonesand_1, FALSE, FALSE,
6197 EL_QUICKSAND_EMPTYING, -1, -1
6200 Xsand_stonesand_2, FALSE, FALSE,
6201 EL_QUICKSAND_EMPTYING, -1, -1
6204 Xsand_stonesand_3, FALSE, FALSE,
6205 EL_QUICKSAND_EMPTYING, -1, -1
6208 Xsand_stonesand_4, FALSE, FALSE,
6209 EL_QUICKSAND_EMPTYING, -1, -1
6212 Xsand_stonesand_quickout_1, FALSE, FALSE,
6213 EL_QUICKSAND_EMPTYING, -1, -1
6216 Xsand_stonesand_quickout_2, FALSE, FALSE,
6217 EL_QUICKSAND_EMPTYING, -1, -1
6220 Xsand_stoneout_1, FALSE, FALSE,
6221 EL_ROCK, ACTION_EMPTYING, -1
6224 Xsand_stoneout_2, FALSE, FALSE,
6225 EL_ROCK, ACTION_EMPTYING, -1
6228 Xsand_sandstone_1, FALSE, FALSE,
6229 EL_QUICKSAND_FILLING, -1, -1
6232 Xsand_sandstone_2, FALSE, FALSE,
6233 EL_QUICKSAND_FILLING, -1, -1
6236 Xsand_sandstone_3, FALSE, FALSE,
6237 EL_QUICKSAND_FILLING, -1, -1
6240 Xsand_sandstone_4, FALSE, FALSE,
6241 EL_QUICKSAND_FILLING, -1, -1
6244 Xplant, TRUE, FALSE,
6245 EL_EMC_PLANT, -1, -1
6248 Yplant, FALSE, FALSE,
6249 EL_EMC_PLANT, -1, -1
6252 Xlenses, TRUE, FALSE,
6253 EL_EMC_LENSES, -1, -1
6256 Xmagnify, TRUE, FALSE,
6257 EL_EMC_MAGNIFIER, -1, -1
6260 Xdripper, TRUE, FALSE,
6261 EL_EMC_DRIPPER, -1, -1
6264 XdripperB, FALSE, FALSE,
6265 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6268 Xfake_blank, TRUE, FALSE,
6269 EL_INVISIBLE_WALL, -1, -1
6272 Xfake_blankB, FALSE, FALSE,
6273 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6276 Xfake_grass, TRUE, FALSE,
6277 EL_EMC_FAKE_GRASS, -1, -1
6280 Xfake_grassB, FALSE, FALSE,
6281 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6284 Xfake_door_1, TRUE, FALSE,
6285 EL_EM_GATE_1_GRAY, -1, -1
6288 Xfake_door_2, TRUE, FALSE,
6289 EL_EM_GATE_2_GRAY, -1, -1
6292 Xfake_door_3, TRUE, FALSE,
6293 EL_EM_GATE_3_GRAY, -1, -1
6296 Xfake_door_4, TRUE, FALSE,
6297 EL_EM_GATE_4_GRAY, -1, -1
6300 Xfake_door_5, TRUE, FALSE,
6301 EL_EMC_GATE_5_GRAY, -1, -1
6304 Xfake_door_6, TRUE, FALSE,
6305 EL_EMC_GATE_6_GRAY, -1, -1
6308 Xfake_door_7, TRUE, FALSE,
6309 EL_EMC_GATE_7_GRAY, -1, -1
6312 Xfake_door_8, TRUE, FALSE,
6313 EL_EMC_GATE_8_GRAY, -1, -1
6316 Xfake_acid_1, TRUE, FALSE,
6317 EL_EMC_FAKE_ACID, -1, -1
6320 Xfake_acid_2, FALSE, FALSE,
6321 EL_EMC_FAKE_ACID, -1, -1
6324 Xfake_acid_3, FALSE, FALSE,
6325 EL_EMC_FAKE_ACID, -1, -1
6328 Xfake_acid_4, FALSE, FALSE,
6329 EL_EMC_FAKE_ACID, -1, -1
6332 Xfake_acid_5, FALSE, FALSE,
6333 EL_EMC_FAKE_ACID, -1, -1
6336 Xfake_acid_6, FALSE, FALSE,
6337 EL_EMC_FAKE_ACID, -1, -1
6340 Xfake_acid_7, FALSE, FALSE,
6341 EL_EMC_FAKE_ACID, -1, -1
6344 Xfake_acid_8, FALSE, FALSE,
6345 EL_EMC_FAKE_ACID, -1, -1
6348 Xsteel_1, TRUE, FALSE,
6349 EL_STEELWALL, -1, -1
6352 Xsteel_2, TRUE, FALSE,
6353 EL_EMC_STEELWALL_2, -1, -1
6356 Xsteel_3, TRUE, FALSE,
6357 EL_EMC_STEELWALL_3, -1, -1
6360 Xsteel_4, TRUE, FALSE,
6361 EL_EMC_STEELWALL_4, -1, -1
6364 Xwall_1, TRUE, FALSE,
6368 Xwall_2, TRUE, FALSE,
6369 EL_EMC_WALL_14, -1, -1
6372 Xwall_3, TRUE, FALSE,
6373 EL_EMC_WALL_15, -1, -1
6376 Xwall_4, TRUE, FALSE,
6377 EL_EMC_WALL_16, -1, -1
6380 Xround_wall_1, TRUE, FALSE,
6381 EL_WALL_SLIPPERY, -1, -1
6384 Xround_wall_2, TRUE, FALSE,
6385 EL_EMC_WALL_SLIPPERY_2, -1, -1
6388 Xround_wall_3, TRUE, FALSE,
6389 EL_EMC_WALL_SLIPPERY_3, -1, -1
6392 Xround_wall_4, TRUE, FALSE,
6393 EL_EMC_WALL_SLIPPERY_4, -1, -1
6396 Xdecor_1, TRUE, FALSE,
6397 EL_EMC_WALL_8, -1, -1
6400 Xdecor_2, TRUE, FALSE,
6401 EL_EMC_WALL_6, -1, -1
6404 Xdecor_3, TRUE, FALSE,
6405 EL_EMC_WALL_4, -1, -1
6408 Xdecor_4, TRUE, FALSE,
6409 EL_EMC_WALL_7, -1, -1
6412 Xdecor_5, TRUE, FALSE,
6413 EL_EMC_WALL_5, -1, -1
6416 Xdecor_6, TRUE, FALSE,
6417 EL_EMC_WALL_9, -1, -1
6420 Xdecor_7, TRUE, FALSE,
6421 EL_EMC_WALL_10, -1, -1
6424 Xdecor_8, TRUE, FALSE,
6425 EL_EMC_WALL_1, -1, -1
6428 Xdecor_9, TRUE, FALSE,
6429 EL_EMC_WALL_2, -1, -1
6432 Xdecor_10, TRUE, FALSE,
6433 EL_EMC_WALL_3, -1, -1
6436 Xdecor_11, TRUE, FALSE,
6437 EL_EMC_WALL_11, -1, -1
6440 Xdecor_12, TRUE, FALSE,
6441 EL_EMC_WALL_12, -1, -1
6444 Xalpha_0, TRUE, FALSE,
6445 EL_CHAR('0'), -1, -1
6448 Xalpha_1, TRUE, FALSE,
6449 EL_CHAR('1'), -1, -1
6452 Xalpha_2, TRUE, FALSE,
6453 EL_CHAR('2'), -1, -1
6456 Xalpha_3, TRUE, FALSE,
6457 EL_CHAR('3'), -1, -1
6460 Xalpha_4, TRUE, FALSE,
6461 EL_CHAR('4'), -1, -1
6464 Xalpha_5, TRUE, FALSE,
6465 EL_CHAR('5'), -1, -1
6468 Xalpha_6, TRUE, FALSE,
6469 EL_CHAR('6'), -1, -1
6472 Xalpha_7, TRUE, FALSE,
6473 EL_CHAR('7'), -1, -1
6476 Xalpha_8, TRUE, FALSE,
6477 EL_CHAR('8'), -1, -1
6480 Xalpha_9, TRUE, FALSE,
6481 EL_CHAR('9'), -1, -1
6484 Xalpha_excla, TRUE, FALSE,
6485 EL_CHAR('!'), -1, -1
6488 Xalpha_quote, TRUE, FALSE,
6489 EL_CHAR('"'), -1, -1
6492 Xalpha_comma, TRUE, FALSE,
6493 EL_CHAR(','), -1, -1
6496 Xalpha_minus, TRUE, FALSE,
6497 EL_CHAR('-'), -1, -1
6500 Xalpha_perio, TRUE, FALSE,
6501 EL_CHAR('.'), -1, -1
6504 Xalpha_colon, TRUE, FALSE,
6505 EL_CHAR(':'), -1, -1
6508 Xalpha_quest, TRUE, FALSE,
6509 EL_CHAR('?'), -1, -1
6512 Xalpha_a, TRUE, FALSE,
6513 EL_CHAR('A'), -1, -1
6516 Xalpha_b, TRUE, FALSE,
6517 EL_CHAR('B'), -1, -1
6520 Xalpha_c, TRUE, FALSE,
6521 EL_CHAR('C'), -1, -1
6524 Xalpha_d, TRUE, FALSE,
6525 EL_CHAR('D'), -1, -1
6528 Xalpha_e, TRUE, FALSE,
6529 EL_CHAR('E'), -1, -1
6532 Xalpha_f, TRUE, FALSE,
6533 EL_CHAR('F'), -1, -1
6536 Xalpha_g, TRUE, FALSE,
6537 EL_CHAR('G'), -1, -1
6540 Xalpha_h, TRUE, FALSE,
6541 EL_CHAR('H'), -1, -1
6544 Xalpha_i, TRUE, FALSE,
6545 EL_CHAR('I'), -1, -1
6548 Xalpha_j, TRUE, FALSE,
6549 EL_CHAR('J'), -1, -1
6552 Xalpha_k, TRUE, FALSE,
6553 EL_CHAR('K'), -1, -1
6556 Xalpha_l, TRUE, FALSE,
6557 EL_CHAR('L'), -1, -1
6560 Xalpha_m, TRUE, FALSE,
6561 EL_CHAR('M'), -1, -1
6564 Xalpha_n, TRUE, FALSE,
6565 EL_CHAR('N'), -1, -1
6568 Xalpha_o, TRUE, FALSE,
6569 EL_CHAR('O'), -1, -1
6572 Xalpha_p, TRUE, FALSE,
6573 EL_CHAR('P'), -1, -1
6576 Xalpha_q, TRUE, FALSE,
6577 EL_CHAR('Q'), -1, -1
6580 Xalpha_r, TRUE, FALSE,
6581 EL_CHAR('R'), -1, -1
6584 Xalpha_s, TRUE, FALSE,
6585 EL_CHAR('S'), -1, -1
6588 Xalpha_t, TRUE, FALSE,
6589 EL_CHAR('T'), -1, -1
6592 Xalpha_u, TRUE, FALSE,
6593 EL_CHAR('U'), -1, -1
6596 Xalpha_v, TRUE, FALSE,
6597 EL_CHAR('V'), -1, -1
6600 Xalpha_w, TRUE, FALSE,
6601 EL_CHAR('W'), -1, -1
6604 Xalpha_x, TRUE, FALSE,
6605 EL_CHAR('X'), -1, -1
6608 Xalpha_y, TRUE, FALSE,
6609 EL_CHAR('Y'), -1, -1
6612 Xalpha_z, TRUE, FALSE,
6613 EL_CHAR('Z'), -1, -1
6616 Xalpha_arrow_e, TRUE, FALSE,
6617 EL_CHAR('>'), -1, -1
6620 Xalpha_arrow_w, TRUE, FALSE,
6621 EL_CHAR('<'), -1, -1
6624 Xalpha_copyr, TRUE, FALSE,
6625 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6629 Xboom_bug, FALSE, FALSE,
6630 EL_BUG, ACTION_EXPLODING, -1
6633 Xboom_bomb, FALSE, FALSE,
6634 EL_BOMB, ACTION_EXPLODING, -1
6637 Xboom_android, FALSE, FALSE,
6638 EL_EMC_ANDROID, ACTION_OTHER, -1
6641 Xboom_1, FALSE, FALSE,
6642 EL_DEFAULT, ACTION_EXPLODING, -1
6645 Xboom_2, FALSE, FALSE,
6646 EL_DEFAULT, ACTION_EXPLODING, -1
6649 Znormal, FALSE, FALSE,
6653 Zdynamite, FALSE, FALSE,
6657 Zplayer, FALSE, FALSE,
6661 ZBORDER, FALSE, FALSE,
6671 static struct Mapping_EM_to_RND_player
6680 em_player_mapping_list[] =
6684 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6688 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6692 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6696 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6700 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6704 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6708 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6712 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6716 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6720 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6724 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6728 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6732 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6736 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6740 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6744 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6748 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6752 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6756 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6760 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6764 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6768 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6772 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6776 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6780 EL_PLAYER_1, ACTION_DEFAULT, -1,
6784 EL_PLAYER_2, ACTION_DEFAULT, -1,
6788 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6792 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6796 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6800 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6804 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6808 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6812 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6816 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6820 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6824 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6828 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6832 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6836 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6840 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6844 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6848 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6852 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6856 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6860 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6864 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6868 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6872 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6876 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6880 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6884 EL_PLAYER_3, ACTION_DEFAULT, -1,
6888 EL_PLAYER_4, ACTION_DEFAULT, -1,
6897 int map_element_RND_to_EM(int element_rnd)
6899 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6900 static boolean mapping_initialized = FALSE;
6902 if (!mapping_initialized)
6906 /* return "Xalpha_quest" for all undefined elements in mapping array */
6907 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6908 mapping_RND_to_EM[i] = Xalpha_quest;
6910 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6911 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6912 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6913 em_object_mapping_list[i].element_em;
6915 mapping_initialized = TRUE;
6918 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6919 return mapping_RND_to_EM[element_rnd];
6921 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6926 int map_element_EM_to_RND(int element_em)
6928 static unsigned short mapping_EM_to_RND[TILE_MAX];
6929 static boolean mapping_initialized = FALSE;
6931 if (!mapping_initialized)
6935 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6936 for (i = 0; i < TILE_MAX; i++)
6937 mapping_EM_to_RND[i] = EL_UNKNOWN;
6939 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6940 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6941 em_object_mapping_list[i].element_rnd;
6943 mapping_initialized = TRUE;
6946 if (element_em >= 0 && element_em < TILE_MAX)
6947 return mapping_EM_to_RND[element_em];
6949 Error(ERR_WARN, "invalid EM level element %d", element_em);
6954 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6956 struct LevelInfo_EM *level_em = level->native_em_level;
6957 struct LEVEL *lev = level_em->lev;
6960 for (i = 0; i < TILE_MAX; i++)
6961 lev->android_array[i] = Xblank;
6963 for (i = 0; i < level->num_android_clone_elements; i++)
6965 int element_rnd = level->android_clone_element[i];
6966 int element_em = map_element_RND_to_EM(element_rnd);
6968 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6969 if (em_object_mapping_list[j].element_rnd == element_rnd)
6970 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6974 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6976 struct LevelInfo_EM *level_em = level->native_em_level;
6977 struct LEVEL *lev = level_em->lev;
6980 level->num_android_clone_elements = 0;
6982 for (i = 0; i < TILE_MAX; i++)
6984 int element_em = lev->android_array[i];
6986 boolean element_found = FALSE;
6988 if (element_em == Xblank)
6991 element_rnd = map_element_EM_to_RND(element_em);
6993 for (j = 0; j < level->num_android_clone_elements; j++)
6994 if (level->android_clone_element[j] == element_rnd)
6995 element_found = TRUE;
6999 level->android_clone_element[level->num_android_clone_elements++] =
7002 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7007 if (level->num_android_clone_elements == 0)
7009 level->num_android_clone_elements = 1;
7010 level->android_clone_element[0] = EL_EMPTY;
7014 int map_direction_RND_to_EM(int direction)
7016 return (direction == MV_UP ? 0 :
7017 direction == MV_RIGHT ? 1 :
7018 direction == MV_DOWN ? 2 :
7019 direction == MV_LEFT ? 3 :
7023 int map_direction_EM_to_RND(int direction)
7025 return (direction == 0 ? MV_UP :
7026 direction == 1 ? MV_RIGHT :
7027 direction == 2 ? MV_DOWN :
7028 direction == 3 ? MV_LEFT :
7032 int map_element_RND_to_SP(int element_rnd)
7034 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
7036 if (element_rnd >= EL_SP_START &&
7037 element_rnd <= EL_SP_END)
7038 element_sp = element_rnd - EL_SP_START;
7039 else if (element_rnd == EL_EMPTY_SPACE)
7041 else if (element_rnd == EL_INVISIBLE_WALL)
7047 int map_element_SP_to_RND(int element_sp)
7049 int element_rnd = EL_UNKNOWN;
7051 if (element_sp >= 0x00 &&
7053 element_rnd = EL_SP_START + element_sp;
7054 else if (element_sp == 0x28)
7055 element_rnd = EL_INVISIBLE_WALL;
7060 int map_action_SP_to_RND(int action_sp)
7064 case actActive: return ACTION_ACTIVE;
7065 case actImpact: return ACTION_IMPACT;
7066 case actExploding: return ACTION_EXPLODING;
7067 case actDigging: return ACTION_DIGGING;
7068 case actSnapping: return ACTION_SNAPPING;
7069 case actCollecting: return ACTION_COLLECTING;
7070 case actPassing: return ACTION_PASSING;
7071 case actPushing: return ACTION_PUSHING;
7072 case actDropping: return ACTION_DROPPING;
7074 default: return ACTION_DEFAULT;
7078 int get_next_element(int element)
7082 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7083 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7084 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7085 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7086 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7087 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7088 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7089 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7090 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7091 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7092 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7094 default: return element;
7098 int el_act_dir2img(int element, int action, int direction)
7100 element = GFX_ELEMENT(element);
7101 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7103 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7104 return element_info[element].direction_graphic[action][direction];
7107 static int el_act_dir2crm(int element, int action, int direction)
7109 element = GFX_ELEMENT(element);
7110 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7112 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7113 return element_info[element].direction_crumbled[action][direction];
7116 int el_act2img(int element, int action)
7118 element = GFX_ELEMENT(element);
7120 return element_info[element].graphic[action];
7123 int el_act2crm(int element, int action)
7125 element = GFX_ELEMENT(element);
7127 return element_info[element].crumbled[action];
7130 int el_dir2img(int element, int direction)
7132 element = GFX_ELEMENT(element);
7134 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7137 int el2baseimg(int element)
7139 return element_info[element].graphic[ACTION_DEFAULT];
7142 int el2img(int element)
7144 element = GFX_ELEMENT(element);
7146 return element_info[element].graphic[ACTION_DEFAULT];
7149 int el2edimg(int element)
7151 element = GFX_ELEMENT(element);
7153 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7156 int el2preimg(int element)
7158 element = GFX_ELEMENT(element);
7160 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7163 int el2panelimg(int element)
7165 element = GFX_ELEMENT(element);
7167 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7170 int font2baseimg(int font_nr)
7172 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7175 int getBeltNrFromBeltElement(int element)
7177 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7178 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7179 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
7182 int getBeltNrFromBeltActiveElement(int element)
7184 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
7185 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
7186 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
7189 int getBeltNrFromBeltSwitchElement(int element)
7191 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
7192 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
7193 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
7196 int getBeltDirNrFromBeltElement(int element)
7198 static int belt_base_element[4] =
7200 EL_CONVEYOR_BELT_1_LEFT,
7201 EL_CONVEYOR_BELT_2_LEFT,
7202 EL_CONVEYOR_BELT_3_LEFT,
7203 EL_CONVEYOR_BELT_4_LEFT
7206 int belt_nr = getBeltNrFromBeltElement(element);
7207 int belt_dir_nr = element - belt_base_element[belt_nr];
7209 return (belt_dir_nr % 3);
7212 int getBeltDirNrFromBeltSwitchElement(int element)
7214 static int belt_base_element[4] =
7216 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7217 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7218 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7219 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7222 int belt_nr = getBeltNrFromBeltSwitchElement(element);
7223 int belt_dir_nr = element - belt_base_element[belt_nr];
7225 return (belt_dir_nr % 3);
7228 int getBeltDirFromBeltElement(int element)
7230 static int belt_move_dir[3] =
7237 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7239 return belt_move_dir[belt_dir_nr];
7242 int getBeltDirFromBeltSwitchElement(int element)
7244 static int belt_move_dir[3] =
7251 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7253 return belt_move_dir[belt_dir_nr];
7256 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7258 static int belt_base_element[4] =
7260 EL_CONVEYOR_BELT_1_LEFT,
7261 EL_CONVEYOR_BELT_2_LEFT,
7262 EL_CONVEYOR_BELT_3_LEFT,
7263 EL_CONVEYOR_BELT_4_LEFT
7266 return belt_base_element[belt_nr] + belt_dir_nr;
7269 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7271 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7273 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7276 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7278 static int belt_base_element[4] =
7280 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7281 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7282 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7283 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7286 return belt_base_element[belt_nr] + belt_dir_nr;
7289 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7291 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7293 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7296 boolean getTeamMode_EM()
7298 return game.team_mode;
7301 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7303 int game_frame_delay_value;
7305 game_frame_delay_value =
7306 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7307 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7310 if (tape.playing && tape.warp_forward && !tape.pausing)
7311 game_frame_delay_value = 0;
7313 return game_frame_delay_value;
7316 unsigned int InitRND(int seed)
7318 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7319 return InitEngineRandom_EM(seed);
7320 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7321 return InitEngineRandom_SP(seed);
7323 return InitEngineRandom_RND(seed);
7326 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7327 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7329 inline static int get_effective_element_EM(int tile, int frame_em)
7331 int element = object_mapping[tile].element_rnd;
7332 int action = object_mapping[tile].action;
7333 boolean is_backside = object_mapping[tile].is_backside;
7334 boolean action_removing = (action == ACTION_DIGGING ||
7335 action == ACTION_SNAPPING ||
7336 action == ACTION_COLLECTING);
7342 case Yacid_splash_eB:
7343 case Yacid_splash_wB:
7344 return (frame_em > 5 ? EL_EMPTY : element);
7350 else /* frame_em == 7 */
7354 case Yacid_splash_eB:
7355 case Yacid_splash_wB:
7358 case Yemerald_stone:
7361 case Ydiamond_stone:
7365 case Xdrip_stretchB:
7384 case Xsand_stonein_1:
7385 case Xsand_stonein_2:
7386 case Xsand_stonein_3:
7387 case Xsand_stonein_4:
7391 return (is_backside || action_removing ? EL_EMPTY : element);
7396 inline static boolean check_linear_animation_EM(int tile)
7400 case Xsand_stonesand_1:
7401 case Xsand_stonesand_quickout_1:
7402 case Xsand_sandstone_1:
7403 case Xsand_stonein_1:
7404 case Xsand_stoneout_1:
7423 case Yacid_splash_eB:
7424 case Yacid_splash_wB:
7425 case Yemerald_stone:
7432 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7433 boolean has_crumbled_graphics,
7434 int crumbled, int sync_frame)
7436 /* if element can be crumbled, but certain action graphics are just empty
7437 space (like instantly snapping sand to empty space in 1 frame), do not
7438 treat these empty space graphics as crumbled graphics in EMC engine */
7439 if (crumbled == IMG_EMPTY_SPACE)
7440 has_crumbled_graphics = FALSE;
7442 if (has_crumbled_graphics)
7444 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7445 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7446 g_crumbled->anim_delay,
7447 g_crumbled->anim_mode,
7448 g_crumbled->anim_start_frame,
7451 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7452 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7454 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7455 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
7457 g_em->has_crumbled_graphics = TRUE;
7461 g_em->crumbled_bitmap = NULL;
7462 g_em->crumbled_src_x = 0;
7463 g_em->crumbled_src_y = 0;
7464 g_em->crumbled_border_size = 0;
7465 g_em->crumbled_tile_size = 0;
7467 g_em->has_crumbled_graphics = FALSE;
7471 void ResetGfxAnimation_EM(int x, int y, int tile)
7476 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7477 int tile, int frame_em, int x, int y)
7479 int action = object_mapping[tile].action;
7480 int direction = object_mapping[tile].direction;
7481 int effective_element = get_effective_element_EM(tile, frame_em);
7482 int graphic = (direction == MV_NONE ?
7483 el_act2img(effective_element, action) :
7484 el_act_dir2img(effective_element, action, direction));
7485 struct GraphicInfo *g = &graphic_info[graphic];
7487 boolean action_removing = (action == ACTION_DIGGING ||
7488 action == ACTION_SNAPPING ||
7489 action == ACTION_COLLECTING);
7490 boolean action_moving = (action == ACTION_FALLING ||
7491 action == ACTION_MOVING ||
7492 action == ACTION_PUSHING ||
7493 action == ACTION_EATING ||
7494 action == ACTION_FILLING ||
7495 action == ACTION_EMPTYING);
7496 boolean action_falling = (action == ACTION_FALLING ||
7497 action == ACTION_FILLING ||
7498 action == ACTION_EMPTYING);
7500 /* special case: graphic uses "2nd movement tile" and has defined
7501 7 frames for movement animation (or less) => use default graphic
7502 for last (8th) frame which ends the movement animation */
7503 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7505 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7506 graphic = (direction == MV_NONE ?
7507 el_act2img(effective_element, action) :
7508 el_act_dir2img(effective_element, action, direction));
7510 g = &graphic_info[graphic];
7513 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7517 else if (action_moving)
7519 boolean is_backside = object_mapping[tile].is_backside;
7523 int direction = object_mapping[tile].direction;
7524 int move_dir = (action_falling ? MV_DOWN : direction);
7529 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7530 if (g->double_movement && frame_em == 0)
7534 if (move_dir == MV_LEFT)
7535 GfxFrame[x - 1][y] = GfxFrame[x][y];
7536 else if (move_dir == MV_RIGHT)
7537 GfxFrame[x + 1][y] = GfxFrame[x][y];
7538 else if (move_dir == MV_UP)
7539 GfxFrame[x][y - 1] = GfxFrame[x][y];
7540 else if (move_dir == MV_DOWN)
7541 GfxFrame[x][y + 1] = GfxFrame[x][y];
7548 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7549 if (tile == Xsand_stonesand_quickout_1 ||
7550 tile == Xsand_stonesand_quickout_2)
7554 if (graphic_info[graphic].anim_global_sync)
7555 sync_frame = FrameCounter;
7556 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7557 sync_frame = GfxFrame[x][y];
7559 sync_frame = 0; /* playfield border (pseudo steel) */
7561 SetRandomAnimationValue(x, y);
7563 int frame = getAnimationFrame(g->anim_frames,
7566 g->anim_start_frame,
7569 g_em->unique_identifier =
7570 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7573 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7574 int tile, int frame_em, int x, int y)
7576 int action = object_mapping[tile].action;
7577 int direction = object_mapping[tile].direction;
7578 boolean is_backside = object_mapping[tile].is_backside;
7579 int effective_element = get_effective_element_EM(tile, frame_em);
7580 int effective_action = action;
7581 int graphic = (direction == MV_NONE ?
7582 el_act2img(effective_element, effective_action) :
7583 el_act_dir2img(effective_element, effective_action,
7585 int crumbled = (direction == MV_NONE ?
7586 el_act2crm(effective_element, effective_action) :
7587 el_act_dir2crm(effective_element, effective_action,
7589 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7590 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7591 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7592 struct GraphicInfo *g = &graphic_info[graphic];
7595 /* special case: graphic uses "2nd movement tile" and has defined
7596 7 frames for movement animation (or less) => use default graphic
7597 for last (8th) frame which ends the movement animation */
7598 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7600 effective_action = ACTION_DEFAULT;
7601 graphic = (direction == MV_NONE ?
7602 el_act2img(effective_element, effective_action) :
7603 el_act_dir2img(effective_element, effective_action,
7605 crumbled = (direction == MV_NONE ?
7606 el_act2crm(effective_element, effective_action) :
7607 el_act_dir2crm(effective_element, effective_action,
7610 g = &graphic_info[graphic];
7613 if (graphic_info[graphic].anim_global_sync)
7614 sync_frame = FrameCounter;
7615 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7616 sync_frame = GfxFrame[x][y];
7618 sync_frame = 0; /* playfield border (pseudo steel) */
7620 SetRandomAnimationValue(x, y);
7622 int frame = getAnimationFrame(g->anim_frames,
7625 g->anim_start_frame,
7628 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7629 g->double_movement && is_backside);
7631 /* (updating the "crumbled" graphic definitions is probably not really needed,
7632 as animations for crumbled graphics can't be longer than one EMC cycle) */
7633 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7637 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7638 int player_nr, int anim, int frame_em)
7640 int element = player_mapping[player_nr][anim].element_rnd;
7641 int action = player_mapping[player_nr][anim].action;
7642 int direction = player_mapping[player_nr][anim].direction;
7643 int graphic = (direction == MV_NONE ?
7644 el_act2img(element, action) :
7645 el_act_dir2img(element, action, direction));
7646 struct GraphicInfo *g = &graphic_info[graphic];
7649 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7651 stored_player[player_nr].StepFrame = frame_em;
7653 sync_frame = stored_player[player_nr].Frame;
7655 int frame = getAnimationFrame(g->anim_frames,
7658 g->anim_start_frame,
7661 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7662 &g_em->src_x, &g_em->src_y, FALSE);
7665 void InitGraphicInfo_EM(void)
7670 int num_em_gfx_errors = 0;
7672 if (graphic_info_em_object[0][0].bitmap == NULL)
7674 /* EM graphics not yet initialized in em_open_all() */
7679 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7682 /* always start with reliable default values */
7683 for (i = 0; i < TILE_MAX; i++)
7685 object_mapping[i].element_rnd = EL_UNKNOWN;
7686 object_mapping[i].is_backside = FALSE;
7687 object_mapping[i].action = ACTION_DEFAULT;
7688 object_mapping[i].direction = MV_NONE;
7691 /* always start with reliable default values */
7692 for (p = 0; p < MAX_PLAYERS; p++)
7694 for (i = 0; i < SPR_MAX; i++)
7696 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7697 player_mapping[p][i].action = ACTION_DEFAULT;
7698 player_mapping[p][i].direction = MV_NONE;
7702 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7704 int e = em_object_mapping_list[i].element_em;
7706 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7707 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7709 if (em_object_mapping_list[i].action != -1)
7710 object_mapping[e].action = em_object_mapping_list[i].action;
7712 if (em_object_mapping_list[i].direction != -1)
7713 object_mapping[e].direction =
7714 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7717 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7719 int a = em_player_mapping_list[i].action_em;
7720 int p = em_player_mapping_list[i].player_nr;
7722 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7724 if (em_player_mapping_list[i].action != -1)
7725 player_mapping[p][a].action = em_player_mapping_list[i].action;
7727 if (em_player_mapping_list[i].direction != -1)
7728 player_mapping[p][a].direction =
7729 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7732 for (i = 0; i < TILE_MAX; i++)
7734 int element = object_mapping[i].element_rnd;
7735 int action = object_mapping[i].action;
7736 int direction = object_mapping[i].direction;
7737 boolean is_backside = object_mapping[i].is_backside;
7738 boolean action_exploding = ((action == ACTION_EXPLODING ||
7739 action == ACTION_SMASHED_BY_ROCK ||
7740 action == ACTION_SMASHED_BY_SPRING) &&
7741 element != EL_DIAMOND);
7742 boolean action_active = (action == ACTION_ACTIVE);
7743 boolean action_other = (action == ACTION_OTHER);
7745 for (j = 0; j < 8; j++)
7747 int effective_element = get_effective_element_EM(i, j);
7748 int effective_action = (j < 7 ? action :
7749 i == Xdrip_stretch ? action :
7750 i == Xdrip_stretchB ? action :
7751 i == Ydrip_s1 ? action :
7752 i == Ydrip_s1B ? action :
7753 i == Xball_1B ? action :
7754 i == Xball_2 ? action :
7755 i == Xball_2B ? action :
7756 i == Yball_eat ? action :
7757 i == Ykey_1_eat ? action :
7758 i == Ykey_2_eat ? action :
7759 i == Ykey_3_eat ? action :
7760 i == Ykey_4_eat ? action :
7761 i == Ykey_5_eat ? action :
7762 i == Ykey_6_eat ? action :
7763 i == Ykey_7_eat ? action :
7764 i == Ykey_8_eat ? action :
7765 i == Ylenses_eat ? action :
7766 i == Ymagnify_eat ? action :
7767 i == Ygrass_eat ? action :
7768 i == Ydirt_eat ? action :
7769 i == Xsand_stonein_1 ? action :
7770 i == Xsand_stonein_2 ? action :
7771 i == Xsand_stonein_3 ? action :
7772 i == Xsand_stonein_4 ? action :
7773 i == Xsand_stoneout_1 ? action :
7774 i == Xsand_stoneout_2 ? action :
7775 i == Xboom_android ? ACTION_EXPLODING :
7776 action_exploding ? ACTION_EXPLODING :
7777 action_active ? action :
7778 action_other ? action :
7780 int graphic = (el_act_dir2img(effective_element, effective_action,
7782 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7784 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7785 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7786 boolean has_action_graphics = (graphic != base_graphic);
7787 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7788 struct GraphicInfo *g = &graphic_info[graphic];
7789 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7792 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7793 boolean special_animation = (action != ACTION_DEFAULT &&
7794 g->anim_frames == 3 &&
7795 g->anim_delay == 2 &&
7796 g->anim_mode & ANIM_LINEAR);
7797 int sync_frame = (i == Xdrip_stretch ? 7 :
7798 i == Xdrip_stretchB ? 7 :
7799 i == Ydrip_s2 ? j + 8 :
7800 i == Ydrip_s2B ? j + 8 :
7809 i == Xfake_acid_1 ? 0 :
7810 i == Xfake_acid_2 ? 10 :
7811 i == Xfake_acid_3 ? 20 :
7812 i == Xfake_acid_4 ? 30 :
7813 i == Xfake_acid_5 ? 40 :
7814 i == Xfake_acid_6 ? 50 :
7815 i == Xfake_acid_7 ? 60 :
7816 i == Xfake_acid_8 ? 70 :
7818 i == Xball_2B ? j + 8 :
7819 i == Yball_eat ? j + 1 :
7820 i == Ykey_1_eat ? j + 1 :
7821 i == Ykey_2_eat ? j + 1 :
7822 i == Ykey_3_eat ? j + 1 :
7823 i == Ykey_4_eat ? j + 1 :
7824 i == Ykey_5_eat ? j + 1 :
7825 i == Ykey_6_eat ? j + 1 :
7826 i == Ykey_7_eat ? j + 1 :
7827 i == Ykey_8_eat ? j + 1 :
7828 i == Ylenses_eat ? j + 1 :
7829 i == Ymagnify_eat ? j + 1 :
7830 i == Ygrass_eat ? j + 1 :
7831 i == Ydirt_eat ? j + 1 :
7832 i == Xamoeba_1 ? 0 :
7833 i == Xamoeba_2 ? 1 :
7834 i == Xamoeba_3 ? 2 :
7835 i == Xamoeba_4 ? 3 :
7836 i == Xamoeba_5 ? 0 :
7837 i == Xamoeba_6 ? 1 :
7838 i == Xamoeba_7 ? 2 :
7839 i == Xamoeba_8 ? 3 :
7840 i == Xexit_2 ? j + 8 :
7841 i == Xexit_3 ? j + 16 :
7842 i == Xdynamite_1 ? 0 :
7843 i == Xdynamite_2 ? 8 :
7844 i == Xdynamite_3 ? 16 :
7845 i == Xdynamite_4 ? 24 :
7846 i == Xsand_stonein_1 ? j + 1 :
7847 i == Xsand_stonein_2 ? j + 9 :
7848 i == Xsand_stonein_3 ? j + 17 :
7849 i == Xsand_stonein_4 ? j + 25 :
7850 i == Xsand_stoneout_1 && j == 0 ? 0 :
7851 i == Xsand_stoneout_1 && j == 1 ? 0 :
7852 i == Xsand_stoneout_1 && j == 2 ? 1 :
7853 i == Xsand_stoneout_1 && j == 3 ? 2 :
7854 i == Xsand_stoneout_1 && j == 4 ? 2 :
7855 i == Xsand_stoneout_1 && j == 5 ? 3 :
7856 i == Xsand_stoneout_1 && j == 6 ? 4 :
7857 i == Xsand_stoneout_1 && j == 7 ? 4 :
7858 i == Xsand_stoneout_2 && j == 0 ? 5 :
7859 i == Xsand_stoneout_2 && j == 1 ? 6 :
7860 i == Xsand_stoneout_2 && j == 2 ? 7 :
7861 i == Xsand_stoneout_2 && j == 3 ? 8 :
7862 i == Xsand_stoneout_2 && j == 4 ? 9 :
7863 i == Xsand_stoneout_2 && j == 5 ? 11 :
7864 i == Xsand_stoneout_2 && j == 6 ? 13 :
7865 i == Xsand_stoneout_2 && j == 7 ? 15 :
7866 i == Xboom_bug && j == 1 ? 2 :
7867 i == Xboom_bug && j == 2 ? 2 :
7868 i == Xboom_bug && j == 3 ? 4 :
7869 i == Xboom_bug && j == 4 ? 4 :
7870 i == Xboom_bug && j == 5 ? 2 :
7871 i == Xboom_bug && j == 6 ? 2 :
7872 i == Xboom_bug && j == 7 ? 0 :
7873 i == Xboom_bomb && j == 1 ? 2 :
7874 i == Xboom_bomb && j == 2 ? 2 :
7875 i == Xboom_bomb && j == 3 ? 4 :
7876 i == Xboom_bomb && j == 4 ? 4 :
7877 i == Xboom_bomb && j == 5 ? 2 :
7878 i == Xboom_bomb && j == 6 ? 2 :
7879 i == Xboom_bomb && j == 7 ? 0 :
7880 i == Xboom_android && j == 7 ? 6 :
7881 i == Xboom_1 && j == 1 ? 2 :
7882 i == Xboom_1 && j == 2 ? 2 :
7883 i == Xboom_1 && j == 3 ? 4 :
7884 i == Xboom_1 && j == 4 ? 4 :
7885 i == Xboom_1 && j == 5 ? 6 :
7886 i == Xboom_1 && j == 6 ? 6 :
7887 i == Xboom_1 && j == 7 ? 8 :
7888 i == Xboom_2 && j == 0 ? 8 :
7889 i == Xboom_2 && j == 1 ? 8 :
7890 i == Xboom_2 && j == 2 ? 10 :
7891 i == Xboom_2 && j == 3 ? 10 :
7892 i == Xboom_2 && j == 4 ? 10 :
7893 i == Xboom_2 && j == 5 ? 12 :
7894 i == Xboom_2 && j == 6 ? 12 :
7895 i == Xboom_2 && j == 7 ? 12 :
7896 special_animation && j == 4 ? 3 :
7897 effective_action != action ? 0 :
7901 Bitmap *debug_bitmap = g_em->bitmap;
7902 int debug_src_x = g_em->src_x;
7903 int debug_src_y = g_em->src_y;
7906 int frame = getAnimationFrame(g->anim_frames,
7909 g->anim_start_frame,
7912 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7913 g->double_movement && is_backside);
7915 g_em->bitmap = src_bitmap;
7916 g_em->src_x = src_x;
7917 g_em->src_y = src_y;
7918 g_em->src_offset_x = 0;
7919 g_em->src_offset_y = 0;
7920 g_em->dst_offset_x = 0;
7921 g_em->dst_offset_y = 0;
7922 g_em->width = TILEX;
7923 g_em->height = TILEY;
7925 g_em->preserve_background = FALSE;
7927 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7930 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7931 effective_action == ACTION_MOVING ||
7932 effective_action == ACTION_PUSHING ||
7933 effective_action == ACTION_EATING)) ||
7934 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7935 effective_action == ACTION_EMPTYING)))
7938 (effective_action == ACTION_FALLING ||
7939 effective_action == ACTION_FILLING ||
7940 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7941 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7942 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7943 int num_steps = (i == Ydrip_s1 ? 16 :
7944 i == Ydrip_s1B ? 16 :
7945 i == Ydrip_s2 ? 16 :
7946 i == Ydrip_s2B ? 16 :
7947 i == Xsand_stonein_1 ? 32 :
7948 i == Xsand_stonein_2 ? 32 :
7949 i == Xsand_stonein_3 ? 32 :
7950 i == Xsand_stonein_4 ? 32 :
7951 i == Xsand_stoneout_1 ? 16 :
7952 i == Xsand_stoneout_2 ? 16 : 8);
7953 int cx = ABS(dx) * (TILEX / num_steps);
7954 int cy = ABS(dy) * (TILEY / num_steps);
7955 int step_frame = (i == Ydrip_s2 ? j + 8 :
7956 i == Ydrip_s2B ? j + 8 :
7957 i == Xsand_stonein_2 ? j + 8 :
7958 i == Xsand_stonein_3 ? j + 16 :
7959 i == Xsand_stonein_4 ? j + 24 :
7960 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7961 int step = (is_backside ? step_frame : num_steps - step_frame);
7963 if (is_backside) /* tile where movement starts */
7965 if (dx < 0 || dy < 0)
7967 g_em->src_offset_x = cx * step;
7968 g_em->src_offset_y = cy * step;
7972 g_em->dst_offset_x = cx * step;
7973 g_em->dst_offset_y = cy * step;
7976 else /* tile where movement ends */
7978 if (dx < 0 || dy < 0)
7980 g_em->dst_offset_x = cx * step;
7981 g_em->dst_offset_y = cy * step;
7985 g_em->src_offset_x = cx * step;
7986 g_em->src_offset_y = cy * step;
7990 g_em->width = TILEX - cx * step;
7991 g_em->height = TILEY - cy * step;
7994 /* create unique graphic identifier to decide if tile must be redrawn */
7995 /* bit 31 - 16 (16 bit): EM style graphic
7996 bit 15 - 12 ( 4 bit): EM style frame
7997 bit 11 - 6 ( 6 bit): graphic width
7998 bit 5 - 0 ( 6 bit): graphic height */
7999 g_em->unique_identifier =
8000 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8004 /* skip check for EMC elements not contained in original EMC artwork */
8005 if (element == EL_EMC_FAKE_ACID)
8008 if (g_em->bitmap != debug_bitmap ||
8009 g_em->src_x != debug_src_x ||
8010 g_em->src_y != debug_src_y ||
8011 g_em->src_offset_x != 0 ||
8012 g_em->src_offset_y != 0 ||
8013 g_em->dst_offset_x != 0 ||
8014 g_em->dst_offset_y != 0 ||
8015 g_em->width != TILEX ||
8016 g_em->height != TILEY)
8018 static int last_i = -1;
8026 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8027 i, element, element_info[element].token_name,
8028 element_action_info[effective_action].suffix, direction);
8030 if (element != effective_element)
8031 printf(" [%d ('%s')]",
8033 element_info[effective_element].token_name);
8037 if (g_em->bitmap != debug_bitmap)
8038 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8039 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8041 if (g_em->src_x != debug_src_x ||
8042 g_em->src_y != debug_src_y)
8043 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8044 j, (is_backside ? 'B' : 'F'),
8045 g_em->src_x, g_em->src_y,
8046 g_em->src_x / 32, g_em->src_y / 32,
8047 debug_src_x, debug_src_y,
8048 debug_src_x / 32, debug_src_y / 32);
8050 if (g_em->src_offset_x != 0 ||
8051 g_em->src_offset_y != 0 ||
8052 g_em->dst_offset_x != 0 ||
8053 g_em->dst_offset_y != 0)
8054 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8056 g_em->src_offset_x, g_em->src_offset_y,
8057 g_em->dst_offset_x, g_em->dst_offset_y);
8059 if (g_em->width != TILEX ||
8060 g_em->height != TILEY)
8061 printf(" %d (%d): size %d,%d should be %d,%d\n",
8063 g_em->width, g_em->height, TILEX, TILEY);
8065 num_em_gfx_errors++;
8072 for (i = 0; i < TILE_MAX; i++)
8074 for (j = 0; j < 8; j++)
8076 int element = object_mapping[i].element_rnd;
8077 int action = object_mapping[i].action;
8078 int direction = object_mapping[i].direction;
8079 boolean is_backside = object_mapping[i].is_backside;
8080 int graphic_action = el_act_dir2img(element, action, direction);
8081 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8083 if ((action == ACTION_SMASHED_BY_ROCK ||
8084 action == ACTION_SMASHED_BY_SPRING ||
8085 action == ACTION_EATING) &&
8086 graphic_action == graphic_default)
8088 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8089 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8090 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8091 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8094 /* no separate animation for "smashed by rock" -- use rock instead */
8095 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8096 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8098 g_em->bitmap = g_xx->bitmap;
8099 g_em->src_x = g_xx->src_x;
8100 g_em->src_y = g_xx->src_y;
8101 g_em->src_offset_x = g_xx->src_offset_x;
8102 g_em->src_offset_y = g_xx->src_offset_y;
8103 g_em->dst_offset_x = g_xx->dst_offset_x;
8104 g_em->dst_offset_y = g_xx->dst_offset_y;
8105 g_em->width = g_xx->width;
8106 g_em->height = g_xx->height;
8107 g_em->unique_identifier = g_xx->unique_identifier;
8110 g_em->preserve_background = TRUE;
8115 for (p = 0; p < MAX_PLAYERS; p++)
8117 for (i = 0; i < SPR_MAX; i++)
8119 int element = player_mapping[p][i].element_rnd;
8120 int action = player_mapping[p][i].action;
8121 int direction = player_mapping[p][i].direction;
8123 for (j = 0; j < 8; j++)
8125 int effective_element = element;
8126 int effective_action = action;
8127 int graphic = (direction == MV_NONE ?
8128 el_act2img(effective_element, effective_action) :
8129 el_act_dir2img(effective_element, effective_action,
8131 struct GraphicInfo *g = &graphic_info[graphic];
8132 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8138 Bitmap *debug_bitmap = g_em->bitmap;
8139 int debug_src_x = g_em->src_x;
8140 int debug_src_y = g_em->src_y;
8143 int frame = getAnimationFrame(g->anim_frames,
8146 g->anim_start_frame,
8149 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8151 g_em->bitmap = src_bitmap;
8152 g_em->src_x = src_x;
8153 g_em->src_y = src_y;
8154 g_em->src_offset_x = 0;
8155 g_em->src_offset_y = 0;
8156 g_em->dst_offset_x = 0;
8157 g_em->dst_offset_y = 0;
8158 g_em->width = TILEX;
8159 g_em->height = TILEY;
8163 /* skip check for EMC elements not contained in original EMC artwork */
8164 if (element == EL_PLAYER_3 ||
8165 element == EL_PLAYER_4)
8168 if (g_em->bitmap != debug_bitmap ||
8169 g_em->src_x != debug_src_x ||
8170 g_em->src_y != debug_src_y)
8172 static int last_i = -1;
8180 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
8181 p, i, element, element_info[element].token_name,
8182 element_action_info[effective_action].suffix, direction);
8184 if (element != effective_element)
8185 printf(" [%d ('%s')]",
8187 element_info[effective_element].token_name);
8191 if (g_em->bitmap != debug_bitmap)
8192 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
8193 j, (int)(g_em->bitmap), (int)(debug_bitmap));
8195 if (g_em->src_x != debug_src_x ||
8196 g_em->src_y != debug_src_y)
8197 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8199 g_em->src_x, g_em->src_y,
8200 g_em->src_x / 32, g_em->src_y / 32,
8201 debug_src_x, debug_src_y,
8202 debug_src_x / 32, debug_src_y / 32);
8204 num_em_gfx_errors++;
8214 printf("::: [%d errors found]\n", num_em_gfx_errors);
8220 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
8221 boolean any_player_moving,
8222 boolean any_player_snapping,
8223 boolean any_player_dropping)
8225 if (frame == 0 && !any_player_dropping)
8227 if (!local_player->was_waiting)
8229 if (!CheckSaveEngineSnapshotToList())
8232 local_player->was_waiting = TRUE;
8235 else if (any_player_moving || any_player_snapping || any_player_dropping)
8237 local_player->was_waiting = FALSE;
8241 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8242 boolean murphy_is_dropping)
8244 if (murphy_is_waiting)
8246 if (!local_player->was_waiting)
8248 if (!CheckSaveEngineSnapshotToList())
8251 local_player->was_waiting = TRUE;
8256 local_player->was_waiting = FALSE;
8260 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8261 boolean any_player_moving,
8262 boolean any_player_snapping,
8263 boolean any_player_dropping)
8265 if (tape.single_step && tape.recording && !tape.pausing)
8266 if (frame == 0 && !any_player_dropping)
8267 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8269 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8270 any_player_snapping, any_player_dropping);
8273 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8274 boolean murphy_is_dropping)
8276 if (tape.single_step && tape.recording && !tape.pausing)
8277 if (murphy_is_waiting)
8278 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8280 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8283 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8284 int graphic, int sync_frame, int x, int y)
8286 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8288 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8291 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8293 return (IS_NEXT_FRAME(sync_frame, graphic));
8296 int getGraphicInfo_Delay(int graphic)
8298 return graphic_info[graphic].anim_delay;
8301 void PlayMenuSoundExt(int sound)
8303 if (sound == SND_UNDEFINED)
8306 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8307 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8310 if (IS_LOOP_SOUND(sound))
8311 PlaySoundLoop(sound);
8316 void PlayMenuSound()
8318 PlayMenuSoundExt(menu.sound[game_status]);
8321 void PlayMenuSoundStereo(int sound, int stereo_position)
8323 if (sound == SND_UNDEFINED)
8326 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8327 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8330 if (IS_LOOP_SOUND(sound))
8331 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8333 PlaySoundStereo(sound, stereo_position);
8336 void PlayMenuSoundIfLoopExt(int sound)
8338 if (sound == SND_UNDEFINED)
8341 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8342 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8345 if (IS_LOOP_SOUND(sound))
8346 PlaySoundLoop(sound);
8349 void PlayMenuSoundIfLoop()
8351 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8354 void PlayMenuMusicExt(int music)
8356 if (music == MUS_UNDEFINED)
8359 if (!setup.sound_music)
8365 void PlayMenuMusic()
8367 PlayMenuMusicExt(menu.music[game_status]);
8370 void PlaySoundActivating()
8373 PlaySound(SND_MENU_ITEM_ACTIVATING);
8377 void PlaySoundSelecting()
8380 PlaySound(SND_MENU_ITEM_SELECTING);
8384 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8386 boolean change_fullscreen = (setup.fullscreen !=
8387 video.fullscreen_enabled);
8388 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8389 setup.window_scaling_percent !=
8390 video.window_scaling_percent);
8392 if (change_window_scaling_percent && video.fullscreen_enabled)
8395 if (!change_window_scaling_percent && !video.fullscreen_available)
8398 #if defined(TARGET_SDL2)
8399 if (change_window_scaling_percent)
8401 SDLSetWindowScaling(setup.window_scaling_percent);
8405 else if (change_fullscreen)
8407 SDLSetWindowFullscreen(setup.fullscreen);
8409 /* set setup value according to successfully changed fullscreen mode */
8410 setup.fullscreen = video.fullscreen_enabled;
8416 if (change_fullscreen ||
8417 change_window_scaling_percent)
8419 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8421 /* save backbuffer content which gets lost when toggling fullscreen mode */
8422 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8424 if (change_window_scaling_percent)
8426 /* keep window mode, but change window scaling */
8427 video.fullscreen_enabled = TRUE; /* force new window scaling */
8430 /* toggle fullscreen */
8431 ChangeVideoModeIfNeeded(setup.fullscreen);
8433 /* set setup value according to successfully changed fullscreen mode */
8434 setup.fullscreen = video.fullscreen_enabled;
8436 /* restore backbuffer content from temporary backbuffer backup bitmap */
8437 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8439 FreeBitmap(tmp_backbuffer);
8441 /* update visible window/screen */
8442 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8446 void JoinRectangles(int *x, int *y, int *width, int *height,
8447 int x2, int y2, int width2, int height2)
8449 // do not join with "off-screen" rectangle
8450 if (x2 == -1 || y2 == -1)
8455 *width = MAX(*width, width2);
8456 *height = MAX(*height, height2);
8459 void SetAnimStatus(int anim_status_new)
8461 if (anim_status_new == GAME_MODE_MAIN)
8462 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
8464 global.anim_status_next = anim_status_new;
8466 // directly set screen modes that are entered without fading
8467 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
8468 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
8469 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
8470 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
8471 global.anim_status = global.anim_status_next;
8474 void SetGameStatus(int game_status_new)
8476 game_status = game_status_new;
8478 SetAnimStatus(game_status_new);
8481 void SetFontStatus(int game_status_new)
8483 static int last_game_status = -1;
8485 if (game_status_new != -1)
8487 // set game status for font use after storing last game status
8488 last_game_status = game_status;
8489 game_status = game_status_new;
8493 // reset game status after font use from last stored game status
8494 game_status = last_game_status;
8498 void ResetFontStatus()
8503 void ChangeViewportPropertiesIfNeeded()
8505 int gfx_game_mode = game_status;
8506 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8508 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
8509 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8510 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8511 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8512 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8513 int new_win_xsize = vp_window->width;
8514 int new_win_ysize = vp_window->height;
8515 int border_size = vp_playfield->border_size;
8516 int new_sx = vp_playfield->x + border_size;
8517 int new_sy = vp_playfield->y + border_size;
8518 int new_sxsize = vp_playfield->width - 2 * border_size;
8519 int new_sysize = vp_playfield->height - 2 * border_size;
8520 int new_real_sx = vp_playfield->x;
8521 int new_real_sy = vp_playfield->y;
8522 int new_full_sxsize = vp_playfield->width;
8523 int new_full_sysize = vp_playfield->height;
8524 int new_dx = vp_door_1->x;
8525 int new_dy = vp_door_1->y;
8526 int new_dxsize = vp_door_1->width;
8527 int new_dysize = vp_door_1->height;
8528 int new_vx = vp_door_2->x;
8529 int new_vy = vp_door_2->y;
8530 int new_vxsize = vp_door_2->width;
8531 int new_vysize = vp_door_2->height;
8532 int new_ex = vp_door_3->x;
8533 int new_ey = vp_door_3->y;
8534 int new_exsize = vp_door_3->width;
8535 int new_eysize = vp_door_3->height;
8536 int new_tilesize_var =
8537 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8539 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8540 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8541 int new_scr_fieldx = new_sxsize / tilesize;
8542 int new_scr_fieldy = new_sysize / tilesize;
8543 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8544 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8545 boolean init_gfx_buffers = FALSE;
8546 boolean init_video_buffer = FALSE;
8547 boolean init_gadgets_and_anims = FALSE;
8548 boolean init_em_graphics = FALSE;
8550 if (new_win_xsize != WIN_XSIZE ||
8551 new_win_ysize != WIN_YSIZE)
8553 WIN_XSIZE = new_win_xsize;
8554 WIN_YSIZE = new_win_ysize;
8556 init_video_buffer = TRUE;
8557 init_gfx_buffers = TRUE;
8558 init_gadgets_and_anims = TRUE;
8560 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8563 if (new_scr_fieldx != SCR_FIELDX ||
8564 new_scr_fieldy != SCR_FIELDY)
8566 /* this always toggles between MAIN and GAME when using small tile size */
8568 SCR_FIELDX = new_scr_fieldx;
8569 SCR_FIELDY = new_scr_fieldy;
8571 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8582 new_sxsize != SXSIZE ||
8583 new_sysize != SYSIZE ||
8584 new_dxsize != DXSIZE ||
8585 new_dysize != DYSIZE ||
8586 new_vxsize != VXSIZE ||
8587 new_vysize != VYSIZE ||
8588 new_exsize != EXSIZE ||
8589 new_eysize != EYSIZE ||
8590 new_real_sx != REAL_SX ||
8591 new_real_sy != REAL_SY ||
8592 new_full_sxsize != FULL_SXSIZE ||
8593 new_full_sysize != FULL_SYSIZE ||
8594 new_tilesize_var != TILESIZE_VAR
8597 // ------------------------------------------------------------------------
8598 // determine next fading area for changed viewport definitions
8599 // ------------------------------------------------------------------------
8601 // start with current playfield area (default fading area)
8604 FADE_SXSIZE = FULL_SXSIZE;
8605 FADE_SYSIZE = FULL_SYSIZE;
8607 // add new playfield area if position or size has changed
8608 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8609 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8611 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8612 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8615 // add current and new door 1 area if position or size has changed
8616 if (new_dx != DX || new_dy != DY ||
8617 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8619 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8620 DX, DY, DXSIZE, DYSIZE);
8621 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8622 new_dx, new_dy, new_dxsize, new_dysize);
8625 // add current and new door 2 area if position or size has changed
8626 if (new_dx != VX || new_dy != VY ||
8627 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8629 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8630 VX, VY, VXSIZE, VYSIZE);
8631 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8632 new_vx, new_vy, new_vxsize, new_vysize);
8635 // ------------------------------------------------------------------------
8636 // handle changed tile size
8637 // ------------------------------------------------------------------------
8639 if (new_tilesize_var != TILESIZE_VAR)
8641 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8643 // changing tile size invalidates scroll values of engine snapshots
8644 FreeEngineSnapshotSingle();
8646 // changing tile size requires update of graphic mapping for EM engine
8647 init_em_graphics = TRUE;
8658 SXSIZE = new_sxsize;
8659 SYSIZE = new_sysize;
8660 DXSIZE = new_dxsize;
8661 DYSIZE = new_dysize;
8662 VXSIZE = new_vxsize;
8663 VYSIZE = new_vysize;
8664 EXSIZE = new_exsize;
8665 EYSIZE = new_eysize;
8666 REAL_SX = new_real_sx;
8667 REAL_SY = new_real_sy;
8668 FULL_SXSIZE = new_full_sxsize;
8669 FULL_SYSIZE = new_full_sysize;
8670 TILESIZE_VAR = new_tilesize_var;
8672 init_gfx_buffers = TRUE;
8673 init_gadgets_and_anims = TRUE;
8675 // printf("::: viewports: init_gfx_buffers\n");
8676 // printf("::: viewports: init_gadgets_and_anims\n");
8679 if (init_gfx_buffers)
8681 // printf("::: init_gfx_buffers\n");
8683 SCR_FIELDX = new_scr_fieldx_buffers;
8684 SCR_FIELDY = new_scr_fieldy_buffers;
8688 SCR_FIELDX = new_scr_fieldx;
8689 SCR_FIELDY = new_scr_fieldy;
8691 SetDrawDeactivationMask(REDRAW_NONE);
8692 SetDrawBackgroundMask(REDRAW_FIELD);
8695 if (init_video_buffer)
8697 // printf("::: init_video_buffer\n");
8699 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8700 InitImageTextures();
8703 if (init_gadgets_and_anims)
8705 // printf("::: init_gadgets_and_anims\n");
8708 InitGlobalAnimations();
8711 if (init_em_graphics)
8713 InitGraphicInfo_EM();