1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 #define DEBUG_FRAME_TIME FALSE
28 // tool button identifiers
29 #define TOOL_CTRL_ID_YES 0
30 #define TOOL_CTRL_ID_NO 1
31 #define TOOL_CTRL_ID_CONFIRM 2
32 #define TOOL_CTRL_ID_PLAYER_1 3
33 #define TOOL_CTRL_ID_PLAYER_2 4
34 #define TOOL_CTRL_ID_PLAYER_3 5
35 #define TOOL_CTRL_ID_PLAYER_4 6
36 #define TOOL_CTRL_ID_TOUCH_YES 7
37 #define TOOL_CTRL_ID_TOUCH_NO 8
38 #define TOOL_CTRL_ID_TOUCH_CONFIRM 9
40 #define NUM_TOOL_BUTTONS 10
42 // constants for number of doors and door parts
44 #define NUM_PANELS NUM_DOORS
45 // #define NUM_PANELS 0
46 #define MAX_PARTS_PER_DOOR 8
47 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
48 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
51 struct DoorPartOrderInfo
57 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
59 struct DoorPartControlInfo
63 struct DoorPartPosInfo *pos;
66 static struct DoorPartControlInfo door_part_controls[] =
70 IMG_GFX_DOOR_1_PART_1,
75 IMG_GFX_DOOR_1_PART_2,
80 IMG_GFX_DOOR_1_PART_3,
85 IMG_GFX_DOOR_1_PART_4,
90 IMG_GFX_DOOR_1_PART_5,
95 IMG_GFX_DOOR_1_PART_6,
100 IMG_GFX_DOOR_1_PART_7,
105 IMG_GFX_DOOR_1_PART_8,
111 IMG_GFX_DOOR_2_PART_1,
116 IMG_GFX_DOOR_2_PART_2,
121 IMG_GFX_DOOR_2_PART_3,
126 IMG_GFX_DOOR_2_PART_4,
131 IMG_GFX_DOOR_2_PART_5,
136 IMG_GFX_DOOR_2_PART_6,
141 IMG_GFX_DOOR_2_PART_7,
146 IMG_GFX_DOOR_2_PART_8,
152 IMG_BACKGROUND_PANEL,
169 // forward declaration for internal use
170 static void UnmapToolButtons(void);
171 static void HandleToolButtons(struct GadgetInfo *);
172 static int el_act_dir2crm(int, int, int);
173 static int el_act2crm(int, int);
175 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
176 static int request_gadget_id = -1;
178 static char *print_if_not_empty(int element)
180 static char *s = NULL;
181 char *token_name = element_info[element].token_name;
186 s = checked_malloc(strlen(token_name) + 10 + 1);
188 if (element != EL_EMPTY)
189 sprintf(s, "%d\t['%s']", element, token_name);
191 sprintf(s, "%d", element);
196 int getFieldbufferOffsetX_RND(int dir, int pos)
198 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
199 int dx = (dir & MV_HORIZONTAL ? pos : 0);
200 int dx_var = dx * TILESIZE_VAR / TILESIZE;
203 if (EVEN(SCR_FIELDX))
205 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
206 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
208 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
209 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
211 fx += (dx_var > 0 ? TILEX_VAR : 0);
218 if (full_lev_fieldx <= SCR_FIELDX)
220 if (EVEN(SCR_FIELDX))
221 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
223 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
229 int getFieldbufferOffsetY_RND(int dir, int pos)
231 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
232 int dy = (dir & MV_VERTICAL ? pos : 0);
233 int dy_var = dy * TILESIZE_VAR / TILESIZE;
236 if (EVEN(SCR_FIELDY))
238 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
239 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
241 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
242 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
244 fy += (dy_var > 0 ? TILEY_VAR : 0);
251 if (full_lev_fieldy <= SCR_FIELDY)
253 if (EVEN(SCR_FIELDY))
254 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
256 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
262 static int getLevelFromScreenX_RND(int sx)
264 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
267 int lx = LEVELX((px + dx) / TILESIZE_VAR);
272 static int getLevelFromScreenY_RND(int sy)
274 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
277 int ly = LEVELY((py + dy) / TILESIZE_VAR);
282 static int getLevelFromScreenX_EM(int sx)
284 int level_xsize = level.native_em_level->cav->width;
285 int full_xsize = level_xsize * TILESIZE_VAR;
287 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
289 int fx = getFieldbufferOffsetX_EM();
292 int lx = LEVELX((px + dx) / TILESIZE_VAR);
297 static int getLevelFromScreenY_EM(int sy)
299 int level_ysize = level.native_em_level->cav->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);
312 static int getLevelFromScreenX_SP(int sx)
314 int menBorder = setup.sp_show_border_elements;
315 int level_xsize = level.native_sp_level->width;
316 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
318 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
320 int fx = getFieldbufferOffsetX_SP();
323 int lx = LEVELX((px + dx) / TILESIZE_VAR);
328 static int getLevelFromScreenY_SP(int sy)
330 int menBorder = setup.sp_show_border_elements;
331 int level_ysize = level.native_sp_level->height;
332 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
334 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
336 int fy = getFieldbufferOffsetY_SP();
339 int ly = LEVELY((py + dy) / TILESIZE_VAR);
344 static int getLevelFromScreenX_MM(int sx)
346 int level_xsize = level.native_mm_level->fieldx;
347 int full_xsize = level_xsize * TILESIZE_VAR;
349 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
352 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
357 static int getLevelFromScreenY_MM(int sy)
359 int level_ysize = level.native_mm_level->fieldy;
360 int full_ysize = level_ysize * TILESIZE_VAR;
362 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
365 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
370 int getLevelFromScreenX(int x)
372 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
373 return getLevelFromScreenX_EM(x);
374 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
375 return getLevelFromScreenX_SP(x);
376 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
377 return getLevelFromScreenX_MM(x);
379 return getLevelFromScreenX_RND(x);
382 int getLevelFromScreenY(int y)
384 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
385 return getLevelFromScreenY_EM(y);
386 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
387 return getLevelFromScreenY_SP(y);
388 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
389 return getLevelFromScreenY_MM(y);
391 return getLevelFromScreenY_RND(y);
394 void DumpTile(int x, int y)
401 Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
404 if (!IN_LEV_FIELD(x, y))
406 Info("(not in level field)");
412 token_name = element_info[Tile[x][y]].token_name;
414 Info("Tile: %d\t['%s']", Tile[x][y], token_name);
415 Info("Back: %s", print_if_not_empty(Back[x][y]));
416 Info("Store: %s", print_if_not_empty(Store[x][y]));
417 Info("Store2: %s", print_if_not_empty(Store2[x][y]));
418 Info("StorePlayer: %s", print_if_not_empty(StorePlayer[x][y]));
419 Info("MovPos: %d", MovPos[x][y]);
420 Info("MovDir: %d", MovDir[x][y]);
421 Info("MovDelay: %d", MovDelay[x][y]);
422 Info("ChangeDelay: %d", ChangeDelay[x][y]);
423 Info("CustomValue: %d", CustomValue[x][y]);
424 Info("GfxElement: %d", GfxElement[x][y]);
425 Info("GfxAction: %d", GfxAction[x][y]);
426 Info("GfxFrame: %d [%d]", GfxFrame[x][y], FrameCounter);
427 Info("Player x/y: %d, %d", local_player->jx, local_player->jy);
431 void DumpTileFromScreen(int sx, int sy)
433 int lx = getLevelFromScreenX(sx);
434 int ly = getLevelFromScreenY(sy);
439 void SetDrawtoField(int mode)
441 if (mode == DRAW_TO_FIELDBUFFER)
447 BX2 = SCR_FIELDX + 1;
448 BY2 = SCR_FIELDY + 1;
450 drawto_field = fieldbuffer;
452 else // DRAW_TO_BACKBUFFER
458 BX2 = SCR_FIELDX - 1;
459 BY2 = SCR_FIELDY - 1;
461 drawto_field = backbuffer;
465 static void RedrawPlayfield_RND(void)
467 if (game.envelope_active)
470 DrawLevel(REDRAW_ALL);
474 void RedrawPlayfield(void)
476 if (game_status != GAME_MODE_PLAYING)
479 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
480 RedrawPlayfield_EM(TRUE);
481 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
482 RedrawPlayfield_SP(TRUE);
483 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
484 RedrawPlayfield_MM();
485 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
486 RedrawPlayfield_RND();
488 BlitScreenToBitmap(backbuffer);
490 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
494 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
497 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
498 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
500 if (x == -1 && y == -1)
503 if (draw_target == DRAW_TO_SCREEN)
504 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
506 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
509 static void DrawMaskedBorderExt_FIELD(int draw_target)
511 if (global.border_status >= GAME_MODE_MAIN &&
512 global.border_status <= GAME_MODE_PLAYING &&
513 border.draw_masked[global.border_status])
514 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
518 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
520 // when drawing to backbuffer, never draw border over open doors
521 if (draw_target == DRAW_TO_BACKBUFFER &&
522 (GetDoorState() & DOOR_OPEN_1))
525 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
526 (global.border_status != GAME_MODE_EDITOR ||
527 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
528 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
531 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
533 // when drawing to backbuffer, never draw border over open doors
534 if (draw_target == DRAW_TO_BACKBUFFER &&
535 (GetDoorState() & DOOR_OPEN_2))
538 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
539 global.border_status != GAME_MODE_EDITOR)
540 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
543 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
545 // currently not available
548 static void DrawMaskedBorderExt_ALL(int draw_target)
550 DrawMaskedBorderExt_FIELD(draw_target);
551 DrawMaskedBorderExt_DOOR_1(draw_target);
552 DrawMaskedBorderExt_DOOR_2(draw_target);
553 DrawMaskedBorderExt_DOOR_3(draw_target);
556 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
558 // never draw masked screen borders on borderless screens
559 if (global.border_status == GAME_MODE_LOADING ||
560 global.border_status == GAME_MODE_TITLE)
563 if (redraw_mask & REDRAW_ALL)
564 DrawMaskedBorderExt_ALL(draw_target);
567 if (redraw_mask & REDRAW_FIELD)
568 DrawMaskedBorderExt_FIELD(draw_target);
569 if (redraw_mask & REDRAW_DOOR_1)
570 DrawMaskedBorderExt_DOOR_1(draw_target);
571 if (redraw_mask & REDRAW_DOOR_2)
572 DrawMaskedBorderExt_DOOR_2(draw_target);
573 if (redraw_mask & REDRAW_DOOR_3)
574 DrawMaskedBorderExt_DOOR_3(draw_target);
578 void DrawMaskedBorder_FIELD(void)
580 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
583 void DrawMaskedBorder(int redraw_mask)
585 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
588 void DrawMaskedBorderToTarget(int draw_target)
590 if (draw_target == DRAW_TO_BACKBUFFER ||
591 draw_target == DRAW_TO_SCREEN)
593 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
597 int last_border_status = global.border_status;
599 if (draw_target == DRAW_TO_FADE_SOURCE)
601 global.border_status = gfx.fade_border_source_status;
602 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
604 else if (draw_target == DRAW_TO_FADE_TARGET)
606 global.border_status = gfx.fade_border_target_status;
607 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
610 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
612 global.border_status = last_border_status;
613 gfx.masked_border_bitmap_ptr = backbuffer;
617 void DrawTileCursor(int draw_target)
619 DrawTileCursor_MM(draw_target, game_status == GAME_MODE_PLAYING);
622 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
624 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
627 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
629 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
630 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
632 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
635 void BlitScreenToBitmap(Bitmap *target_bitmap)
637 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
638 BlitScreenToBitmap_EM(target_bitmap);
639 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
640 BlitScreenToBitmap_SP(target_bitmap);
641 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
642 BlitScreenToBitmap_MM(target_bitmap);
643 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
644 BlitScreenToBitmap_RND(target_bitmap);
646 redraw_mask |= REDRAW_FIELD;
649 static void DrawFramesPerSecond(void)
652 int font_nr = FONT_TEXT_2;
653 int font_width = getFontWidth(font_nr);
654 int draw_deactivation_mask = GetDrawDeactivationMask();
655 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
657 // draw FPS with leading space (needed if field buffer deactivated)
658 sprintf(text, " %04.1f fps", global.frames_per_second);
660 // override draw deactivation mask (required for invisible warp mode)
661 SetDrawDeactivationMask(REDRAW_NONE);
663 // draw opaque FPS if field buffer deactivated, else draw masked FPS
664 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
665 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
667 // set draw deactivation mask to previous value
668 SetDrawDeactivationMask(draw_deactivation_mask);
670 // force full-screen redraw in this frame
671 redraw_mask = REDRAW_ALL;
675 static void PrintFrameTimeDebugging(void)
677 static unsigned int last_counter = 0;
678 unsigned int counter = Counter();
679 int diff_1 = counter - last_counter;
680 int diff_2 = diff_1 - GAME_FRAME_DELAY;
682 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
683 char diff_bar[2 * diff_2_max + 5];
687 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
689 for (i = 0; i < diff_2_max; i++)
690 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
691 i >= diff_2_max - diff_2_cut ? '-' : ' ');
693 diff_bar[pos++] = '|';
695 for (i = 0; i < diff_2_max; i++)
696 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
698 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
700 diff_bar[pos++] = '\0';
702 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
705 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
708 last_counter = counter;
712 static int unifiedRedrawMask(int mask)
714 if (mask & REDRAW_ALL)
717 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
723 static boolean equalRedrawMasks(int mask_1, int mask_2)
725 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
728 void BackToFront(void)
730 static int last_redraw_mask = REDRAW_NONE;
732 // force screen redraw in every frame to continue drawing global animations
733 // (but always use the last redraw mask to prevent unwanted side effects)
734 if (redraw_mask == REDRAW_NONE)
735 redraw_mask = last_redraw_mask;
737 last_redraw_mask = redraw_mask;
740 // masked border now drawn immediately when blitting backbuffer to window
742 // draw masked border to all viewports, if defined
743 DrawMaskedBorder(redraw_mask);
746 // draw frames per second (only if debug mode is enabled)
747 if (redraw_mask & REDRAW_FPS)
748 DrawFramesPerSecond();
750 // remove playfield redraw before potentially merging with doors redraw
751 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
752 redraw_mask &= ~REDRAW_FIELD;
754 // redraw complete window if both playfield and (some) doors need redraw
755 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
756 redraw_mask = REDRAW_ALL;
758 /* although redrawing the whole window would be fine for normal gameplay,
759 being able to only redraw the playfield is required for deactivating
760 certain drawing areas (mainly playfield) to work, which is needed for
761 warp-forward to be fast enough (by skipping redraw of most frames) */
763 if (redraw_mask & REDRAW_ALL)
765 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
767 else if (redraw_mask & REDRAW_FIELD)
769 BlitBitmap(backbuffer, window,
770 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
772 else if (redraw_mask & REDRAW_DOORS)
774 // merge door areas to prevent calling screen redraw more than once
780 if (redraw_mask & REDRAW_DOOR_1)
784 x2 = MAX(x2, DX + DXSIZE);
785 y2 = MAX(y2, DY + DYSIZE);
788 if (redraw_mask & REDRAW_DOOR_2)
792 x2 = MAX(x2, VX + VXSIZE);
793 y2 = MAX(y2, VY + VYSIZE);
796 if (redraw_mask & REDRAW_DOOR_3)
800 x2 = MAX(x2, EX + EXSIZE);
801 y2 = MAX(y2, EY + EYSIZE);
804 // make sure that at least one pixel is blitted, and inside the screen
805 // (else nothing is blitted, causing the animations not to be updated)
806 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
807 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
808 x2 = MIN(MAX(1, x2), WIN_XSIZE);
809 y2 = MIN(MAX(1, y2), WIN_YSIZE);
811 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
814 redraw_mask = REDRAW_NONE;
817 PrintFrameTimeDebugging();
821 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
823 unsigned int frame_delay_value_old = GetVideoFrameDelay();
825 SetVideoFrameDelay(frame_delay_value);
829 SetVideoFrameDelay(frame_delay_value_old);
832 static int fade_type_skip = FADE_TYPE_NONE;
834 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
836 void (*draw_border_function)(void) = NULL;
837 int x, y, width, height;
838 int fade_delay, post_delay;
840 if (fade_type == FADE_TYPE_FADE_OUT)
842 if (fade_type_skip != FADE_TYPE_NONE)
844 // skip all fade operations until specified fade operation
845 if (fade_type & fade_type_skip)
846 fade_type_skip = FADE_TYPE_NONE;
851 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
855 redraw_mask |= fade_mask;
857 if (fade_type == FADE_TYPE_SKIP)
859 fade_type_skip = fade_mode;
864 fade_delay = fading.fade_delay;
865 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
867 if (fade_type_skip != FADE_TYPE_NONE)
869 // skip all fade operations until specified fade operation
870 if (fade_type & fade_type_skip)
871 fade_type_skip = FADE_TYPE_NONE;
876 if (global.autoplay_leveldir)
881 if (fade_mask == REDRAW_FIELD)
886 height = FADE_SYSIZE;
888 if (border.draw_masked_when_fading)
889 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
891 DrawMaskedBorder_FIELD(); // draw once
901 // when switching screens without fading, set fade delay to zero
902 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
905 // do not display black frame when fading out without fade delay
906 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
909 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
910 draw_border_function);
912 redraw_mask &= ~fade_mask;
914 ClearAutoRepeatKeyEvents();
917 static void SetScreenStates_BeforeFadingIn(void)
919 // temporarily set screen mode for animations to screen after fading in
920 global.anim_status = global.anim_status_next;
922 // store backbuffer with all animations that will be started after fading in
923 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
925 // set screen mode for animations back to fading
926 global.anim_status = GAME_MODE_PSEUDO_FADING;
929 static void SetScreenStates_AfterFadingIn(void)
931 // store new source screen (to use correct masked border for fading)
932 gfx.fade_border_source_status = global.border_status;
934 global.anim_status = global.anim_status_next;
937 static void SetScreenStates_BeforeFadingOut(void)
939 // store new target screen (to use correct masked border for fading)
940 gfx.fade_border_target_status = game_status;
942 // set screen mode for animations to fading
943 global.anim_status = GAME_MODE_PSEUDO_FADING;
945 // store backbuffer with all animations that will be stopped for fading out
946 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
949 static void SetScreenStates_AfterFadingOut(void)
951 global.border_status = game_status;
954 void FadeIn(int fade_mask)
956 SetScreenStates_BeforeFadingIn();
959 DrawMaskedBorder(REDRAW_ALL);
962 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
963 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
965 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
969 FADE_SXSIZE = FULL_SXSIZE;
970 FADE_SYSIZE = FULL_SYSIZE;
972 // activate virtual buttons depending on upcoming game status
973 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
974 game_status == GAME_MODE_PLAYING && !tape.playing)
975 SetOverlayActive(TRUE);
977 SetScreenStates_AfterFadingIn();
979 // force update of global animation status in case of rapid screen changes
980 redraw_mask = REDRAW_ALL;
984 void FadeOut(int fade_mask)
986 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
987 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
988 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
991 SetScreenStates_BeforeFadingOut();
993 SetTileCursorActive(FALSE);
994 SetOverlayActive(FALSE);
997 DrawMaskedBorder(REDRAW_ALL);
1000 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1001 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1003 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1005 SetScreenStates_AfterFadingOut();
1008 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1010 static struct TitleFadingInfo fading_leave_stored;
1013 fading_leave_stored = fading_leave;
1015 fading = fading_leave_stored;
1018 void FadeSetEnterMenu(void)
1020 fading = menu.enter_menu;
1022 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1025 void FadeSetLeaveMenu(void)
1027 fading = menu.leave_menu;
1029 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1032 void FadeSetEnterScreen(void)
1034 fading = menu.enter_screen[game_status];
1036 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1039 void FadeSetNextScreen(void)
1041 fading = menu.next_screen[game_status];
1043 // (do not overwrite fade mode set by FadeSetEnterScreen)
1044 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1047 void FadeSetLeaveScreen(void)
1049 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1052 void FadeSetFromType(int type)
1054 if (type & TYPE_ENTER_SCREEN)
1055 FadeSetEnterScreen();
1056 else if (type & TYPE_ENTER)
1058 else if (type & TYPE_LEAVE)
1062 void FadeSetDisabled(void)
1064 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1066 fading = fading_none;
1069 void FadeSkipNextFadeIn(void)
1071 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1074 void FadeSkipNextFadeOut(void)
1076 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1079 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1081 if (graphic == IMG_UNDEFINED)
1084 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1086 return (graphic_info[graphic].bitmap != NULL || redefined ?
1087 graphic_info[graphic].bitmap :
1088 graphic_info[default_graphic].bitmap);
1091 static Bitmap *getBackgroundBitmap(int graphic)
1093 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1096 static Bitmap *getGlobalBorderBitmap(int graphic)
1098 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1101 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1104 (status == GAME_MODE_MAIN ||
1105 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1106 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1107 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1108 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1111 return getGlobalBorderBitmap(graphic);
1114 void SetWindowBackgroundImageIfDefined(int graphic)
1116 if (graphic_info[graphic].bitmap)
1117 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1120 void SetMainBackgroundImageIfDefined(int graphic)
1122 if (graphic_info[graphic].bitmap)
1123 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1126 void SetDoorBackgroundImageIfDefined(int graphic)
1128 if (graphic_info[graphic].bitmap)
1129 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1132 void SetWindowBackgroundImage(int graphic)
1134 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1137 void SetMainBackgroundImage(int graphic)
1139 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1142 void SetDoorBackgroundImage(int graphic)
1144 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1147 void SetPanelBackground(void)
1149 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1151 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1152 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1154 SetDoorBackgroundBitmap(bitmap_db_panel);
1157 void DrawBackground(int x, int y, int width, int height)
1159 // "drawto" might still point to playfield buffer here (hall of fame)
1160 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1162 if (IN_GFX_FIELD_FULL(x, y))
1163 redraw_mask |= REDRAW_FIELD;
1164 else if (IN_GFX_DOOR_1(x, y))
1165 redraw_mask |= REDRAW_DOOR_1;
1166 else if (IN_GFX_DOOR_2(x, y))
1167 redraw_mask |= REDRAW_DOOR_2;
1168 else if (IN_GFX_DOOR_3(x, y))
1169 redraw_mask |= REDRAW_DOOR_3;
1172 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1174 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1176 if (font->bitmap == NULL)
1179 DrawBackground(x, y, width, height);
1182 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1184 struct GraphicInfo *g = &graphic_info[graphic];
1186 if (g->bitmap == NULL)
1189 DrawBackground(x, y, width, height);
1192 static int game_status_last = -1;
1193 static Bitmap *global_border_bitmap_last = NULL;
1194 static Bitmap *global_border_bitmap = NULL;
1195 static int real_sx_last = -1, real_sy_last = -1;
1196 static int full_sxsize_last = -1, full_sysize_last = -1;
1197 static int dx_last = -1, dy_last = -1;
1198 static int dxsize_last = -1, dysize_last = -1;
1199 static int vx_last = -1, vy_last = -1;
1200 static int vxsize_last = -1, vysize_last = -1;
1201 static int ex_last = -1, ey_last = -1;
1202 static int exsize_last = -1, eysize_last = -1;
1204 boolean CheckIfGlobalBorderHasChanged(void)
1206 // if game status has not changed, global border has not changed either
1207 if (game_status == game_status_last)
1210 // determine and store new global border bitmap for current game status
1211 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1213 return (global_border_bitmap_last != global_border_bitmap);
1216 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1218 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1219 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1221 // if game status has not changed, nothing has to be redrawn
1222 if (game_status == game_status_last)
1225 // redraw if last screen was title screen
1226 if (game_status_last == GAME_MODE_TITLE)
1229 // redraw if global screen border has changed
1230 if (CheckIfGlobalBorderHasChanged())
1233 // redraw if position or size of playfield area has changed
1234 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1235 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1238 // redraw if position or size of door area has changed
1239 if (dx_last != DX || dy_last != DY ||
1240 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1243 // redraw if position or size of tape area has changed
1244 if (vx_last != VX || vy_last != VY ||
1245 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1248 // redraw if position or size of editor area has changed
1249 if (ex_last != EX || ey_last != EY ||
1250 exsize_last != EXSIZE || eysize_last != EYSIZE)
1257 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1260 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1262 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1265 void RedrawGlobalBorder(void)
1267 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1269 RedrawGlobalBorderFromBitmap(bitmap);
1271 redraw_mask = REDRAW_ALL;
1274 static void RedrawGlobalBorderIfNeeded(void)
1276 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1277 if (game_status == game_status_last)
1281 // copy current draw buffer to later copy back areas that have not changed
1282 if (game_status_last != GAME_MODE_TITLE)
1283 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1285 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1286 if (CheckIfGlobalBorderRedrawIsNeeded())
1288 // determine and store new global border bitmap for current game status
1289 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1292 // redraw global screen border (or clear, if defined to be empty)
1293 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1295 if (game_status == GAME_MODE_EDITOR)
1296 DrawSpecialEditorDoor();
1298 // copy previous playfield and door areas, if they are defined on both
1299 // previous and current screen and if they still have the same size
1301 if (real_sx_last != -1 && real_sy_last != -1 &&
1302 REAL_SX != -1 && REAL_SY != -1 &&
1303 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1304 BlitBitmap(bitmap_db_store_1, backbuffer,
1305 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1308 if (dx_last != -1 && dy_last != -1 &&
1309 DX != -1 && DY != -1 &&
1310 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1311 BlitBitmap(bitmap_db_store_1, backbuffer,
1312 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1314 if (game_status != GAME_MODE_EDITOR)
1316 if (vx_last != -1 && vy_last != -1 &&
1317 VX != -1 && VY != -1 &&
1318 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1319 BlitBitmap(bitmap_db_store_1, backbuffer,
1320 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1324 if (ex_last != -1 && ey_last != -1 &&
1325 EX != -1 && EY != -1 &&
1326 exsize_last == EXSIZE && eysize_last == EYSIZE)
1327 BlitBitmap(bitmap_db_store_1, backbuffer,
1328 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1331 redraw_mask = REDRAW_ALL;
1334 game_status_last = game_status;
1336 global_border_bitmap_last = global_border_bitmap;
1338 real_sx_last = REAL_SX;
1339 real_sy_last = REAL_SY;
1340 full_sxsize_last = FULL_SXSIZE;
1341 full_sysize_last = FULL_SYSIZE;
1344 dxsize_last = DXSIZE;
1345 dysize_last = DYSIZE;
1348 vxsize_last = VXSIZE;
1349 vysize_last = VYSIZE;
1352 exsize_last = EXSIZE;
1353 eysize_last = EYSIZE;
1356 void ClearField(void)
1358 RedrawGlobalBorderIfNeeded();
1360 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1361 // (when entering hall of fame after playing)
1362 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1364 // !!! maybe this should be done before clearing the background !!!
1365 if (game_status == GAME_MODE_PLAYING)
1367 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1368 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1372 SetDrawtoField(DRAW_TO_BACKBUFFER);
1376 void MarkTileDirty(int x, int y)
1378 redraw_mask |= REDRAW_FIELD;
1381 void SetBorderElement(void)
1385 BorderElement = EL_EMPTY;
1387 // only the R'n'D game engine may use an additional steelwall border
1388 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1391 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1393 for (x = 0; x < lev_fieldx; x++)
1395 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1396 BorderElement = EL_STEELWALL;
1398 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1404 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1405 int max_array_fieldx, int max_array_fieldy,
1406 short field[max_array_fieldx][max_array_fieldy],
1407 int max_fieldx, int max_fieldy)
1411 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1412 static int safety = 0;
1414 // check if starting field still has the desired content
1415 if (field[from_x][from_y] == fill_element)
1420 if (safety > max_fieldx * max_fieldy)
1421 Fail("Something went wrong in 'FloodFill()'. Please debug.");
1423 old_element = field[from_x][from_y];
1424 field[from_x][from_y] = fill_element;
1426 for (i = 0; i < 4; i++)
1428 x = from_x + check[i][0];
1429 y = from_y + check[i][1];
1431 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1432 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1433 field, max_fieldx, max_fieldy);
1439 void FloodFillLevel(int from_x, int from_y, int fill_element,
1440 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1441 int max_fieldx, int max_fieldy)
1443 FloodFillLevelExt(from_x, from_y, fill_element,
1444 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1445 max_fieldx, max_fieldy);
1448 void SetRandomAnimationValue(int x, int y)
1450 gfx.anim_random_frame = GfxRandom[x][y];
1453 int getGraphicAnimationFrame(int graphic, int sync_frame)
1455 // animation synchronized with global frame counter, not move position
1456 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1457 sync_frame = FrameCounter;
1459 return getAnimationFrame(graphic_info[graphic].anim_frames,
1460 graphic_info[graphic].anim_delay,
1461 graphic_info[graphic].anim_mode,
1462 graphic_info[graphic].anim_start_frame,
1466 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1468 struct GraphicInfo *g = &graphic_info[graphic];
1469 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1471 if (tilesize == gfx.standard_tile_size)
1472 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1473 else if (tilesize == game.tile_size)
1474 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1476 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1479 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1480 boolean get_backside)
1482 struct GraphicInfo *g = &graphic_info[graphic];
1483 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1484 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1486 if (g->offset_y == 0) // frames are ordered horizontally
1488 int max_width = g->anim_frames_per_line * g->width;
1489 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1491 *x = pos % max_width;
1492 *y = src_y % g->height + pos / max_width * g->height;
1494 else if (g->offset_x == 0) // frames are ordered vertically
1496 int max_height = g->anim_frames_per_line * g->height;
1497 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1499 *x = src_x % g->width + pos / max_height * g->width;
1500 *y = pos % max_height;
1502 else // frames are ordered diagonally
1504 *x = src_x + frame * g->offset_x;
1505 *y = src_y + frame * g->offset_y;
1509 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1510 Bitmap **bitmap, int *x, int *y,
1511 boolean get_backside)
1513 struct GraphicInfo *g = &graphic_info[graphic];
1515 // if no graphics defined at all, use fallback graphics
1516 if (g->bitmaps == NULL)
1517 *g = graphic_info[IMG_CHAR_EXCLAM];
1519 // if no in-game graphics defined, always use standard graphic size
1520 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1521 tilesize = TILESIZE;
1523 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1524 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1526 *x = *x * tilesize / g->tile_size;
1527 *y = *y * tilesize / g->tile_size;
1530 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1531 Bitmap **bitmap, int *x, int *y)
1533 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1536 void getFixedGraphicSource(int graphic, int frame,
1537 Bitmap **bitmap, int *x, int *y)
1539 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1542 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1544 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1547 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1548 int *x, int *y, boolean get_backside)
1550 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1554 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1556 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1559 void DrawGraphic(int x, int y, int graphic, int frame)
1562 if (!IN_SCR_FIELD(x, y))
1564 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1565 Debug("draw:DrawGraphic", "This should never happen!");
1571 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1574 MarkTileDirty(x, y);
1577 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1580 if (!IN_SCR_FIELD(x, y))
1582 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1584 Debug("draw:DrawFixedGraphic", "This should never happen!");
1590 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1592 MarkTileDirty(x, y);
1595 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1601 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1603 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1606 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1612 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1613 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1616 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1619 if (!IN_SCR_FIELD(x, y))
1621 Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1623 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1629 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1632 MarkTileDirty(x, y);
1635 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1638 if (!IN_SCR_FIELD(x, y))
1640 Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1642 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1648 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1650 MarkTileDirty(x, y);
1653 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1659 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1661 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1665 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1666 int graphic, int frame)
1671 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1673 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1677 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1679 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1681 MarkTileDirty(x / tilesize, y / tilesize);
1684 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1687 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1688 graphic, frame, tilesize);
1689 MarkTileDirty(x / tilesize, y / tilesize);
1692 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1698 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1699 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1702 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1703 int frame, int tilesize)
1708 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1709 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1712 void DrawMiniGraphic(int x, int y, int graphic)
1714 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1715 MarkTileDirty(x / 2, y / 2);
1718 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1723 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1724 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1727 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1728 int graphic, int frame,
1729 int cut_mode, int mask_mode)
1734 int width = TILEX, height = TILEY;
1737 if (dx || dy) // shifted graphic
1739 if (x < BX1) // object enters playfield from the left
1746 else if (x > BX2) // object enters playfield from the right
1752 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1758 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1760 else if (dx) // general horizontal movement
1761 MarkTileDirty(x + SIGN(dx), y);
1763 if (y < BY1) // object enters playfield from the top
1765 if (cut_mode == CUT_BELOW) // object completely above top border
1773 else if (y > BY2) // object enters playfield from the bottom
1779 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1785 else if (dy > 0 && cut_mode == CUT_ABOVE)
1787 if (y == BY2) // object completely above bottom border
1793 MarkTileDirty(x, y + 1);
1794 } // object leaves playfield to the bottom
1795 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1797 else if (dy) // general vertical movement
1798 MarkTileDirty(x, y + SIGN(dy));
1802 if (!IN_SCR_FIELD(x, y))
1804 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1806 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1812 width = width * TILESIZE_VAR / TILESIZE;
1813 height = height * TILESIZE_VAR / TILESIZE;
1814 cx = cx * TILESIZE_VAR / TILESIZE;
1815 cy = cy * TILESIZE_VAR / TILESIZE;
1816 dx = dx * TILESIZE_VAR / TILESIZE;
1817 dy = dy * TILESIZE_VAR / TILESIZE;
1819 if (width > 0 && height > 0)
1821 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1826 dst_x = FX + x * TILEX_VAR + dx;
1827 dst_y = FY + y * TILEY_VAR + dy;
1829 if (mask_mode == USE_MASKING)
1830 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1833 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1836 MarkTileDirty(x, y);
1840 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1841 int graphic, int frame,
1842 int cut_mode, int mask_mode)
1847 int width = TILEX_VAR, height = TILEY_VAR;
1850 int x2 = x + SIGN(dx);
1851 int y2 = y + SIGN(dy);
1853 // movement with two-tile animations must be sync'ed with movement position,
1854 // not with current GfxFrame (which can be higher when using slow movement)
1855 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1856 int anim_frames = graphic_info[graphic].anim_frames;
1858 // (we also need anim_delay here for movement animations with less frames)
1859 int anim_delay = graphic_info[graphic].anim_delay;
1860 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1862 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1863 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1865 // re-calculate animation frame for two-tile movement animation
1866 frame = getGraphicAnimationFrame(graphic, sync_frame);
1868 // check if movement start graphic inside screen area and should be drawn
1869 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1871 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1873 dst_x = FX + x1 * TILEX_VAR;
1874 dst_y = FY + y1 * TILEY_VAR;
1876 if (mask_mode == USE_MASKING)
1877 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1880 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1883 MarkTileDirty(x1, y1);
1886 // check if movement end graphic inside screen area and should be drawn
1887 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1889 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1891 dst_x = FX + x2 * TILEX_VAR;
1892 dst_y = FY + y2 * TILEY_VAR;
1894 if (mask_mode == USE_MASKING)
1895 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1898 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1901 MarkTileDirty(x2, y2);
1905 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1906 int graphic, int frame,
1907 int cut_mode, int mask_mode)
1911 DrawGraphic(x, y, graphic, frame);
1916 if (graphic_info[graphic].double_movement) // EM style movement images
1917 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1919 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1922 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1923 int graphic, int frame, int cut_mode)
1925 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1928 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1929 int cut_mode, int mask_mode)
1931 int lx = LEVELX(x), ly = LEVELY(y);
1935 if (IN_LEV_FIELD(lx, ly))
1937 SetRandomAnimationValue(lx, ly);
1939 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1940 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1942 // do not use double (EM style) movement graphic when not moving
1943 if (graphic_info[graphic].double_movement && !dx && !dy)
1945 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1946 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1949 else // border element
1951 graphic = el2img(element);
1952 frame = getGraphicAnimationFrame(graphic, -1);
1955 if (element == EL_EXPANDABLE_WALL)
1957 boolean left_stopped = FALSE, right_stopped = FALSE;
1959 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
1960 left_stopped = TRUE;
1961 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
1962 right_stopped = TRUE;
1964 if (left_stopped && right_stopped)
1966 else if (left_stopped)
1968 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1969 frame = graphic_info[graphic].anim_frames - 1;
1971 else if (right_stopped)
1973 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1974 frame = graphic_info[graphic].anim_frames - 1;
1979 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1980 else if (mask_mode == USE_MASKING)
1981 DrawGraphicThruMask(x, y, graphic, frame);
1983 DrawGraphic(x, y, graphic, frame);
1986 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1987 int cut_mode, int mask_mode)
1989 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1990 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1991 cut_mode, mask_mode);
1994 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1997 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2000 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2003 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2006 void DrawLevelElementThruMask(int x, int y, int element)
2008 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2011 void DrawLevelFieldThruMask(int x, int y)
2013 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2016 // !!! implementation of quicksand is totally broken !!!
2017 #define IS_CRUMBLED_TILE(x, y, e) \
2018 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2019 !IS_MOVING(x, y) || \
2020 (e) == EL_QUICKSAND_EMPTYING || \
2021 (e) == EL_QUICKSAND_FAST_EMPTYING))
2023 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2028 int width, height, cx, cy;
2029 int sx = SCREENX(x), sy = SCREENY(y);
2030 int crumbled_border_size = graphic_info[graphic].border_size;
2031 int crumbled_tile_size = graphic_info[graphic].tile_size;
2032 int crumbled_border_size_var =
2033 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2036 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2038 for (i = 1; i < 4; i++)
2040 int dxx = (i & 1 ? dx : 0);
2041 int dyy = (i & 2 ? dy : 0);
2044 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2047 // check if neighbour field is of same crumble type
2048 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2049 graphic_info[graphic].class ==
2050 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2052 // return if check prevents inner corner
2053 if (same == (dxx == dx && dyy == dy))
2057 // if we reach this point, we have an inner corner
2059 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2061 width = crumbled_border_size_var;
2062 height = crumbled_border_size_var;
2063 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2064 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2066 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2067 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2070 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2075 int width, height, bx, by, cx, cy;
2076 int sx = SCREENX(x), sy = SCREENY(y);
2077 int crumbled_border_size = graphic_info[graphic].border_size;
2078 int crumbled_tile_size = graphic_info[graphic].tile_size;
2079 int crumbled_border_size_var =
2080 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2081 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2084 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2086 // draw simple, sloppy, non-corner-accurate crumbled border
2088 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2089 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2090 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2091 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2093 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2094 FX + sx * TILEX_VAR + cx,
2095 FY + sy * TILEY_VAR + cy);
2097 // (remaining middle border part must be at least as big as corner part)
2098 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2099 crumbled_border_size_var >= TILESIZE_VAR / 3)
2102 // correct corners of crumbled border, if needed
2104 for (i = -1; i <= 1; i += 2)
2106 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2107 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2108 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2111 // check if neighbour field is of same crumble type
2112 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2113 graphic_info[graphic].class ==
2114 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2116 // no crumbled corner, but continued crumbled border
2118 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2119 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2120 int b1 = (i == 1 ? crumbled_border_size_var :
2121 TILESIZE_VAR - 2 * crumbled_border_size_var);
2123 width = crumbled_border_size_var;
2124 height = crumbled_border_size_var;
2126 if (dir == 1 || dir == 2)
2141 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2143 FX + sx * TILEX_VAR + cx,
2144 FY + sy * TILEY_VAR + cy);
2149 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2151 int sx = SCREENX(x), sy = SCREENY(y);
2154 static int xy[4][2] =
2162 if (!IN_LEV_FIELD(x, y))
2165 element = TILE_GFX_ELEMENT(x, y);
2167 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2169 if (!IN_SCR_FIELD(sx, sy))
2172 // crumble field borders towards direct neighbour fields
2173 for (i = 0; i < 4; i++)
2175 int xx = x + xy[i][0];
2176 int yy = y + xy[i][1];
2178 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2181 // check if neighbour field is of same crumble type
2182 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2183 graphic_info[graphic].class ==
2184 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2187 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2190 // crumble inner field corners towards corner neighbour fields
2191 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2192 graphic_info[graphic].anim_frames == 2)
2194 for (i = 0; i < 4; i++)
2196 int dx = (i & 1 ? +1 : -1);
2197 int dy = (i & 2 ? +1 : -1);
2199 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2203 MarkTileDirty(sx, sy);
2205 else // center field is not crumbled -- crumble neighbour fields
2207 // crumble field borders of direct neighbour fields
2208 for (i = 0; i < 4; i++)
2210 int xx = x + xy[i][0];
2211 int yy = y + xy[i][1];
2212 int sxx = sx + xy[i][0];
2213 int syy = sy + xy[i][1];
2215 if (!IN_LEV_FIELD(xx, yy) ||
2216 !IN_SCR_FIELD(sxx, syy))
2219 // do not crumble fields that are being digged or snapped
2220 if (Tile[xx][yy] == EL_EMPTY ||
2221 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2224 element = TILE_GFX_ELEMENT(xx, yy);
2226 if (!IS_CRUMBLED_TILE(xx, yy, element))
2229 graphic = el_act2crm(element, ACTION_DEFAULT);
2231 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2233 MarkTileDirty(sxx, syy);
2236 // crumble inner field corners of corner neighbour fields
2237 for (i = 0; i < 4; i++)
2239 int dx = (i & 1 ? +1 : -1);
2240 int dy = (i & 2 ? +1 : -1);
2246 if (!IN_LEV_FIELD(xx, yy) ||
2247 !IN_SCR_FIELD(sxx, syy))
2250 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2253 element = TILE_GFX_ELEMENT(xx, yy);
2255 if (!IS_CRUMBLED_TILE(xx, yy, element))
2258 graphic = el_act2crm(element, ACTION_DEFAULT);
2260 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2261 graphic_info[graphic].anim_frames == 2)
2262 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2264 MarkTileDirty(sxx, syy);
2269 void DrawLevelFieldCrumbled(int x, int y)
2273 if (!IN_LEV_FIELD(x, y))
2276 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2277 GfxElement[x][y] != EL_UNDEFINED &&
2278 GFX_CRUMBLED(GfxElement[x][y]))
2280 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2285 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2287 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2290 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2293 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2294 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2295 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2296 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2297 int sx = SCREENX(x), sy = SCREENY(y);
2299 DrawGraphic(sx, sy, graphic1, frame1);
2300 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2303 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2305 int sx = SCREENX(x), sy = SCREENY(y);
2306 static int xy[4][2] =
2315 // crumble direct neighbour fields (required for field borders)
2316 for (i = 0; i < 4; i++)
2318 int xx = x + xy[i][0];
2319 int yy = y + xy[i][1];
2320 int sxx = sx + xy[i][0];
2321 int syy = sy + xy[i][1];
2323 if (!IN_LEV_FIELD(xx, yy) ||
2324 !IN_SCR_FIELD(sxx, syy) ||
2325 !GFX_CRUMBLED(Tile[xx][yy]) ||
2329 DrawLevelField(xx, yy);
2332 // crumble corner neighbour fields (required for inner field corners)
2333 for (i = 0; i < 4; i++)
2335 int dx = (i & 1 ? +1 : -1);
2336 int dy = (i & 2 ? +1 : -1);
2342 if (!IN_LEV_FIELD(xx, yy) ||
2343 !IN_SCR_FIELD(sxx, syy) ||
2344 !GFX_CRUMBLED(Tile[xx][yy]) ||
2348 int element = TILE_GFX_ELEMENT(xx, yy);
2349 int graphic = el_act2crm(element, ACTION_DEFAULT);
2351 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2352 graphic_info[graphic].anim_frames == 2)
2353 DrawLevelField(xx, yy);
2357 static int getBorderElement(int x, int y)
2361 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2362 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2363 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2364 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2365 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2366 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2367 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2369 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2370 int steel_position = (x == -1 && y == -1 ? 0 :
2371 x == lev_fieldx && y == -1 ? 1 :
2372 x == -1 && y == lev_fieldy ? 2 :
2373 x == lev_fieldx && y == lev_fieldy ? 3 :
2374 x == -1 || x == lev_fieldx ? 4 :
2375 y == -1 || y == lev_fieldy ? 5 : 6);
2377 return border[steel_position][steel_type];
2380 void DrawScreenElement(int x, int y, int element)
2382 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2383 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2386 void DrawLevelElement(int x, int y, int element)
2388 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2389 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2392 void DrawScreenField(int x, int y)
2394 int lx = LEVELX(x), ly = LEVELY(y);
2395 int element, content;
2397 if (!IN_LEV_FIELD(lx, ly))
2399 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2402 element = getBorderElement(lx, ly);
2404 DrawScreenElement(x, y, element);
2409 element = Tile[lx][ly];
2410 content = Store[lx][ly];
2412 if (IS_MOVING(lx, ly))
2414 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2415 boolean cut_mode = NO_CUTTING;
2417 if (element == EL_QUICKSAND_EMPTYING ||
2418 element == EL_QUICKSAND_FAST_EMPTYING ||
2419 element == EL_MAGIC_WALL_EMPTYING ||
2420 element == EL_BD_MAGIC_WALL_EMPTYING ||
2421 element == EL_DC_MAGIC_WALL_EMPTYING ||
2422 element == EL_AMOEBA_DROPPING)
2423 cut_mode = CUT_ABOVE;
2424 else if (element == EL_QUICKSAND_FILLING ||
2425 element == EL_QUICKSAND_FAST_FILLING ||
2426 element == EL_MAGIC_WALL_FILLING ||
2427 element == EL_BD_MAGIC_WALL_FILLING ||
2428 element == EL_DC_MAGIC_WALL_FILLING)
2429 cut_mode = CUT_BELOW;
2431 if (cut_mode == CUT_ABOVE)
2432 DrawScreenElement(x, y, element);
2434 DrawScreenElement(x, y, EL_EMPTY);
2437 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2438 else if (cut_mode == NO_CUTTING)
2439 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2442 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2444 if (cut_mode == CUT_BELOW &&
2445 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2446 DrawLevelElement(lx, ly + 1, element);
2449 if (content == EL_ACID)
2451 int dir = MovDir[lx][ly];
2452 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2453 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2455 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2457 // prevent target field from being drawn again (but without masking)
2458 // (this would happen if target field is scanned after moving element)
2459 Stop[newlx][newly] = TRUE;
2462 else if (IS_BLOCKED(lx, ly))
2467 boolean cut_mode = NO_CUTTING;
2468 int element_old, content_old;
2470 Blocked2Moving(lx, ly, &oldx, &oldy);
2473 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2474 MovDir[oldx][oldy] == MV_RIGHT);
2476 element_old = Tile[oldx][oldy];
2477 content_old = Store[oldx][oldy];
2479 if (element_old == EL_QUICKSAND_EMPTYING ||
2480 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2481 element_old == EL_MAGIC_WALL_EMPTYING ||
2482 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2483 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2484 element_old == EL_AMOEBA_DROPPING)
2485 cut_mode = CUT_ABOVE;
2487 DrawScreenElement(x, y, EL_EMPTY);
2490 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2492 else if (cut_mode == NO_CUTTING)
2493 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2496 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2499 else if (IS_DRAWABLE(element))
2500 DrawScreenElement(x, y, element);
2502 DrawScreenElement(x, y, EL_EMPTY);
2505 void DrawLevelField(int x, int y)
2507 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2508 DrawScreenField(SCREENX(x), SCREENY(y));
2509 else if (IS_MOVING(x, y))
2513 Moving2Blocked(x, y, &newx, &newy);
2514 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2515 DrawScreenField(SCREENX(newx), SCREENY(newy));
2517 else if (IS_BLOCKED(x, y))
2521 Blocked2Moving(x, y, &oldx, &oldy);
2522 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2523 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2527 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2528 int (*el2img_function)(int), boolean masked,
2529 int element_bits_draw)
2531 int element_base = map_mm_wall_element(element);
2532 int element_bits = (IS_DF_WALL(element) ?
2533 element - EL_DF_WALL_START :
2534 IS_MM_WALL(element) ?
2535 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2536 int graphic = el2img_function(element_base);
2537 int tilesize_draw = tilesize / 2;
2542 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2544 for (i = 0; i < 4; i++)
2546 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2547 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2549 if (!(element_bits_draw & (1 << i)))
2552 if (element_bits & (1 << i))
2555 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2556 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2558 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2559 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2564 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2565 tilesize_draw, tilesize_draw);
2570 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2571 boolean masked, int element_bits_draw)
2573 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2574 element, tilesize, el2edimg, masked, element_bits_draw);
2577 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2578 int (*el2img_function)(int))
2580 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2584 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2587 if (IS_MM_WALL(element))
2589 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2590 element, tilesize, el2edimg, masked, 0x000f);
2594 int graphic = el2edimg(element);
2597 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2599 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2603 void DrawSizedElement(int x, int y, int element, int tilesize)
2605 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2608 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2610 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2613 void DrawMiniElement(int x, int y, int element)
2617 graphic = el2edimg(element);
2618 DrawMiniGraphic(x, y, graphic);
2621 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2624 int x = sx + scroll_x, y = sy + scroll_y;
2626 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2627 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2628 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2629 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2631 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2634 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2636 int x = sx + scroll_x, y = sy + scroll_y;
2638 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2639 DrawMiniElement(sx, sy, EL_EMPTY);
2640 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2641 DrawMiniElement(sx, sy, Tile[x][y]);
2643 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2646 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2647 int x, int y, int xsize, int ysize,
2648 int tile_width, int tile_height)
2652 int dst_x = startx + x * tile_width;
2653 int dst_y = starty + y * tile_height;
2654 int width = graphic_info[graphic].width;
2655 int height = graphic_info[graphic].height;
2656 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2657 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2658 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2659 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2660 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2661 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2662 boolean draw_masked = graphic_info[graphic].draw_masked;
2664 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2666 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2668 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2672 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2673 inner_sx + (x - 1) * tile_width % inner_width);
2674 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2675 inner_sy + (y - 1) * tile_height % inner_height);
2678 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2681 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2685 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2686 int x, int y, int xsize, int ysize,
2689 int font_width = getFontWidth(font_nr);
2690 int font_height = getFontHeight(font_nr);
2692 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2693 font_width, font_height);
2696 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2698 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2699 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2700 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2701 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2702 boolean no_delay = (tape.warp_forward);
2703 unsigned int anim_delay = 0;
2704 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2705 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2706 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2707 int font_width = getFontWidth(font_nr);
2708 int font_height = getFontHeight(font_nr);
2709 int max_xsize = level.envelope[envelope_nr].xsize;
2710 int max_ysize = level.envelope[envelope_nr].ysize;
2711 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2712 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2713 int xend = max_xsize;
2714 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2715 int xstep = (xstart < xend ? 1 : 0);
2716 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2718 int end = MAX(xend - xstart, yend - ystart);
2721 for (i = start; i <= end; i++)
2723 int last_frame = end; // last frame of this "for" loop
2724 int x = xstart + i * xstep;
2725 int y = ystart + i * ystep;
2726 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2727 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2728 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2729 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2732 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2734 BlitScreenToBitmap(backbuffer);
2736 SetDrawtoField(DRAW_TO_BACKBUFFER);
2738 for (yy = 0; yy < ysize; yy++)
2739 for (xx = 0; xx < xsize; xx++)
2740 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2742 DrawTextBuffer(sx + font_width, sy + font_height,
2743 level.envelope[envelope_nr].text, font_nr, max_xsize,
2744 xsize - 2, ysize - 2, 0, mask_mode,
2745 level.envelope[envelope_nr].autowrap,
2746 level.envelope[envelope_nr].centered, FALSE);
2748 redraw_mask |= REDRAW_FIELD;
2751 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2754 ClearAutoRepeatKeyEvents();
2757 void ShowEnvelope(int envelope_nr)
2759 int element = EL_ENVELOPE_1 + envelope_nr;
2760 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2761 int sound_opening = element_info[element].sound[ACTION_OPENING];
2762 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2763 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2764 boolean no_delay = (tape.warp_forward);
2765 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2766 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2767 int anim_mode = graphic_info[graphic].anim_mode;
2768 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2769 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2770 boolean overlay_enabled = GetOverlayEnabled();
2772 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2774 SetOverlayEnabled(FALSE);
2777 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2779 if (anim_mode == ANIM_DEFAULT)
2780 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2782 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2785 Delay_WithScreenUpdates(wait_delay_value);
2787 WaitForEventToContinue();
2790 SetOverlayEnabled(overlay_enabled);
2792 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2794 if (anim_mode != ANIM_NONE)
2795 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2797 if (anim_mode == ANIM_DEFAULT)
2798 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2800 game.envelope_active = FALSE;
2802 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2804 redraw_mask |= REDRAW_FIELD;
2808 static void setRequestBasePosition(int *x, int *y)
2810 int sx_base, sy_base;
2812 if (request.x != -1)
2813 sx_base = request.x;
2814 else if (request.align == ALIGN_LEFT)
2816 else if (request.align == ALIGN_RIGHT)
2817 sx_base = SX + SXSIZE;
2819 sx_base = SX + SXSIZE / 2;
2821 if (request.y != -1)
2822 sy_base = request.y;
2823 else if (request.valign == VALIGN_TOP)
2825 else if (request.valign == VALIGN_BOTTOM)
2826 sy_base = SY + SYSIZE;
2828 sy_base = SY + SYSIZE / 2;
2834 static void setRequestPositionExt(int *x, int *y, int width, int height,
2835 boolean add_border_size)
2837 int border_size = request.border_size;
2838 int sx_base, sy_base;
2841 setRequestBasePosition(&sx_base, &sy_base);
2843 if (request.align == ALIGN_LEFT)
2845 else if (request.align == ALIGN_RIGHT)
2846 sx = sx_base - width;
2848 sx = sx_base - width / 2;
2850 if (request.valign == VALIGN_TOP)
2852 else if (request.valign == VALIGN_BOTTOM)
2853 sy = sy_base - height;
2855 sy = sy_base - height / 2;
2857 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2858 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2860 if (add_border_size)
2870 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2872 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2875 static void DrawEnvelopeRequest(char *text)
2877 char *text_final = text;
2878 char *text_door_style = NULL;
2879 int graphic = IMG_BACKGROUND_REQUEST;
2880 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2881 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2882 int font_nr = FONT_REQUEST;
2883 int font_width = getFontWidth(font_nr);
2884 int font_height = getFontHeight(font_nr);
2885 int border_size = request.border_size;
2886 int line_spacing = request.line_spacing;
2887 int line_height = font_height + line_spacing;
2888 int max_text_width = request.width - 2 * border_size;
2889 int max_text_height = request.height - 2 * border_size;
2890 int line_length = max_text_width / font_width;
2891 int max_lines = max_text_height / line_height;
2892 int text_width = line_length * font_width;
2893 int width = request.width;
2894 int height = request.height;
2895 int tile_size = MAX(request.step_offset, 1);
2896 int x_steps = width / tile_size;
2897 int y_steps = height / tile_size;
2898 int sx_offset = border_size;
2899 int sy_offset = border_size;
2903 if (request.centered)
2904 sx_offset = (request.width - text_width) / 2;
2906 if (request.wrap_single_words && !request.autowrap)
2908 char *src_text_ptr, *dst_text_ptr;
2910 text_door_style = checked_malloc(2 * strlen(text) + 1);
2912 src_text_ptr = text;
2913 dst_text_ptr = text_door_style;
2915 while (*src_text_ptr)
2917 if (*src_text_ptr == ' ' ||
2918 *src_text_ptr == '?' ||
2919 *src_text_ptr == '!')
2920 *dst_text_ptr++ = '\n';
2922 if (*src_text_ptr != ' ')
2923 *dst_text_ptr++ = *src_text_ptr;
2928 *dst_text_ptr = '\0';
2930 text_final = text_door_style;
2933 setRequestPosition(&sx, &sy, FALSE);
2935 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2937 for (y = 0; y < y_steps; y++)
2938 for (x = 0; x < x_steps; x++)
2939 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2940 x, y, x_steps, y_steps,
2941 tile_size, tile_size);
2943 // force DOOR font inside door area
2944 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2946 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2947 line_length, -1, max_lines, line_spacing, mask_mode,
2948 request.autowrap, request.centered, FALSE);
2952 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2953 RedrawGadget(tool_gadget[i]);
2955 // store readily prepared envelope request for later use when animating
2956 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2958 if (text_door_style)
2959 free(text_door_style);
2962 static void AnimateEnvelopeRequest(int anim_mode, int action)
2964 int graphic = IMG_BACKGROUND_REQUEST;
2965 boolean draw_masked = graphic_info[graphic].draw_masked;
2966 int delay_value_normal = request.step_delay;
2967 int delay_value_fast = delay_value_normal / 2;
2968 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2969 boolean no_delay = (tape.warp_forward);
2970 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2971 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
2972 unsigned int anim_delay = 0;
2974 int tile_size = MAX(request.step_offset, 1);
2975 int max_xsize = request.width / tile_size;
2976 int max_ysize = request.height / tile_size;
2977 int max_xsize_inner = max_xsize - 2;
2978 int max_ysize_inner = max_ysize - 2;
2980 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2981 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2982 int xend = max_xsize_inner;
2983 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2984 int xstep = (xstart < xend ? 1 : 0);
2985 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2987 int end = MAX(xend - xstart, yend - ystart);
2990 if (setup.quick_doors)
2997 for (i = start; i <= end; i++)
2999 int last_frame = end; // last frame of this "for" loop
3000 int x = xstart + i * xstep;
3001 int y = ystart + i * ystep;
3002 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3003 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3004 int xsize_size_left = (xsize - 1) * tile_size;
3005 int ysize_size_top = (ysize - 1) * tile_size;
3006 int max_xsize_pos = (max_xsize - 1) * tile_size;
3007 int max_ysize_pos = (max_ysize - 1) * tile_size;
3008 int width = xsize * tile_size;
3009 int height = ysize * tile_size;
3014 setRequestPosition(&src_x, &src_y, FALSE);
3015 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3017 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3019 for (yy = 0; yy < 2; yy++)
3021 for (xx = 0; xx < 2; xx++)
3023 int src_xx = src_x + xx * max_xsize_pos;
3024 int src_yy = src_y + yy * max_ysize_pos;
3025 int dst_xx = dst_x + xx * xsize_size_left;
3026 int dst_yy = dst_y + yy * ysize_size_top;
3027 int xx_size = (xx ? tile_size : xsize_size_left);
3028 int yy_size = (yy ? tile_size : ysize_size_top);
3031 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3032 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3034 BlitBitmap(bitmap_db_store_2, backbuffer,
3035 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3039 redraw_mask |= REDRAW_FIELD;
3043 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3046 ClearAutoRepeatKeyEvents();
3049 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3051 int graphic = IMG_BACKGROUND_REQUEST;
3052 int sound_opening = SND_REQUEST_OPENING;
3053 int sound_closing = SND_REQUEST_CLOSING;
3054 int anim_mode_1 = request.anim_mode; // (higher priority)
3055 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3056 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3057 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3058 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3060 if (game_status == GAME_MODE_PLAYING)
3061 BlitScreenToBitmap(backbuffer);
3063 SetDrawtoField(DRAW_TO_BACKBUFFER);
3065 // SetDrawBackgroundMask(REDRAW_NONE);
3067 if (action == ACTION_OPENING)
3069 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3071 if (req_state & REQ_ASK)
3073 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3074 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3075 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3076 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3078 else if (req_state & REQ_CONFIRM)
3080 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3081 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3083 else if (req_state & REQ_PLAYER)
3085 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3086 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3087 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3088 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3091 DrawEnvelopeRequest(text);
3094 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3096 if (action == ACTION_OPENING)
3098 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3100 if (anim_mode == ANIM_DEFAULT)
3101 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3103 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3107 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3109 if (anim_mode != ANIM_NONE)
3110 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3112 if (anim_mode == ANIM_DEFAULT)
3113 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3116 game.envelope_active = FALSE;
3118 if (action == ACTION_CLOSING)
3119 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3121 // SetDrawBackgroundMask(last_draw_background_mask);
3123 redraw_mask |= REDRAW_FIELD;
3127 if (action == ACTION_CLOSING &&
3128 game_status == GAME_MODE_PLAYING &&
3129 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3130 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3133 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3135 if (IS_MM_WALL(element))
3137 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3143 int graphic = el2preimg(element);
3145 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3146 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3151 void DrawLevel(int draw_background_mask)
3155 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3156 SetDrawBackgroundMask(draw_background_mask);
3160 for (x = BX1; x <= BX2; x++)
3161 for (y = BY1; y <= BY2; y++)
3162 DrawScreenField(x, y);
3164 redraw_mask |= REDRAW_FIELD;
3167 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3172 for (x = 0; x < size_x; x++)
3173 for (y = 0; y < size_y; y++)
3174 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3176 redraw_mask |= REDRAW_FIELD;
3179 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3183 for (x = 0; x < size_x; x++)
3184 for (y = 0; y < size_y; y++)
3185 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3187 redraw_mask |= REDRAW_FIELD;
3190 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3192 boolean show_level_border = (BorderElement != EL_EMPTY);
3193 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3194 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3195 int tile_size = preview.tile_size;
3196 int preview_width = preview.xsize * tile_size;
3197 int preview_height = preview.ysize * tile_size;
3198 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3199 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3200 int real_preview_width = real_preview_xsize * tile_size;
3201 int real_preview_height = real_preview_ysize * tile_size;
3202 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3203 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3206 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3209 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3211 dst_x += (preview_width - real_preview_width) / 2;
3212 dst_y += (preview_height - real_preview_height) / 2;
3214 for (x = 0; x < real_preview_xsize; x++)
3216 for (y = 0; y < real_preview_ysize; y++)
3218 int lx = from_x + x + (show_level_border ? -1 : 0);
3219 int ly = from_y + y + (show_level_border ? -1 : 0);
3220 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3221 getBorderElement(lx, ly));
3223 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3224 element, tile_size);
3228 redraw_mask |= REDRAW_FIELD;
3231 #define MICROLABEL_EMPTY 0
3232 #define MICROLABEL_LEVEL_NAME 1
3233 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3234 #define MICROLABEL_LEVEL_AUTHOR 3
3235 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3236 #define MICROLABEL_IMPORTED_FROM 5
3237 #define MICROLABEL_IMPORTED_BY_HEAD 6
3238 #define MICROLABEL_IMPORTED_BY 7
3240 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3242 int max_text_width = SXSIZE;
3243 int font_width = getFontWidth(font_nr);
3245 if (pos->align == ALIGN_CENTER)
3246 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3247 else if (pos->align == ALIGN_RIGHT)
3248 max_text_width = pos->x;
3250 max_text_width = SXSIZE - pos->x;
3252 return max_text_width / font_width;
3255 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3257 char label_text[MAX_OUTPUT_LINESIZE + 1];
3258 int max_len_label_text;
3259 int font_nr = pos->font;
3262 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3265 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3266 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3267 mode == MICROLABEL_IMPORTED_BY_HEAD)
3268 font_nr = pos->font_alt;
3270 max_len_label_text = getMaxTextLength(pos, font_nr);
3272 if (pos->size != -1)
3273 max_len_label_text = pos->size;
3275 for (i = 0; i < max_len_label_text; i++)
3276 label_text[i] = ' ';
3277 label_text[max_len_label_text] = '\0';
3279 if (strlen(label_text) > 0)
3280 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3283 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3284 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3285 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3286 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3287 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3288 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3289 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3290 max_len_label_text);
3291 label_text[max_len_label_text] = '\0';
3293 if (strlen(label_text) > 0)
3294 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3296 redraw_mask |= REDRAW_FIELD;
3299 static void DrawPreviewLevelLabel(int mode)
3301 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3304 static void DrawPreviewLevelInfo(int mode)
3306 if (mode == MICROLABEL_LEVEL_NAME)
3307 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3308 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3309 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3312 static void DrawPreviewLevelExt(boolean restart)
3314 static unsigned int scroll_delay = 0;
3315 static unsigned int label_delay = 0;
3316 static int from_x, from_y, scroll_direction;
3317 static int label_state, label_counter;
3318 unsigned int scroll_delay_value = preview.step_delay;
3319 boolean show_level_border = (BorderElement != EL_EMPTY);
3320 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3321 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3328 if (preview.anim_mode == ANIM_CENTERED)
3330 if (level_xsize > preview.xsize)
3331 from_x = (level_xsize - preview.xsize) / 2;
3332 if (level_ysize > preview.ysize)
3333 from_y = (level_ysize - preview.ysize) / 2;
3336 from_x += preview.xoffset;
3337 from_y += preview.yoffset;
3339 scroll_direction = MV_RIGHT;
3343 DrawPreviewLevelPlayfield(from_x, from_y);
3344 DrawPreviewLevelLabel(label_state);
3346 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3347 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3349 // initialize delay counters
3350 DelayReached(&scroll_delay, 0);
3351 DelayReached(&label_delay, 0);
3353 if (leveldir_current->name)
3355 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3356 char label_text[MAX_OUTPUT_LINESIZE + 1];
3357 int font_nr = pos->font;
3358 int max_len_label_text = getMaxTextLength(pos, font_nr);
3360 if (pos->size != -1)
3361 max_len_label_text = pos->size;
3363 strncpy(label_text, leveldir_current->name, max_len_label_text);
3364 label_text[max_len_label_text] = '\0';
3366 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3367 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3373 // scroll preview level, if needed
3374 if (preview.anim_mode != ANIM_NONE &&
3375 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3376 DelayReached(&scroll_delay, scroll_delay_value))
3378 switch (scroll_direction)
3383 from_x -= preview.step_offset;
3384 from_x = (from_x < 0 ? 0 : from_x);
3387 scroll_direction = MV_UP;
3391 if (from_x < level_xsize - preview.xsize)
3393 from_x += preview.step_offset;
3394 from_x = (from_x > level_xsize - preview.xsize ?
3395 level_xsize - preview.xsize : from_x);
3398 scroll_direction = MV_DOWN;
3404 from_y -= preview.step_offset;
3405 from_y = (from_y < 0 ? 0 : from_y);
3408 scroll_direction = MV_RIGHT;
3412 if (from_y < level_ysize - preview.ysize)
3414 from_y += preview.step_offset;
3415 from_y = (from_y > level_ysize - preview.ysize ?
3416 level_ysize - preview.ysize : from_y);
3419 scroll_direction = MV_LEFT;
3426 DrawPreviewLevelPlayfield(from_x, from_y);
3429 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3430 // redraw micro level label, if needed
3431 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3432 !strEqual(level.author, ANONYMOUS_NAME) &&
3433 !strEqual(level.author, leveldir_current->name) &&
3434 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3436 int max_label_counter = 23;
3438 if (leveldir_current->imported_from != NULL &&
3439 strlen(leveldir_current->imported_from) > 0)
3440 max_label_counter += 14;
3441 if (leveldir_current->imported_by != NULL &&
3442 strlen(leveldir_current->imported_by) > 0)
3443 max_label_counter += 14;
3445 label_counter = (label_counter + 1) % max_label_counter;
3446 label_state = (label_counter >= 0 && label_counter <= 7 ?
3447 MICROLABEL_LEVEL_NAME :
3448 label_counter >= 9 && label_counter <= 12 ?
3449 MICROLABEL_LEVEL_AUTHOR_HEAD :
3450 label_counter >= 14 && label_counter <= 21 ?
3451 MICROLABEL_LEVEL_AUTHOR :
3452 label_counter >= 23 && label_counter <= 26 ?
3453 MICROLABEL_IMPORTED_FROM_HEAD :
3454 label_counter >= 28 && label_counter <= 35 ?
3455 MICROLABEL_IMPORTED_FROM :
3456 label_counter >= 37 && label_counter <= 40 ?
3457 MICROLABEL_IMPORTED_BY_HEAD :
3458 label_counter >= 42 && label_counter <= 49 ?
3459 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3461 if (leveldir_current->imported_from == NULL &&
3462 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3463 label_state == MICROLABEL_IMPORTED_FROM))
3464 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3465 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3467 DrawPreviewLevelLabel(label_state);
3471 void DrawPreviewPlayers(void)
3473 if (game_status != GAME_MODE_MAIN)
3476 // do not draw preview players if level preview redefined, but players aren't
3477 if (preview.redefined && !menu.main.preview_players.redefined)
3480 boolean player_found[MAX_PLAYERS];
3481 int num_players = 0;
3484 for (i = 0; i < MAX_PLAYERS; i++)
3485 player_found[i] = FALSE;
3487 // check which players can be found in the level (simple approach)
3488 for (x = 0; x < lev_fieldx; x++)
3490 for (y = 0; y < lev_fieldy; y++)
3492 int element = level.field[x][y];
3494 if (ELEM_IS_PLAYER(element))
3496 int player_nr = GET_PLAYER_NR(element);
3498 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3500 if (!player_found[player_nr])
3503 player_found[player_nr] = TRUE;
3508 struct TextPosInfo *pos = &menu.main.preview_players;
3509 int tile_size = pos->tile_size;
3510 int border_size = pos->border_size;
3511 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3512 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3513 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3514 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3515 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3516 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3517 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3518 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3519 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3520 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3521 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3522 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3524 // clear area in which the players will be drawn
3525 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3526 max_players_width, max_players_height);
3528 if (!network.enabled && !setup.team_mode)
3531 // only draw players if level is suited for team mode
3532 if (num_players < 2)
3535 // draw all players that were found in the level
3536 for (i = 0; i < MAX_PLAYERS; i++)
3538 if (player_found[i])
3540 int graphic = el2img(EL_PLAYER_1 + i);
3542 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3544 xpos += player_xoffset;
3545 ypos += player_yoffset;
3550 void DrawPreviewLevelInitial(void)
3552 DrawPreviewLevelExt(TRUE);
3553 DrawPreviewPlayers();
3556 void DrawPreviewLevelAnimation(void)
3558 DrawPreviewLevelExt(FALSE);
3561 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3562 int border_size, int font_nr)
3564 int graphic = el2img(EL_PLAYER_1 + player_nr);
3565 int font_height = getFontHeight(font_nr);
3566 int player_height = MAX(tile_size, font_height);
3567 int xoffset_text = tile_size + border_size;
3568 int yoffset_text = (player_height - font_height) / 2;
3569 int yoffset_graphic = (player_height - tile_size) / 2;
3570 char *player_name = getNetworkPlayerName(player_nr + 1);
3572 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3574 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3577 static void DrawNetworkPlayersExt(boolean force)
3579 if (game_status != GAME_MODE_MAIN)
3582 if (!network.connected && !force)
3585 // do not draw network players if level preview redefined, but players aren't
3586 if (preview.redefined && !menu.main.network_players.redefined)
3589 int num_players = 0;
3592 for (i = 0; i < MAX_PLAYERS; i++)
3593 if (stored_player[i].connected_network)
3596 struct TextPosInfo *pos = &menu.main.network_players;
3597 int tile_size = pos->tile_size;
3598 int border_size = pos->border_size;
3599 int xoffset_text = tile_size + border_size;
3600 int font_nr = pos->font;
3601 int font_width = getFontWidth(font_nr);
3602 int font_height = getFontHeight(font_nr);
3603 int player_height = MAX(tile_size, font_height);
3604 int player_yoffset = player_height + border_size;
3605 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3606 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3607 int all_players_height = num_players * player_yoffset - border_size;
3608 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3609 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3610 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3612 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3613 max_players_width, max_players_height);
3615 // first draw local network player ...
3616 for (i = 0; i < MAX_PLAYERS; i++)
3618 if (stored_player[i].connected_network &&
3619 stored_player[i].connected_locally)
3621 char *player_name = getNetworkPlayerName(i + 1);
3622 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3623 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3625 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3627 ypos += player_yoffset;
3631 // ... then draw all other network players
3632 for (i = 0; i < MAX_PLAYERS; i++)
3634 if (stored_player[i].connected_network &&
3635 !stored_player[i].connected_locally)
3637 char *player_name = getNetworkPlayerName(i + 1);
3638 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3639 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3641 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3643 ypos += player_yoffset;
3648 void DrawNetworkPlayers(void)
3650 DrawNetworkPlayersExt(FALSE);
3653 void ClearNetworkPlayers(void)
3655 DrawNetworkPlayersExt(TRUE);
3658 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3659 int graphic, int sync_frame,
3662 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3664 if (mask_mode == USE_MASKING)
3665 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3667 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3670 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3671 int graphic, int sync_frame, int mask_mode)
3673 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3675 if (mask_mode == USE_MASKING)
3676 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3678 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3681 static void DrawGraphicAnimation(int x, int y, int graphic)
3683 int lx = LEVELX(x), ly = LEVELY(y);
3685 if (!IN_SCR_FIELD(x, y))
3688 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3689 graphic, GfxFrame[lx][ly], NO_MASKING);
3691 MarkTileDirty(x, y);
3694 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3696 int lx = LEVELX(x), ly = LEVELY(y);
3698 if (!IN_SCR_FIELD(x, y))
3701 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3702 graphic, GfxFrame[lx][ly], NO_MASKING);
3703 MarkTileDirty(x, y);
3706 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3708 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3711 void DrawLevelElementAnimation(int x, int y, int element)
3713 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3715 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3718 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3720 int sx = SCREENX(x), sy = SCREENY(y);
3722 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3725 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3728 DrawGraphicAnimation(sx, sy, graphic);
3731 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3732 DrawLevelFieldCrumbled(x, y);
3734 if (GFX_CRUMBLED(Tile[x][y]))
3735 DrawLevelFieldCrumbled(x, y);
3739 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3741 int sx = SCREENX(x), sy = SCREENY(y);
3744 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3747 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3749 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3752 DrawGraphicAnimation(sx, sy, graphic);
3754 if (GFX_CRUMBLED(element))
3755 DrawLevelFieldCrumbled(x, y);
3758 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3760 if (player->use_murphy)
3762 // this works only because currently only one player can be "murphy" ...
3763 static int last_horizontal_dir = MV_LEFT;
3764 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3766 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3767 last_horizontal_dir = move_dir;
3769 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3771 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3773 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3779 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3782 static boolean equalGraphics(int graphic1, int graphic2)
3784 struct GraphicInfo *g1 = &graphic_info[graphic1];
3785 struct GraphicInfo *g2 = &graphic_info[graphic2];
3787 return (g1->bitmap == g2->bitmap &&
3788 g1->src_x == g2->src_x &&
3789 g1->src_y == g2->src_y &&
3790 g1->anim_frames == g2->anim_frames &&
3791 g1->anim_delay == g2->anim_delay &&
3792 g1->anim_mode == g2->anim_mode);
3795 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3799 DRAW_PLAYER_STAGE_INIT = 0,
3800 DRAW_PLAYER_STAGE_LAST_FIELD,
3801 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
3802 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3803 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3804 DRAW_PLAYER_STAGE_PLAYER,
3806 DRAW_PLAYER_STAGE_PLAYER,
3807 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3809 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
3810 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
3812 NUM_DRAW_PLAYER_STAGES
3815 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
3817 static int static_last_player_graphic[MAX_PLAYERS];
3818 static int static_last_player_frame[MAX_PLAYERS];
3819 static boolean static_player_is_opaque[MAX_PLAYERS];
3820 static boolean draw_player[MAX_PLAYERS];
3821 int pnr = player->index_nr;
3823 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3825 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
3826 static_last_player_frame[pnr] = player->Frame;
3827 static_player_is_opaque[pnr] = FALSE;
3829 draw_player[pnr] = TRUE;
3832 if (!draw_player[pnr])
3836 if (!IN_LEV_FIELD(player->jx, player->jy))
3838 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
3839 Debug("draw:DrawPlayerExt", "This should never happen!");
3841 draw_player[pnr] = FALSE;
3847 int last_player_graphic = static_last_player_graphic[pnr];
3848 int last_player_frame = static_last_player_frame[pnr];
3849 boolean player_is_opaque = static_player_is_opaque[pnr];
3851 int jx = player->jx;
3852 int jy = player->jy;
3853 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
3854 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3855 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3856 int last_jx = (player->is_moving ? jx - dx : jx);
3857 int last_jy = (player->is_moving ? jy - dy : jy);
3858 int next_jx = jx + dx;
3859 int next_jy = jy + dy;
3860 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
3861 int sx = SCREENX(jx);
3862 int sy = SCREENY(jy);
3863 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
3864 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
3865 int element = Tile[jx][jy];
3866 int last_element = Tile[last_jx][last_jy];
3867 int action = (player->is_pushing ? ACTION_PUSHING :
3868 player->is_digging ? ACTION_DIGGING :
3869 player->is_collecting ? ACTION_COLLECTING :
3870 player->is_moving ? ACTION_MOVING :
3871 player->is_snapping ? ACTION_SNAPPING :
3872 player->is_dropping ? ACTION_DROPPING :
3873 player->is_waiting ? player->action_waiting :
3876 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3878 // ------------------------------------------------------------------------
3879 // initialize drawing the player
3880 // ------------------------------------------------------------------------
3882 draw_player[pnr] = FALSE;
3884 // GfxElement[][] is set to the element the player is digging or collecting;
3885 // remove also for off-screen player if the player is not moving anymore
3886 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3887 GfxElement[jx][jy] = EL_UNDEFINED;
3889 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3892 if (element == EL_EXPLOSION)
3895 InitPlayerGfxAnimation(player, action, move_dir);
3897 draw_player[pnr] = TRUE;
3899 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
3901 // ------------------------------------------------------------------------
3902 // draw things in the field the player is leaving, if needed
3903 // ------------------------------------------------------------------------
3905 if (!IN_SCR_FIELD(sx, sy))
3906 draw_player[pnr] = FALSE;
3908 if (!player->is_moving)
3911 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3913 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3915 if (last_element == EL_DYNAMITE_ACTIVE ||
3916 last_element == EL_EM_DYNAMITE_ACTIVE ||
3917 last_element == EL_SP_DISK_RED_ACTIVE)
3918 DrawDynamite(last_jx, last_jy);
3920 DrawLevelFieldThruMask(last_jx, last_jy);
3922 else if (last_element == EL_DYNAMITE_ACTIVE ||
3923 last_element == EL_EM_DYNAMITE_ACTIVE ||
3924 last_element == EL_SP_DISK_RED_ACTIVE)
3925 DrawDynamite(last_jx, last_jy);
3927 DrawLevelField(last_jx, last_jy);
3929 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3930 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3932 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
3934 // ------------------------------------------------------------------------
3935 // draw things behind the player, if needed
3936 // ------------------------------------------------------------------------
3940 DrawLevelElement(jx, jy, Back[jx][jy]);
3945 if (IS_ACTIVE_BOMB(element))
3947 DrawLevelElement(jx, jy, EL_EMPTY);
3952 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3954 int old_element = GfxElement[jx][jy];
3955 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3956 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3958 if (GFX_CRUMBLED(old_element))
3959 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3961 DrawGraphic(sx, sy, old_graphic, frame);
3963 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3964 static_player_is_opaque[pnr] = TRUE;
3968 GfxElement[jx][jy] = EL_UNDEFINED;
3970 // make sure that pushed elements are drawn with correct frame rate
3971 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3973 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3974 GfxFrame[jx][jy] = player->StepFrame;
3976 DrawLevelField(jx, jy);
3979 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
3981 // ------------------------------------------------------------------------
3982 // draw things the player is pushing, if needed
3983 // ------------------------------------------------------------------------
3985 if (!player->is_pushing || !player->is_moving)
3988 int gfx_frame = GfxFrame[jx][jy];
3990 if (!IS_MOVING(jx, jy)) // push movement already finished
3992 element = Tile[next_jx][next_jy];
3993 gfx_frame = GfxFrame[next_jx][next_jy];
3996 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3997 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3998 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4000 // draw background element under pushed element (like the Sokoban field)
4001 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4003 // this allows transparent pushing animation over non-black background
4006 DrawLevelElement(jx, jy, Back[jx][jy]);
4008 DrawLevelElement(jx, jy, EL_EMPTY);
4010 if (Back[next_jx][next_jy])
4011 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4013 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4015 else if (Back[next_jx][next_jy])
4016 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4018 int px = SCREENX(jx), py = SCREENY(jy);
4019 int pxx = (TILEX - ABS(sxx)) * dx;
4020 int pyy = (TILEY - ABS(syy)) * dy;
4023 // do not draw (EM style) pushing animation when pushing is finished
4024 // (two-tile animations usually do not contain start and end frame)
4025 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4026 DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
4028 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4030 // masked drawing is needed for EMC style (double) movement graphics
4031 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4032 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4035 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4037 // ------------------------------------------------------------------------
4038 // draw player himself
4039 // ------------------------------------------------------------------------
4041 int graphic = getPlayerGraphic(player, move_dir);
4043 // in the case of changed player action or direction, prevent the current
4044 // animation frame from being restarted for identical animations
4045 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4046 player->Frame = last_player_frame;
4048 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4050 if (player_is_opaque)
4051 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4053 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4055 if (SHIELD_ON(player))
4057 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4058 IMG_SHIELD_NORMAL_ACTIVE);
4059 frame = getGraphicAnimationFrame(graphic, -1);
4061 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4064 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4066 // ------------------------------------------------------------------------
4067 // draw things in front of player (active dynamite or dynabombs)
4068 // ------------------------------------------------------------------------
4070 if (IS_ACTIVE_BOMB(element))
4072 int graphic = el2img(element);
4073 int frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4075 if (game.emulation == EMU_SUPAPLEX)
4076 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4078 DrawGraphicThruMask(sx, sy, graphic, frame);
4081 if (player_is_moving && last_element == EL_EXPLOSION)
4083 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4084 GfxElement[last_jx][last_jy] : EL_EMPTY);
4085 int graphic = el_act2img(element, ACTION_EXPLODING);
4086 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4087 int phase = ExplodePhase[last_jx][last_jy] - 1;
4088 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4091 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4094 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4096 // ------------------------------------------------------------------------
4097 // draw elements the player is just walking/passing through/under
4098 // ------------------------------------------------------------------------
4100 if (player_is_moving)
4102 // handle the field the player is leaving ...
4103 if (IS_ACCESSIBLE_INSIDE(last_element))
4104 DrawLevelField(last_jx, last_jy);
4105 else if (IS_ACCESSIBLE_UNDER(last_element))
4106 DrawLevelFieldThruMask(last_jx, last_jy);
4109 // do not redraw accessible elements if the player is just pushing them
4110 if (!player_is_moving || !player->is_pushing)
4112 // ... and the field the player is entering
4113 if (IS_ACCESSIBLE_INSIDE(element))
4114 DrawLevelField(jx, jy);
4115 else if (IS_ACCESSIBLE_UNDER(element))
4116 DrawLevelFieldThruMask(jx, jy);
4119 MarkTileDirty(sx, sy);
4123 void DrawPlayer(struct PlayerInfo *player)
4127 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4128 DrawPlayerExt(player, i);
4131 void DrawAllPlayers(void)
4135 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4136 for (j = 0; j < MAX_PLAYERS; j++)
4137 if (stored_player[j].active)
4138 DrawPlayerExt(&stored_player[j], i);
4141 void DrawPlayerField(int x, int y)
4143 if (!IS_PLAYER(x, y))
4146 DrawPlayer(PLAYERINFO(x, y));
4149 // ----------------------------------------------------------------------------
4151 void WaitForEventToContinue(void)
4153 boolean first_wait = TRUE;
4154 boolean still_wait = TRUE;
4156 if (program.headless)
4159 // simulate releasing mouse button over last gadget, if still pressed
4161 HandleGadgets(-1, -1, 0);
4163 button_status = MB_RELEASED;
4166 ClearPlayerAction();
4172 if (NextValidEvent(&event))
4176 case EVENT_BUTTONPRESS:
4177 case EVENT_FINGERPRESS:
4181 case EVENT_BUTTONRELEASE:
4182 case EVENT_FINGERRELEASE:
4183 still_wait = first_wait;
4186 case EVENT_KEYPRESS:
4187 case SDL_CONTROLLERBUTTONDOWN:
4188 case SDL_JOYBUTTONDOWN:
4193 HandleOtherEvents(&event);
4197 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4202 if (!PendingEvent())
4207 #define MAX_REQUEST_LINES 13
4208 #define MAX_REQUEST_LINE_FONT1_LEN 7
4209 #define MAX_REQUEST_LINE_FONT2_LEN 10
4211 static int RequestHandleEvents(unsigned int req_state)
4213 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4215 int width = request.width;
4216 int height = request.height;
4220 // when showing request dialog after game ended, deactivate game panel
4221 if (game_just_ended)
4222 game.panel.active = FALSE;
4224 game.request_active = TRUE;
4226 setRequestPosition(&sx, &sy, FALSE);
4228 button_status = MB_RELEASED;
4230 request_gadget_id = -1;
4235 if (game_just_ended)
4237 // the MM game engine does not use a special (scrollable) field buffer
4238 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4239 SetDrawtoField(DRAW_TO_FIELDBUFFER);
4241 HandleGameActions();
4243 SetDrawtoField(DRAW_TO_BACKBUFFER);
4245 if (global.use_envelope_request)
4247 // copy current state of request area to middle of playfield area
4248 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4256 while (NextValidEvent(&event))
4260 case EVENT_BUTTONPRESS:
4261 case EVENT_BUTTONRELEASE:
4262 case EVENT_MOTIONNOTIFY:
4266 if (event.type == EVENT_MOTIONNOTIFY)
4271 motion_status = TRUE;
4272 mx = ((MotionEvent *) &event)->x;
4273 my = ((MotionEvent *) &event)->y;
4277 motion_status = FALSE;
4278 mx = ((ButtonEvent *) &event)->x;
4279 my = ((ButtonEvent *) &event)->y;
4280 if (event.type == EVENT_BUTTONPRESS)
4281 button_status = ((ButtonEvent *) &event)->button;
4283 button_status = MB_RELEASED;
4286 // this sets 'request_gadget_id'
4287 HandleGadgets(mx, my, button_status);
4289 switch (request_gadget_id)
4291 case TOOL_CTRL_ID_YES:
4292 case TOOL_CTRL_ID_TOUCH_YES:
4295 case TOOL_CTRL_ID_NO:
4296 case TOOL_CTRL_ID_TOUCH_NO:
4299 case TOOL_CTRL_ID_CONFIRM:
4300 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4301 result = TRUE | FALSE;
4304 case TOOL_CTRL_ID_PLAYER_1:
4307 case TOOL_CTRL_ID_PLAYER_2:
4310 case TOOL_CTRL_ID_PLAYER_3:
4313 case TOOL_CTRL_ID_PLAYER_4:
4318 // only check clickable animations if no request gadget clicked
4319 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4326 case SDL_WINDOWEVENT:
4327 HandleWindowEvent((WindowEvent *) &event);
4330 case SDL_APP_WILLENTERBACKGROUND:
4331 case SDL_APP_DIDENTERBACKGROUND:
4332 case SDL_APP_WILLENTERFOREGROUND:
4333 case SDL_APP_DIDENTERFOREGROUND:
4334 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4337 case EVENT_KEYPRESS:
4339 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4344 if (req_state & REQ_CONFIRM)
4353 #if defined(KSYM_Rewind)
4354 case KSYM_Rewind: // for Amazon Fire TV remote
4363 #if defined(KSYM_FastForward)
4364 case KSYM_FastForward: // for Amazon Fire TV remote
4370 HandleKeysDebug(key, KEY_PRESSED);
4374 if (req_state & REQ_PLAYER)
4376 int old_player_nr = setup.network_player_nr;
4379 result = old_player_nr + 1;
4384 result = old_player_nr + 1;
4415 case EVENT_FINGERRELEASE:
4416 case EVENT_KEYRELEASE:
4417 ClearPlayerAction();
4420 case SDL_CONTROLLERBUTTONDOWN:
4421 switch (event.cbutton.button)
4423 case SDL_CONTROLLER_BUTTON_A:
4424 case SDL_CONTROLLER_BUTTON_X:
4425 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4426 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4430 case SDL_CONTROLLER_BUTTON_B:
4431 case SDL_CONTROLLER_BUTTON_Y:
4432 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4433 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4434 case SDL_CONTROLLER_BUTTON_BACK:
4439 if (req_state & REQ_PLAYER)
4441 int old_player_nr = setup.network_player_nr;
4444 result = old_player_nr + 1;
4446 switch (event.cbutton.button)
4448 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4449 case SDL_CONTROLLER_BUTTON_Y:
4453 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4454 case SDL_CONTROLLER_BUTTON_B:
4458 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4459 case SDL_CONTROLLER_BUTTON_A:
4463 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4464 case SDL_CONTROLLER_BUTTON_X:
4475 case SDL_CONTROLLERBUTTONUP:
4476 HandleJoystickEvent(&event);
4477 ClearPlayerAction();
4481 HandleOtherEvents(&event);
4486 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4488 int joy = AnyJoystick();
4490 if (joy & JOY_BUTTON_1)
4492 else if (joy & JOY_BUTTON_2)
4495 else if (AnyJoystick())
4497 int joy = AnyJoystick();
4499 if (req_state & REQ_PLAYER)
4503 else if (joy & JOY_RIGHT)
4505 else if (joy & JOY_DOWN)
4507 else if (joy & JOY_LEFT)
4512 if (game_just_ended)
4514 if (global.use_envelope_request)
4516 // copy back current state of pressed buttons inside request area
4517 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4524 game.request_active = FALSE;
4529 static boolean RequestDoor(char *text, unsigned int req_state)
4531 unsigned int old_door_state;
4532 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4533 int font_nr = FONT_TEXT_2;
4538 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4540 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4541 font_nr = FONT_TEXT_1;
4544 if (game_status == GAME_MODE_PLAYING)
4545 BlitScreenToBitmap(backbuffer);
4547 // disable deactivated drawing when quick-loading level tape recording
4548 if (tape.playing && tape.deactivate_display)
4549 TapeDeactivateDisplayOff(TRUE);
4551 SetMouseCursor(CURSOR_DEFAULT);
4553 // pause network game while waiting for request to answer
4554 if (network.enabled &&
4555 game_status == GAME_MODE_PLAYING &&
4556 !game.all_players_gone &&
4557 req_state & REQUEST_WAIT_FOR_INPUT)
4558 SendToServer_PausePlaying();
4560 old_door_state = GetDoorState();
4562 // simulate releasing mouse button over last gadget, if still pressed
4564 HandleGadgets(-1, -1, 0);
4568 // draw released gadget before proceeding
4571 if (old_door_state & DOOR_OPEN_1)
4573 CloseDoor(DOOR_CLOSE_1);
4575 // save old door content
4576 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4577 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4580 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4581 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4583 // clear door drawing field
4584 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4586 // force DOOR font inside door area
4587 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4589 // write text for request
4590 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4592 char text_line[max_request_line_len + 1];
4598 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4600 tc = *(text_ptr + tx);
4601 // if (!tc || tc == ' ')
4602 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4606 if ((tc == '?' || tc == '!') && tl == 0)
4616 strncpy(text_line, text_ptr, tl);
4619 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4620 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4621 text_line, font_nr);
4623 text_ptr += tl + (tc == ' ' ? 1 : 0);
4624 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4629 if (req_state & REQ_ASK)
4631 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4632 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4633 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4634 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4636 else if (req_state & REQ_CONFIRM)
4638 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4639 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4641 else if (req_state & REQ_PLAYER)
4643 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4644 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4645 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4646 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4649 // copy request gadgets to door backbuffer
4650 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4652 OpenDoor(DOOR_OPEN_1);
4654 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4656 if (game_status == GAME_MODE_PLAYING)
4658 SetPanelBackground();
4659 SetDrawBackgroundMask(REDRAW_DOOR_1);
4663 SetDrawBackgroundMask(REDRAW_FIELD);
4669 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4671 // ---------- handle request buttons ----------
4672 result = RequestHandleEvents(req_state);
4676 if (!(req_state & REQ_STAY_OPEN))
4678 CloseDoor(DOOR_CLOSE_1);
4680 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4681 (req_state & REQ_REOPEN))
4682 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4687 if (game_status == GAME_MODE_PLAYING)
4689 SetPanelBackground();
4690 SetDrawBackgroundMask(REDRAW_DOOR_1);
4694 SetDrawBackgroundMask(REDRAW_FIELD);
4697 // continue network game after request
4698 if (network.enabled &&
4699 game_status == GAME_MODE_PLAYING &&
4700 !game.all_players_gone &&
4701 req_state & REQUEST_WAIT_FOR_INPUT)
4702 SendToServer_ContinuePlaying();
4704 // restore deactivated drawing when quick-loading level tape recording
4705 if (tape.playing && tape.deactivate_display)
4706 TapeDeactivateDisplayOn();
4711 static boolean RequestEnvelope(char *text, unsigned int req_state)
4715 if (game_status == GAME_MODE_PLAYING)
4716 BlitScreenToBitmap(backbuffer);
4718 // disable deactivated drawing when quick-loading level tape recording
4719 if (tape.playing && tape.deactivate_display)
4720 TapeDeactivateDisplayOff(TRUE);
4722 SetMouseCursor(CURSOR_DEFAULT);
4724 // pause network game while waiting for request to answer
4725 if (network.enabled &&
4726 game_status == GAME_MODE_PLAYING &&
4727 !game.all_players_gone &&
4728 req_state & REQUEST_WAIT_FOR_INPUT)
4729 SendToServer_PausePlaying();
4731 // simulate releasing mouse button over last gadget, if still pressed
4733 HandleGadgets(-1, -1, 0);
4737 // (replace with setting corresponding request background)
4738 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4739 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4741 // clear door drawing field
4742 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4744 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4746 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4748 if (game_status == GAME_MODE_PLAYING)
4750 SetPanelBackground();
4751 SetDrawBackgroundMask(REDRAW_DOOR_1);
4755 SetDrawBackgroundMask(REDRAW_FIELD);
4761 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4763 // ---------- handle request buttons ----------
4764 result = RequestHandleEvents(req_state);
4768 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4772 if (game_status == GAME_MODE_PLAYING)
4774 SetPanelBackground();
4775 SetDrawBackgroundMask(REDRAW_DOOR_1);
4779 SetDrawBackgroundMask(REDRAW_FIELD);
4782 // continue network game after request
4783 if (network.enabled &&
4784 game_status == GAME_MODE_PLAYING &&
4785 !game.all_players_gone &&
4786 req_state & REQUEST_WAIT_FOR_INPUT)
4787 SendToServer_ContinuePlaying();
4789 // restore deactivated drawing when quick-loading level tape recording
4790 if (tape.playing && tape.deactivate_display)
4791 TapeDeactivateDisplayOn();
4796 boolean Request(char *text, unsigned int req_state)
4798 boolean overlay_enabled = GetOverlayEnabled();
4801 SetOverlayEnabled(FALSE);
4803 if (global.use_envelope_request)
4804 result = RequestEnvelope(text, req_state);
4806 result = RequestDoor(text, req_state);
4808 SetOverlayEnabled(overlay_enabled);
4813 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4815 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4816 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4819 if (dpo1->sort_priority != dpo2->sort_priority)
4820 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4822 compare_result = dpo1->nr - dpo2->nr;
4824 return compare_result;
4827 void InitGraphicCompatibilityInfo_Doors(void)
4833 struct DoorInfo *door;
4837 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4838 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4840 { -1, -1, -1, NULL }
4842 struct Rect door_rect_list[] =
4844 { DX, DY, DXSIZE, DYSIZE },
4845 { VX, VY, VXSIZE, VYSIZE }
4849 for (i = 0; doors[i].door_token != -1; i++)
4851 int door_token = doors[i].door_token;
4852 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4853 int part_1 = doors[i].part_1;
4854 int part_8 = doors[i].part_8;
4855 int part_2 = part_1 + 1;
4856 int part_3 = part_1 + 2;
4857 struct DoorInfo *door = doors[i].door;
4858 struct Rect *door_rect = &door_rect_list[door_index];
4859 boolean door_gfx_redefined = FALSE;
4861 // check if any door part graphic definitions have been redefined
4863 for (j = 0; door_part_controls[j].door_token != -1; j++)
4865 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4866 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4868 if (dpc->door_token == door_token && fi->redefined)
4869 door_gfx_redefined = TRUE;
4872 // check for old-style door graphic/animation modifications
4874 if (!door_gfx_redefined)
4876 if (door->anim_mode & ANIM_STATIC_PANEL)
4878 door->panel.step_xoffset = 0;
4879 door->panel.step_yoffset = 0;
4882 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4884 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4885 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4886 int num_door_steps, num_panel_steps;
4888 // remove door part graphics other than the two default wings
4890 for (j = 0; door_part_controls[j].door_token != -1; j++)
4892 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4893 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4895 if (dpc->graphic >= part_3 &&
4896 dpc->graphic <= part_8)
4900 // set graphics and screen positions of the default wings
4902 g_part_1->width = door_rect->width;
4903 g_part_1->height = door_rect->height;
4904 g_part_2->width = door_rect->width;
4905 g_part_2->height = door_rect->height;
4906 g_part_2->src_x = door_rect->width;
4907 g_part_2->src_y = g_part_1->src_y;
4909 door->part_2.x = door->part_1.x;
4910 door->part_2.y = door->part_1.y;
4912 if (door->width != -1)
4914 g_part_1->width = door->width;
4915 g_part_2->width = door->width;
4917 // special treatment for graphics and screen position of right wing
4918 g_part_2->src_x += door_rect->width - door->width;
4919 door->part_2.x += door_rect->width - door->width;
4922 if (door->height != -1)
4924 g_part_1->height = door->height;
4925 g_part_2->height = door->height;
4927 // special treatment for graphics and screen position of bottom wing
4928 g_part_2->src_y += door_rect->height - door->height;
4929 door->part_2.y += door_rect->height - door->height;
4932 // set animation delays for the default wings and panels
4934 door->part_1.step_delay = door->step_delay;
4935 door->part_2.step_delay = door->step_delay;
4936 door->panel.step_delay = door->step_delay;
4938 // set animation draw order for the default wings
4940 door->part_1.sort_priority = 2; // draw left wing over ...
4941 door->part_2.sort_priority = 1; // ... right wing
4943 // set animation draw offset for the default wings
4945 if (door->anim_mode & ANIM_HORIZONTAL)
4947 door->part_1.step_xoffset = door->step_offset;
4948 door->part_1.step_yoffset = 0;
4949 door->part_2.step_xoffset = door->step_offset * -1;
4950 door->part_2.step_yoffset = 0;
4952 num_door_steps = g_part_1->width / door->step_offset;
4954 else // ANIM_VERTICAL
4956 door->part_1.step_xoffset = 0;
4957 door->part_1.step_yoffset = door->step_offset;
4958 door->part_2.step_xoffset = 0;
4959 door->part_2.step_yoffset = door->step_offset * -1;
4961 num_door_steps = g_part_1->height / door->step_offset;
4964 // set animation draw offset for the default panels
4966 if (door->step_offset > 1)
4968 num_panel_steps = 2 * door_rect->height / door->step_offset;
4969 door->panel.start_step = num_panel_steps - num_door_steps;
4970 door->panel.start_step_closing = door->panel.start_step;
4974 num_panel_steps = door_rect->height / door->step_offset;
4975 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4976 door->panel.start_step_closing = door->panel.start_step;
4977 door->panel.step_delay *= 2;
4984 void InitDoors(void)
4988 for (i = 0; door_part_controls[i].door_token != -1; i++)
4990 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4991 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4993 // initialize "start_step_opening" and "start_step_closing", if needed
4994 if (dpc->pos->start_step_opening == 0 &&
4995 dpc->pos->start_step_closing == 0)
4997 // dpc->pos->start_step_opening = dpc->pos->start_step;
4998 dpc->pos->start_step_closing = dpc->pos->start_step;
5001 // fill structure for door part draw order (sorted below)
5003 dpo->sort_priority = dpc->pos->sort_priority;
5006 // sort door part controls according to sort_priority and graphic number
5007 qsort(door_part_order, MAX_DOOR_PARTS,
5008 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5011 unsigned int OpenDoor(unsigned int door_state)
5013 if (door_state & DOOR_COPY_BACK)
5015 if (door_state & DOOR_OPEN_1)
5016 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5017 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5019 if (door_state & DOOR_OPEN_2)
5020 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5021 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5023 door_state &= ~DOOR_COPY_BACK;
5026 return MoveDoor(door_state);
5029 unsigned int CloseDoor(unsigned int door_state)
5031 unsigned int old_door_state = GetDoorState();
5033 if (!(door_state & DOOR_NO_COPY_BACK))
5035 if (old_door_state & DOOR_OPEN_1)
5036 BlitBitmap(backbuffer, bitmap_db_door_1,
5037 DX, DY, DXSIZE, DYSIZE, 0, 0);
5039 if (old_door_state & DOOR_OPEN_2)
5040 BlitBitmap(backbuffer, bitmap_db_door_2,
5041 VX, VY, VXSIZE, VYSIZE, 0, 0);
5043 door_state &= ~DOOR_NO_COPY_BACK;
5046 return MoveDoor(door_state);
5049 unsigned int GetDoorState(void)
5051 return MoveDoor(DOOR_GET_STATE);
5054 unsigned int SetDoorState(unsigned int door_state)
5056 return MoveDoor(door_state | DOOR_SET_STATE);
5059 static int euclid(int a, int b)
5061 return (b ? euclid(b, a % b) : a);
5064 unsigned int MoveDoor(unsigned int door_state)
5066 struct Rect door_rect_list[] =
5068 { DX, DY, DXSIZE, DYSIZE },
5069 { VX, VY, VXSIZE, VYSIZE }
5071 static int door1 = DOOR_CLOSE_1;
5072 static int door2 = DOOR_CLOSE_2;
5073 unsigned int door_delay = 0;
5074 unsigned int door_delay_value;
5077 if (door_state == DOOR_GET_STATE)
5078 return (door1 | door2);
5080 if (door_state & DOOR_SET_STATE)
5082 if (door_state & DOOR_ACTION_1)
5083 door1 = door_state & DOOR_ACTION_1;
5084 if (door_state & DOOR_ACTION_2)
5085 door2 = door_state & DOOR_ACTION_2;
5087 return (door1 | door2);
5090 if (!(door_state & DOOR_FORCE_REDRAW))
5092 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5093 door_state &= ~DOOR_OPEN_1;
5094 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5095 door_state &= ~DOOR_CLOSE_1;
5096 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5097 door_state &= ~DOOR_OPEN_2;
5098 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5099 door_state &= ~DOOR_CLOSE_2;
5102 if (global.autoplay_leveldir)
5104 door_state |= DOOR_NO_DELAY;
5105 door_state &= ~DOOR_CLOSE_ALL;
5108 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5109 door_state |= DOOR_NO_DELAY;
5111 if (door_state & DOOR_ACTION)
5113 boolean door_panel_drawn[NUM_DOORS];
5114 boolean panel_has_doors[NUM_DOORS];
5115 boolean door_part_skip[MAX_DOOR_PARTS];
5116 boolean door_part_done[MAX_DOOR_PARTS];
5117 boolean door_part_done_all;
5118 int num_steps[MAX_DOOR_PARTS];
5119 int max_move_delay = 0; // delay for complete animations of all doors
5120 int max_step_delay = 0; // delay (ms) between two animation frames
5121 int num_move_steps = 0; // number of animation steps for all doors
5122 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5123 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5124 int current_move_delay = 0;
5128 for (i = 0; i < NUM_DOORS; i++)
5129 panel_has_doors[i] = FALSE;
5131 for (i = 0; i < MAX_DOOR_PARTS; i++)
5133 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5134 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5135 int door_token = dpc->door_token;
5137 door_part_done[i] = FALSE;
5138 door_part_skip[i] = (!(door_state & door_token) ||
5142 for (i = 0; i < MAX_DOOR_PARTS; i++)
5144 int nr = door_part_order[i].nr;
5145 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5146 struct DoorPartPosInfo *pos = dpc->pos;
5147 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5148 int door_token = dpc->door_token;
5149 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5150 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5151 int step_xoffset = ABS(pos->step_xoffset);
5152 int step_yoffset = ABS(pos->step_yoffset);
5153 int step_delay = pos->step_delay;
5154 int current_door_state = door_state & door_token;
5155 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5156 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5157 boolean part_opening = (is_panel ? door_closing : door_opening);
5158 int start_step = (part_opening ? pos->start_step_opening :
5159 pos->start_step_closing);
5160 float move_xsize = (step_xoffset ? g->width : 0);
5161 float move_ysize = (step_yoffset ? g->height : 0);
5162 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5163 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5164 int move_steps = (move_xsteps && move_ysteps ?
5165 MIN(move_xsteps, move_ysteps) :
5166 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5167 int move_delay = move_steps * step_delay;
5169 if (door_part_skip[nr])
5172 max_move_delay = MAX(max_move_delay, move_delay);
5173 max_step_delay = (max_step_delay == 0 ? step_delay :
5174 euclid(max_step_delay, step_delay));
5175 num_steps[nr] = move_steps;
5179 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5181 panel_has_doors[door_index] = TRUE;
5185 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5187 num_move_steps = max_move_delay / max_step_delay;
5188 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5190 door_delay_value = max_step_delay;
5192 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5194 start = num_move_steps - 1;
5198 // opening door sound has priority over simultaneously closing door
5199 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5201 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5203 if (door_state & DOOR_OPEN_1)
5204 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5205 if (door_state & DOOR_OPEN_2)
5206 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5208 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5210 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5212 if (door_state & DOOR_CLOSE_1)
5213 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5214 if (door_state & DOOR_CLOSE_2)
5215 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5219 for (k = start; k < num_move_steps; k++)
5221 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5223 door_part_done_all = TRUE;
5225 for (i = 0; i < NUM_DOORS; i++)
5226 door_panel_drawn[i] = FALSE;
5228 for (i = 0; i < MAX_DOOR_PARTS; i++)
5230 int nr = door_part_order[i].nr;
5231 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5232 struct DoorPartPosInfo *pos = dpc->pos;
5233 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5234 int door_token = dpc->door_token;
5235 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5236 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5237 boolean is_panel_and_door_has_closed = FALSE;
5238 struct Rect *door_rect = &door_rect_list[door_index];
5239 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5241 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5242 int current_door_state = door_state & door_token;
5243 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5244 boolean door_closing = !door_opening;
5245 boolean part_opening = (is_panel ? door_closing : door_opening);
5246 boolean part_closing = !part_opening;
5247 int start_step = (part_opening ? pos->start_step_opening :
5248 pos->start_step_closing);
5249 int step_delay = pos->step_delay;
5250 int step_factor = step_delay / max_step_delay;
5251 int k1 = (step_factor ? k / step_factor + 1 : k);
5252 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5253 int kk = MAX(0, k2);
5256 int src_x, src_y, src_xx, src_yy;
5257 int dst_x, dst_y, dst_xx, dst_yy;
5260 if (door_part_skip[nr])
5263 if (!(door_state & door_token))
5271 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5272 int kk_door = MAX(0, k2_door);
5273 int sync_frame = kk_door * door_delay_value;
5274 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5276 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5277 &g_src_x, &g_src_y);
5282 if (!door_panel_drawn[door_index])
5284 ClearRectangle(drawto, door_rect->x, door_rect->y,
5285 door_rect->width, door_rect->height);
5287 door_panel_drawn[door_index] = TRUE;
5290 // draw opening or closing door parts
5292 if (pos->step_xoffset < 0) // door part on right side
5295 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5298 if (dst_xx + width > door_rect->width)
5299 width = door_rect->width - dst_xx;
5301 else // door part on left side
5304 dst_xx = pos->x - kk * pos->step_xoffset;
5308 src_xx = ABS(dst_xx);
5312 width = g->width - src_xx;
5314 if (width > door_rect->width)
5315 width = door_rect->width;
5317 // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5320 if (pos->step_yoffset < 0) // door part on bottom side
5323 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5326 if (dst_yy + height > door_rect->height)
5327 height = door_rect->height - dst_yy;
5329 else // door part on top side
5332 dst_yy = pos->y - kk * pos->step_yoffset;
5336 src_yy = ABS(dst_yy);
5340 height = g->height - src_yy;
5343 src_x = g_src_x + src_xx;
5344 src_y = g_src_y + src_yy;
5346 dst_x = door_rect->x + dst_xx;
5347 dst_y = door_rect->y + dst_yy;
5349 is_panel_and_door_has_closed =
5352 panel_has_doors[door_index] &&
5353 k >= num_move_steps_doors_only - 1);
5355 if (width >= 0 && width <= g->width &&
5356 height >= 0 && height <= g->height &&
5357 !is_panel_and_door_has_closed)
5359 if (is_panel || !pos->draw_masked)
5360 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5363 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5367 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5369 if ((part_opening && (width < 0 || height < 0)) ||
5370 (part_closing && (width >= g->width && height >= g->height)))
5371 door_part_done[nr] = TRUE;
5373 // continue door part animations, but not panel after door has closed
5374 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5375 door_part_done_all = FALSE;
5378 if (!(door_state & DOOR_NO_DELAY))
5382 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5384 current_move_delay += max_step_delay;
5386 // prevent OS (Windows) from complaining about program not responding
5390 if (door_part_done_all)
5394 if (!(door_state & DOOR_NO_DELAY))
5396 // wait for specified door action post delay
5397 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5398 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5399 else if (door_state & DOOR_ACTION_1)
5400 door_delay_value = door_1.post_delay;
5401 else if (door_state & DOOR_ACTION_2)
5402 door_delay_value = door_2.post_delay;
5404 while (!DelayReached(&door_delay, door_delay_value))
5409 if (door_state & DOOR_ACTION_1)
5410 door1 = door_state & DOOR_ACTION_1;
5411 if (door_state & DOOR_ACTION_2)
5412 door2 = door_state & DOOR_ACTION_2;
5414 // draw masked border over door area
5415 DrawMaskedBorder(REDRAW_DOOR_1);
5416 DrawMaskedBorder(REDRAW_DOOR_2);
5418 ClearAutoRepeatKeyEvents();
5420 return (door1 | door2);
5423 static boolean useSpecialEditorDoor(void)
5425 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5426 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5428 // do not draw special editor door if editor border defined or redefined
5429 if (graphic_info[graphic].bitmap != NULL || redefined)
5432 // do not draw special editor door if global border defined to be empty
5433 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5436 // do not draw special editor door if viewport definitions do not match
5440 EY + EYSIZE != VY + VYSIZE)
5446 void DrawSpecialEditorDoor(void)
5448 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5449 int top_border_width = gfx1->width;
5450 int top_border_height = gfx1->height;
5451 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5452 int ex = EX - outer_border;
5453 int ey = EY - outer_border;
5454 int vy = VY - outer_border;
5455 int exsize = EXSIZE + 2 * outer_border;
5457 if (!useSpecialEditorDoor())
5460 // draw bigger level editor toolbox window
5461 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5462 top_border_width, top_border_height, ex, ey - top_border_height);
5463 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5464 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5466 redraw_mask |= REDRAW_ALL;
5469 void UndrawSpecialEditorDoor(void)
5471 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5472 int top_border_width = gfx1->width;
5473 int top_border_height = gfx1->height;
5474 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5475 int ex = EX - outer_border;
5476 int ey = EY - outer_border;
5477 int ey_top = ey - top_border_height;
5478 int exsize = EXSIZE + 2 * outer_border;
5479 int eysize = EYSIZE + 2 * outer_border;
5481 if (!useSpecialEditorDoor())
5484 // draw normal tape recorder window
5485 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5487 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5488 ex, ey_top, top_border_width, top_border_height,
5490 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5491 ex, ey, exsize, eysize, ex, ey);
5495 // if screen background is set to "[NONE]", clear editor toolbox window
5496 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5497 ClearRectangle(drawto, ex, ey, exsize, eysize);
5500 redraw_mask |= REDRAW_ALL;
5504 // ---------- new tool button stuff -------------------------------------------
5509 struct TextPosInfo *pos;
5511 boolean is_touch_button;
5513 } toolbutton_info[NUM_TOOL_BUTTONS] =
5516 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5517 TOOL_CTRL_ID_YES, FALSE, "yes"
5520 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5521 TOOL_CTRL_ID_NO, FALSE, "no"
5524 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5525 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5528 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5529 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5532 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5533 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5536 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5537 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5540 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5541 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5544 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5545 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5548 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5549 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5552 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5553 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5557 void CreateToolButtons(void)
5561 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5563 int graphic = toolbutton_info[i].graphic;
5564 struct GraphicInfo *gfx = &graphic_info[graphic];
5565 struct TextPosInfo *pos = toolbutton_info[i].pos;
5566 struct GadgetInfo *gi;
5567 Bitmap *deco_bitmap = None;
5568 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5569 unsigned int event_mask = GD_EVENT_RELEASED;
5570 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5571 int base_x = (is_touch_button ? 0 : DX);
5572 int base_y = (is_touch_button ? 0 : DY);
5573 int gd_x = gfx->src_x;
5574 int gd_y = gfx->src_y;
5575 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5576 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5581 if (global.use_envelope_request && !is_touch_button)
5583 setRequestPosition(&base_x, &base_y, TRUE);
5585 // check if request buttons are outside of envelope and fix, if needed
5586 if (x < 0 || x + gfx->width > request.width ||
5587 y < 0 || y + gfx->height > request.height)
5589 if (id == TOOL_CTRL_ID_YES)
5592 y = request.height - 2 * request.border_size - gfx->height;
5594 else if (id == TOOL_CTRL_ID_NO)
5596 x = request.width - 2 * request.border_size - gfx->width;
5597 y = request.height - 2 * request.border_size - gfx->height;
5599 else if (id == TOOL_CTRL_ID_CONFIRM)
5601 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5602 y = request.height - 2 * request.border_size - gfx->height;
5604 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5606 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5608 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5609 y = request.height - 2 * request.border_size - gfx->height * 2;
5611 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5612 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5617 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5619 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5621 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5622 pos->size, &deco_bitmap, &deco_x, &deco_y);
5623 deco_xpos = (gfx->width - pos->size) / 2;
5624 deco_ypos = (gfx->height - pos->size) / 2;
5627 gi = CreateGadget(GDI_CUSTOM_ID, id,
5628 GDI_IMAGE_ID, graphic,
5629 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5632 GDI_WIDTH, gfx->width,
5633 GDI_HEIGHT, gfx->height,
5634 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5635 GDI_STATE, GD_BUTTON_UNPRESSED,
5636 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5637 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5638 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5639 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5640 GDI_DECORATION_SIZE, pos->size, pos->size,
5641 GDI_DECORATION_SHIFTING, 1, 1,
5642 GDI_DIRECT_DRAW, FALSE,
5643 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5644 GDI_EVENT_MASK, event_mask,
5645 GDI_CALLBACK_ACTION, HandleToolButtons,
5649 Fail("cannot create gadget");
5651 tool_gadget[id] = gi;
5655 void FreeToolButtons(void)
5659 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5660 FreeGadget(tool_gadget[i]);
5663 static void UnmapToolButtons(void)
5667 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5668 UnmapGadget(tool_gadget[i]);
5671 static void HandleToolButtons(struct GadgetInfo *gi)
5673 request_gadget_id = gi->custom_id;
5676 static struct Mapping_EM_to_RND_object
5679 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5680 boolean is_backside; // backside of moving element
5686 em_object_mapping_list[GAME_TILE_MAX + 1] =
5689 Zborder, FALSE, FALSE,
5693 Zplayer, FALSE, FALSE,
5702 Ztank, FALSE, FALSE,
5706 Zeater, FALSE, FALSE,
5710 Zdynamite, FALSE, FALSE,
5714 Zboom, FALSE, FALSE,
5719 Xchain, FALSE, FALSE,
5720 EL_DEFAULT, ACTION_EXPLODING, -1
5723 Xboom_bug, FALSE, FALSE,
5724 EL_BUG, ACTION_EXPLODING, -1
5727 Xboom_tank, FALSE, FALSE,
5728 EL_SPACESHIP, ACTION_EXPLODING, -1
5731 Xboom_android, FALSE, FALSE,
5732 EL_EMC_ANDROID, ACTION_OTHER, -1
5735 Xboom_1, FALSE, FALSE,
5736 EL_DEFAULT, ACTION_EXPLODING, -1
5739 Xboom_2, FALSE, FALSE,
5740 EL_DEFAULT, ACTION_EXPLODING, -1
5744 Xblank, TRUE, FALSE,
5749 Xsplash_e, FALSE, FALSE,
5750 EL_ACID_SPLASH_RIGHT, -1, -1
5753 Xsplash_w, FALSE, FALSE,
5754 EL_ACID_SPLASH_LEFT, -1, -1
5758 Xplant, TRUE, FALSE,
5759 EL_EMC_PLANT, -1, -1
5762 Yplant, FALSE, FALSE,
5763 EL_EMC_PLANT, -1, -1
5767 Xacid_1, TRUE, FALSE,
5771 Xacid_2, FALSE, FALSE,
5775 Xacid_3, FALSE, FALSE,
5779 Xacid_4, FALSE, FALSE,
5783 Xacid_5, FALSE, FALSE,
5787 Xacid_6, FALSE, FALSE,
5791 Xacid_7, FALSE, FALSE,
5795 Xacid_8, FALSE, FALSE,
5800 Xfake_acid_1, TRUE, FALSE,
5801 EL_EMC_FAKE_ACID, -1, -1
5804 Xfake_acid_2, FALSE, FALSE,
5805 EL_EMC_FAKE_ACID, -1, -1
5808 Xfake_acid_3, FALSE, FALSE,
5809 EL_EMC_FAKE_ACID, -1, -1
5812 Xfake_acid_4, FALSE, FALSE,
5813 EL_EMC_FAKE_ACID, -1, -1
5816 Xfake_acid_5, FALSE, FALSE,
5817 EL_EMC_FAKE_ACID, -1, -1
5820 Xfake_acid_6, FALSE, FALSE,
5821 EL_EMC_FAKE_ACID, -1, -1
5824 Xfake_acid_7, FALSE, FALSE,
5825 EL_EMC_FAKE_ACID, -1, -1
5828 Xfake_acid_8, FALSE, FALSE,
5829 EL_EMC_FAKE_ACID, -1, -1
5833 Xfake_acid_1_player, FALSE, FALSE,
5834 EL_EMC_FAKE_ACID, -1, -1
5837 Xfake_acid_2_player, FALSE, FALSE,
5838 EL_EMC_FAKE_ACID, -1, -1
5841 Xfake_acid_3_player, FALSE, FALSE,
5842 EL_EMC_FAKE_ACID, -1, -1
5845 Xfake_acid_4_player, FALSE, FALSE,
5846 EL_EMC_FAKE_ACID, -1, -1
5849 Xfake_acid_5_player, FALSE, FALSE,
5850 EL_EMC_FAKE_ACID, -1, -1
5853 Xfake_acid_6_player, FALSE, FALSE,
5854 EL_EMC_FAKE_ACID, -1, -1
5857 Xfake_acid_7_player, FALSE, FALSE,
5858 EL_EMC_FAKE_ACID, -1, -1
5861 Xfake_acid_8_player, FALSE, FALSE,
5862 EL_EMC_FAKE_ACID, -1, -1
5866 Xgrass, TRUE, FALSE,
5867 EL_EMC_GRASS, -1, -1
5870 Ygrass_nB, FALSE, FALSE,
5871 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5874 Ygrass_eB, FALSE, FALSE,
5875 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5878 Ygrass_sB, FALSE, FALSE,
5879 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5882 Ygrass_wB, FALSE, FALSE,
5883 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5891 Ydirt_nB, FALSE, FALSE,
5892 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5895 Ydirt_eB, FALSE, FALSE,
5896 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5899 Ydirt_sB, FALSE, FALSE,
5900 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5903 Ydirt_wB, FALSE, FALSE,
5904 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5908 Xandroid, TRUE, FALSE,
5909 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5912 Xandroid_1_n, FALSE, FALSE,
5913 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5916 Xandroid_2_n, FALSE, FALSE,
5917 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5920 Xandroid_1_e, FALSE, FALSE,
5921 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5924 Xandroid_2_e, FALSE, FALSE,
5925 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5928 Xandroid_1_w, FALSE, FALSE,
5929 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5932 Xandroid_2_w, FALSE, FALSE,
5933 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5936 Xandroid_1_s, FALSE, FALSE,
5937 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5940 Xandroid_2_s, FALSE, FALSE,
5941 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5944 Yandroid_n, FALSE, FALSE,
5945 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5948 Yandroid_nB, FALSE, TRUE,
5949 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5952 Yandroid_ne, FALSE, FALSE,
5953 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5956 Yandroid_neB, FALSE, TRUE,
5957 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5960 Yandroid_e, FALSE, FALSE,
5961 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5964 Yandroid_eB, FALSE, TRUE,
5965 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5968 Yandroid_se, FALSE, FALSE,
5969 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5972 Yandroid_seB, FALSE, TRUE,
5973 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5976 Yandroid_s, FALSE, FALSE,
5977 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5980 Yandroid_sB, FALSE, TRUE,
5981 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5984 Yandroid_sw, FALSE, FALSE,
5985 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5988 Yandroid_swB, FALSE, TRUE,
5989 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5992 Yandroid_w, FALSE, FALSE,
5993 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5996 Yandroid_wB, FALSE, TRUE,
5997 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6000 Yandroid_nw, FALSE, FALSE,
6001 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6004 Yandroid_nwB, FALSE, TRUE,
6005 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6009 Xeater_n, TRUE, FALSE,
6010 EL_YAMYAM_UP, -1, -1
6013 Xeater_e, TRUE, FALSE,
6014 EL_YAMYAM_RIGHT, -1, -1
6017 Xeater_w, TRUE, FALSE,
6018 EL_YAMYAM_LEFT, -1, -1
6021 Xeater_s, TRUE, FALSE,
6022 EL_YAMYAM_DOWN, -1, -1
6025 Yeater_n, FALSE, FALSE,
6026 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6029 Yeater_nB, FALSE, TRUE,
6030 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6033 Yeater_e, FALSE, FALSE,
6034 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6037 Yeater_eB, FALSE, TRUE,
6038 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6041 Yeater_s, FALSE, FALSE,
6042 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6045 Yeater_sB, FALSE, TRUE,
6046 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6049 Yeater_w, FALSE, FALSE,
6050 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6053 Yeater_wB, FALSE, TRUE,
6054 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6057 Yeater_stone, FALSE, FALSE,
6058 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6061 Yeater_spring, FALSE, FALSE,
6062 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6066 Xalien, TRUE, FALSE,
6070 Xalien_pause, FALSE, FALSE,
6074 Yalien_n, FALSE, FALSE,
6075 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6078 Yalien_nB, FALSE, TRUE,
6079 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6082 Yalien_e, FALSE, FALSE,
6083 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6086 Yalien_eB, FALSE, TRUE,
6087 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6090 Yalien_s, FALSE, FALSE,
6091 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6094 Yalien_sB, FALSE, TRUE,
6095 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6098 Yalien_w, FALSE, FALSE,
6099 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6102 Yalien_wB, FALSE, TRUE,
6103 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6106 Yalien_stone, FALSE, FALSE,
6107 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6110 Yalien_spring, FALSE, FALSE,
6111 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6115 Xbug_1_n, TRUE, FALSE,
6119 Xbug_1_e, TRUE, FALSE,
6120 EL_BUG_RIGHT, -1, -1
6123 Xbug_1_s, TRUE, FALSE,
6127 Xbug_1_w, TRUE, FALSE,
6131 Xbug_2_n, FALSE, FALSE,
6135 Xbug_2_e, FALSE, FALSE,
6136 EL_BUG_RIGHT, -1, -1
6139 Xbug_2_s, FALSE, FALSE,
6143 Xbug_2_w, FALSE, FALSE,
6147 Ybug_n, FALSE, FALSE,
6148 EL_BUG, ACTION_MOVING, MV_BIT_UP
6151 Ybug_nB, FALSE, TRUE,
6152 EL_BUG, ACTION_MOVING, MV_BIT_UP
6155 Ybug_e, FALSE, FALSE,
6156 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6159 Ybug_eB, FALSE, TRUE,
6160 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6163 Ybug_s, FALSE, FALSE,
6164 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6167 Ybug_sB, FALSE, TRUE,
6168 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6171 Ybug_w, FALSE, FALSE,
6172 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6175 Ybug_wB, FALSE, TRUE,
6176 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6179 Ybug_w_n, FALSE, FALSE,
6180 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6183 Ybug_n_e, FALSE, FALSE,
6184 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6187 Ybug_e_s, FALSE, FALSE,
6188 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6191 Ybug_s_w, FALSE, FALSE,
6192 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6195 Ybug_e_n, FALSE, FALSE,
6196 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6199 Ybug_s_e, FALSE, FALSE,
6200 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6203 Ybug_w_s, FALSE, FALSE,
6204 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6207 Ybug_n_w, FALSE, FALSE,
6208 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6211 Ybug_stone, FALSE, FALSE,
6212 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6215 Ybug_spring, FALSE, FALSE,
6216 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6220 Xtank_1_n, TRUE, FALSE,
6221 EL_SPACESHIP_UP, -1, -1
6224 Xtank_1_e, TRUE, FALSE,
6225 EL_SPACESHIP_RIGHT, -1, -1
6228 Xtank_1_s, TRUE, FALSE,
6229 EL_SPACESHIP_DOWN, -1, -1
6232 Xtank_1_w, TRUE, FALSE,
6233 EL_SPACESHIP_LEFT, -1, -1
6236 Xtank_2_n, FALSE, FALSE,
6237 EL_SPACESHIP_UP, -1, -1
6240 Xtank_2_e, FALSE, FALSE,
6241 EL_SPACESHIP_RIGHT, -1, -1
6244 Xtank_2_s, FALSE, FALSE,
6245 EL_SPACESHIP_DOWN, -1, -1
6248 Xtank_2_w, FALSE, FALSE,
6249 EL_SPACESHIP_LEFT, -1, -1
6252 Ytank_n, FALSE, FALSE,
6253 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6256 Ytank_nB, FALSE, TRUE,
6257 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6260 Ytank_e, FALSE, FALSE,
6261 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6264 Ytank_eB, FALSE, TRUE,
6265 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6268 Ytank_s, FALSE, FALSE,
6269 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6272 Ytank_sB, FALSE, TRUE,
6273 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6276 Ytank_w, FALSE, FALSE,
6277 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6280 Ytank_wB, FALSE, TRUE,
6281 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6284 Ytank_w_n, FALSE, FALSE,
6285 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6288 Ytank_n_e, FALSE, FALSE,
6289 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6292 Ytank_e_s, FALSE, FALSE,
6293 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6296 Ytank_s_w, FALSE, FALSE,
6297 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6300 Ytank_e_n, FALSE, FALSE,
6301 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6304 Ytank_s_e, FALSE, FALSE,
6305 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6308 Ytank_w_s, FALSE, FALSE,
6309 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6312 Ytank_n_w, FALSE, FALSE,
6313 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6316 Ytank_stone, FALSE, FALSE,
6317 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6320 Ytank_spring, FALSE, FALSE,
6321 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6325 Xemerald, TRUE, FALSE,
6329 Xemerald_pause, FALSE, FALSE,
6333 Xemerald_fall, FALSE, FALSE,
6337 Xemerald_shine, FALSE, FALSE,
6338 EL_EMERALD, ACTION_TWINKLING, -1
6341 Yemerald_s, FALSE, FALSE,
6342 EL_EMERALD, ACTION_FALLING, -1
6345 Yemerald_sB, FALSE, TRUE,
6346 EL_EMERALD, ACTION_FALLING, -1
6349 Yemerald_e, FALSE, FALSE,
6350 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6353 Yemerald_eB, FALSE, TRUE,
6354 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6357 Yemerald_w, FALSE, FALSE,
6358 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6361 Yemerald_wB, FALSE, TRUE,
6362 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6365 Yemerald_blank, FALSE, FALSE,
6366 EL_EMERALD, ACTION_COLLECTING, -1
6370 Xdiamond, TRUE, FALSE,
6374 Xdiamond_pause, FALSE, FALSE,
6378 Xdiamond_fall, FALSE, FALSE,
6382 Xdiamond_shine, FALSE, FALSE,
6383 EL_DIAMOND, ACTION_TWINKLING, -1
6386 Ydiamond_s, FALSE, FALSE,
6387 EL_DIAMOND, ACTION_FALLING, -1
6390 Ydiamond_sB, FALSE, TRUE,
6391 EL_DIAMOND, ACTION_FALLING, -1
6394 Ydiamond_e, FALSE, FALSE,
6395 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6398 Ydiamond_eB, FALSE, TRUE,
6399 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6402 Ydiamond_w, FALSE, FALSE,
6403 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6406 Ydiamond_wB, FALSE, TRUE,
6407 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6410 Ydiamond_blank, FALSE, FALSE,
6411 EL_DIAMOND, ACTION_COLLECTING, -1
6414 Ydiamond_stone, FALSE, FALSE,
6415 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6419 Xstone, TRUE, FALSE,
6423 Xstone_pause, FALSE, FALSE,
6427 Xstone_fall, FALSE, FALSE,
6431 Ystone_s, FALSE, FALSE,
6432 EL_ROCK, ACTION_FALLING, -1
6435 Ystone_sB, FALSE, TRUE,
6436 EL_ROCK, ACTION_FALLING, -1
6439 Ystone_e, FALSE, FALSE,
6440 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6443 Ystone_eB, FALSE, TRUE,
6444 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6447 Ystone_w, FALSE, FALSE,
6448 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6451 Ystone_wB, FALSE, TRUE,
6452 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6460 Xbomb_pause, FALSE, FALSE,
6464 Xbomb_fall, FALSE, FALSE,
6468 Ybomb_s, FALSE, FALSE,
6469 EL_BOMB, ACTION_FALLING, -1
6472 Ybomb_sB, FALSE, TRUE,
6473 EL_BOMB, ACTION_FALLING, -1
6476 Ybomb_e, FALSE, FALSE,
6477 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6480 Ybomb_eB, FALSE, TRUE,
6481 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6484 Ybomb_w, FALSE, FALSE,
6485 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6488 Ybomb_wB, FALSE, TRUE,
6489 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6492 Ybomb_blank, FALSE, FALSE,
6493 EL_BOMB, ACTION_ACTIVATING, -1
6501 Xnut_pause, FALSE, FALSE,
6505 Xnut_fall, FALSE, FALSE,
6509 Ynut_s, FALSE, FALSE,
6510 EL_NUT, ACTION_FALLING, -1
6513 Ynut_sB, FALSE, TRUE,
6514 EL_NUT, ACTION_FALLING, -1
6517 Ynut_e, FALSE, FALSE,
6518 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6521 Ynut_eB, FALSE, TRUE,
6522 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6525 Ynut_w, FALSE, FALSE,
6526 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6529 Ynut_wB, FALSE, TRUE,
6530 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6533 Ynut_stone, FALSE, FALSE,
6534 EL_NUT, ACTION_BREAKING, -1
6538 Xspring, TRUE, FALSE,
6542 Xspring_pause, FALSE, FALSE,
6546 Xspring_e, TRUE, FALSE,
6547 EL_SPRING_RIGHT, -1, -1
6550 Xspring_w, TRUE, FALSE,
6551 EL_SPRING_LEFT, -1, -1
6554 Xspring_fall, FALSE, FALSE,
6558 Yspring_s, FALSE, FALSE,
6559 EL_SPRING, ACTION_FALLING, -1
6562 Yspring_sB, FALSE, TRUE,
6563 EL_SPRING, ACTION_FALLING, -1
6566 Yspring_e, FALSE, FALSE,
6567 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6570 Yspring_eB, FALSE, TRUE,
6571 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6574 Yspring_w, FALSE, FALSE,
6575 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6578 Yspring_wB, FALSE, TRUE,
6579 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6582 Yspring_alien_e, FALSE, FALSE,
6583 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6586 Yspring_alien_eB, FALSE, TRUE,
6587 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6590 Yspring_alien_w, FALSE, FALSE,
6591 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6594 Yspring_alien_wB, FALSE, TRUE,
6595 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6599 Xpush_emerald_e, FALSE, FALSE,
6600 EL_EMERALD, -1, MV_BIT_RIGHT
6603 Xpush_emerald_w, FALSE, FALSE,
6604 EL_EMERALD, -1, MV_BIT_LEFT
6607 Xpush_diamond_e, FALSE, FALSE,
6608 EL_DIAMOND, -1, MV_BIT_RIGHT
6611 Xpush_diamond_w, FALSE, FALSE,
6612 EL_DIAMOND, -1, MV_BIT_LEFT
6615 Xpush_stone_e, FALSE, FALSE,
6616 EL_ROCK, -1, MV_BIT_RIGHT
6619 Xpush_stone_w, FALSE, FALSE,
6620 EL_ROCK, -1, MV_BIT_LEFT
6623 Xpush_bomb_e, FALSE, FALSE,
6624 EL_BOMB, -1, MV_BIT_RIGHT
6627 Xpush_bomb_w, FALSE, FALSE,
6628 EL_BOMB, -1, MV_BIT_LEFT
6631 Xpush_nut_e, FALSE, FALSE,
6632 EL_NUT, -1, MV_BIT_RIGHT
6635 Xpush_nut_w, FALSE, FALSE,
6636 EL_NUT, -1, MV_BIT_LEFT
6639 Xpush_spring_e, FALSE, FALSE,
6640 EL_SPRING_RIGHT, -1, MV_BIT_RIGHT
6643 Xpush_spring_w, FALSE, FALSE,
6644 EL_SPRING_LEFT, -1, MV_BIT_LEFT
6648 Xdynamite, TRUE, FALSE,
6649 EL_EM_DYNAMITE, -1, -1
6652 Ydynamite_blank, FALSE, FALSE,
6653 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6656 Xdynamite_1, TRUE, FALSE,
6657 EL_EM_DYNAMITE_ACTIVE, -1, -1
6660 Xdynamite_2, FALSE, FALSE,
6661 EL_EM_DYNAMITE_ACTIVE, -1, -1
6664 Xdynamite_3, FALSE, FALSE,
6665 EL_EM_DYNAMITE_ACTIVE, -1, -1
6668 Xdynamite_4, FALSE, FALSE,
6669 EL_EM_DYNAMITE_ACTIVE, -1, -1
6673 Xkey_1, TRUE, FALSE,
6677 Xkey_2, TRUE, FALSE,
6681 Xkey_3, TRUE, FALSE,
6685 Xkey_4, TRUE, FALSE,
6689 Xkey_5, TRUE, FALSE,
6690 EL_EMC_KEY_5, -1, -1
6693 Xkey_6, TRUE, FALSE,
6694 EL_EMC_KEY_6, -1, -1
6697 Xkey_7, TRUE, FALSE,
6698 EL_EMC_KEY_7, -1, -1
6701 Xkey_8, TRUE, FALSE,
6702 EL_EMC_KEY_8, -1, -1
6706 Xdoor_1, TRUE, FALSE,
6707 EL_EM_GATE_1, -1, -1
6710 Xdoor_2, TRUE, FALSE,
6711 EL_EM_GATE_2, -1, -1
6714 Xdoor_3, TRUE, FALSE,
6715 EL_EM_GATE_3, -1, -1
6718 Xdoor_4, TRUE, FALSE,
6719 EL_EM_GATE_4, -1, -1
6722 Xdoor_5, TRUE, FALSE,
6723 EL_EMC_GATE_5, -1, -1
6726 Xdoor_6, TRUE, FALSE,
6727 EL_EMC_GATE_6, -1, -1
6730 Xdoor_7, TRUE, FALSE,
6731 EL_EMC_GATE_7, -1, -1
6734 Xdoor_8, TRUE, FALSE,
6735 EL_EMC_GATE_8, -1, -1
6739 Xfake_door_1, TRUE, FALSE,
6740 EL_EM_GATE_1_GRAY, -1, -1
6743 Xfake_door_2, TRUE, FALSE,
6744 EL_EM_GATE_2_GRAY, -1, -1
6747 Xfake_door_3, TRUE, FALSE,
6748 EL_EM_GATE_3_GRAY, -1, -1
6751 Xfake_door_4, TRUE, FALSE,
6752 EL_EM_GATE_4_GRAY, -1, -1
6755 Xfake_door_5, TRUE, FALSE,
6756 EL_EMC_GATE_5_GRAY, -1, -1
6759 Xfake_door_6, TRUE, FALSE,
6760 EL_EMC_GATE_6_GRAY, -1, -1
6763 Xfake_door_7, TRUE, FALSE,
6764 EL_EMC_GATE_7_GRAY, -1, -1
6767 Xfake_door_8, TRUE, FALSE,
6768 EL_EMC_GATE_8_GRAY, -1, -1
6772 Xballoon, TRUE, FALSE,
6776 Yballoon_n, FALSE, FALSE,
6777 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6780 Yballoon_nB, FALSE, TRUE,
6781 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6784 Yballoon_e, FALSE, FALSE,
6785 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6788 Yballoon_eB, FALSE, TRUE,
6789 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6792 Yballoon_s, FALSE, FALSE,
6793 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6796 Yballoon_sB, FALSE, TRUE,
6797 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6800 Yballoon_w, FALSE, FALSE,
6801 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6804 Yballoon_wB, FALSE, TRUE,
6805 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6809 Xball_1, TRUE, FALSE,
6810 EL_EMC_MAGIC_BALL, -1, -1
6813 Yball_1, FALSE, FALSE,
6814 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6817 Xball_2, FALSE, FALSE,
6818 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6821 Yball_2, FALSE, FALSE,
6822 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6825 Yball_blank, FALSE, FALSE,
6826 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6830 Xamoeba_1, TRUE, FALSE,
6831 EL_AMOEBA_DRY, ACTION_OTHER, -1
6834 Xamoeba_2, FALSE, FALSE,
6835 EL_AMOEBA_DRY, ACTION_OTHER, -1
6838 Xamoeba_3, FALSE, FALSE,
6839 EL_AMOEBA_DRY, ACTION_OTHER, -1
6842 Xamoeba_4, FALSE, FALSE,
6843 EL_AMOEBA_DRY, ACTION_OTHER, -1
6846 Xamoeba_5, TRUE, FALSE,
6847 EL_AMOEBA_WET, ACTION_OTHER, -1
6850 Xamoeba_6, FALSE, FALSE,
6851 EL_AMOEBA_WET, ACTION_OTHER, -1
6854 Xamoeba_7, FALSE, FALSE,
6855 EL_AMOEBA_WET, ACTION_OTHER, -1
6858 Xamoeba_8, FALSE, FALSE,
6859 EL_AMOEBA_WET, ACTION_OTHER, -1
6864 EL_AMOEBA_DROP, ACTION_GROWING, -1
6867 Xdrip_fall, FALSE, FALSE,
6868 EL_AMOEBA_DROP, -1, -1
6871 Xdrip_stretch, FALSE, FALSE,
6872 EL_AMOEBA_DROP, ACTION_FALLING, -1
6875 Xdrip_stretchB, FALSE, TRUE,
6876 EL_AMOEBA_DROP, ACTION_FALLING, -1
6879 Ydrip_1_s, FALSE, FALSE,
6880 EL_AMOEBA_DROP, ACTION_FALLING, -1
6883 Ydrip_1_sB, FALSE, TRUE,
6884 EL_AMOEBA_DROP, ACTION_FALLING, -1
6887 Ydrip_2_s, FALSE, FALSE,
6888 EL_AMOEBA_DROP, ACTION_FALLING, -1
6891 Ydrip_2_sB, FALSE, TRUE,
6892 EL_AMOEBA_DROP, ACTION_FALLING, -1
6896 Xwonderwall, TRUE, FALSE,
6897 EL_MAGIC_WALL, -1, -1
6900 Ywonderwall, FALSE, FALSE,
6901 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6905 Xwheel, TRUE, FALSE,
6906 EL_ROBOT_WHEEL, -1, -1
6909 Ywheel, FALSE, FALSE,
6910 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6914 Xswitch, TRUE, FALSE,
6915 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6918 Yswitch, FALSE, FALSE,
6919 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6923 Xbumper, TRUE, FALSE,
6924 EL_EMC_SPRING_BUMPER, -1, -1
6927 Ybumper, FALSE, FALSE,
6928 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6932 Xacid_nw, TRUE, FALSE,
6933 EL_ACID_POOL_TOPLEFT, -1, -1
6936 Xacid_ne, TRUE, FALSE,
6937 EL_ACID_POOL_TOPRIGHT, -1, -1
6940 Xacid_sw, TRUE, FALSE,
6941 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6944 Xacid_s, TRUE, FALSE,
6945 EL_ACID_POOL_BOTTOM, -1, -1
6948 Xacid_se, TRUE, FALSE,
6949 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6953 Xfake_blank, TRUE, FALSE,
6954 EL_INVISIBLE_WALL, -1, -1
6957 Yfake_blank, FALSE, FALSE,
6958 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6962 Xfake_grass, TRUE, FALSE,
6963 EL_EMC_FAKE_GRASS, -1, -1
6966 Yfake_grass, FALSE, FALSE,
6967 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6971 Xfake_amoeba, TRUE, FALSE,
6972 EL_EMC_DRIPPER, -1, -1
6975 Yfake_amoeba, FALSE, FALSE,
6976 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6980 Xlenses, TRUE, FALSE,
6981 EL_EMC_LENSES, -1, -1
6985 Xmagnify, TRUE, FALSE,
6986 EL_EMC_MAGNIFIER, -1, -1
6991 EL_QUICKSAND_EMPTY, -1, -1
6994 Xsand_stone, TRUE, FALSE,
6995 EL_QUICKSAND_FULL, -1, -1
6998 Xsand_stonein_1, FALSE, TRUE,
6999 EL_ROCK, ACTION_FILLING, -1
7002 Xsand_stonein_2, FALSE, TRUE,
7003 EL_ROCK, ACTION_FILLING, -1
7006 Xsand_stonein_3, FALSE, TRUE,
7007 EL_ROCK, ACTION_FILLING, -1
7010 Xsand_stonein_4, FALSE, TRUE,
7011 EL_ROCK, ACTION_FILLING, -1
7014 Xsand_sandstone_1, FALSE, FALSE,
7015 EL_QUICKSAND_FILLING, -1, -1
7018 Xsand_sandstone_2, FALSE, FALSE,
7019 EL_QUICKSAND_FILLING, -1, -1
7022 Xsand_sandstone_3, FALSE, FALSE,
7023 EL_QUICKSAND_FILLING, -1, -1
7026 Xsand_sandstone_4, FALSE, FALSE,
7027 EL_QUICKSAND_FILLING, -1, -1
7030 Xsand_stonesand_1, FALSE, FALSE,
7031 EL_QUICKSAND_EMPTYING, -1, -1
7034 Xsand_stonesand_2, FALSE, FALSE,
7035 EL_QUICKSAND_EMPTYING, -1, -1
7038 Xsand_stonesand_3, FALSE, FALSE,
7039 EL_QUICKSAND_EMPTYING, -1, -1
7042 Xsand_stonesand_4, FALSE, FALSE,
7043 EL_QUICKSAND_EMPTYING, -1, -1
7046 Xsand_stoneout_1, FALSE, FALSE,
7047 EL_ROCK, ACTION_EMPTYING, -1
7050 Xsand_stoneout_2, FALSE, FALSE,
7051 EL_ROCK, ACTION_EMPTYING, -1
7054 Xsand_stonesand_quickout_1, FALSE, FALSE,
7055 EL_QUICKSAND_EMPTYING, -1, -1
7058 Xsand_stonesand_quickout_2, FALSE, FALSE,
7059 EL_QUICKSAND_EMPTYING, -1, -1
7063 Xslide_ns, TRUE, FALSE,
7064 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7067 Yslide_ns_blank, FALSE, FALSE,
7068 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7071 Xslide_ew, TRUE, FALSE,
7072 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7075 Yslide_ew_blank, FALSE, FALSE,
7076 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7080 Xwind_n, TRUE, FALSE,
7081 EL_BALLOON_SWITCH_UP, -1, -1
7084 Xwind_e, TRUE, FALSE,
7085 EL_BALLOON_SWITCH_RIGHT, -1, -1
7088 Xwind_s, TRUE, FALSE,
7089 EL_BALLOON_SWITCH_DOWN, -1, -1
7092 Xwind_w, TRUE, FALSE,
7093 EL_BALLOON_SWITCH_LEFT, -1, -1
7096 Xwind_any, TRUE, FALSE,
7097 EL_BALLOON_SWITCH_ANY, -1, -1
7100 Xwind_stop, TRUE, FALSE,
7101 EL_BALLOON_SWITCH_NONE, -1, -1
7106 EL_EM_EXIT_CLOSED, -1, -1
7109 Xexit_1, TRUE, FALSE,
7110 EL_EM_EXIT_OPEN, -1, -1
7113 Xexit_2, FALSE, FALSE,
7114 EL_EM_EXIT_OPEN, -1, -1
7117 Xexit_3, FALSE, FALSE,
7118 EL_EM_EXIT_OPEN, -1, -1
7122 Xpause, FALSE, FALSE,
7127 Xwall_1, TRUE, FALSE,
7131 Xwall_2, TRUE, FALSE,
7132 EL_EMC_WALL_14, -1, -1
7135 Xwall_3, TRUE, FALSE,
7136 EL_EMC_WALL_15, -1, -1
7139 Xwall_4, TRUE, FALSE,
7140 EL_EMC_WALL_16, -1, -1
7144 Xroundwall_1, TRUE, FALSE,
7145 EL_WALL_SLIPPERY, -1, -1
7148 Xroundwall_2, TRUE, FALSE,
7149 EL_EMC_WALL_SLIPPERY_2, -1, -1
7152 Xroundwall_3, TRUE, FALSE,
7153 EL_EMC_WALL_SLIPPERY_3, -1, -1
7156 Xroundwall_4, TRUE, FALSE,
7157 EL_EMC_WALL_SLIPPERY_4, -1, -1
7161 Xsteel_1, TRUE, FALSE,
7162 EL_STEELWALL, -1, -1
7165 Xsteel_2, TRUE, FALSE,
7166 EL_EMC_STEELWALL_2, -1, -1
7169 Xsteel_3, TRUE, FALSE,
7170 EL_EMC_STEELWALL_3, -1, -1
7173 Xsteel_4, TRUE, FALSE,
7174 EL_EMC_STEELWALL_4, -1, -1
7178 Xdecor_1, TRUE, FALSE,
7179 EL_EMC_WALL_8, -1, -1
7182 Xdecor_2, TRUE, FALSE,
7183 EL_EMC_WALL_6, -1, -1
7186 Xdecor_3, TRUE, FALSE,
7187 EL_EMC_WALL_4, -1, -1
7190 Xdecor_4, TRUE, FALSE,
7191 EL_EMC_WALL_7, -1, -1
7194 Xdecor_5, TRUE, FALSE,
7195 EL_EMC_WALL_5, -1, -1
7198 Xdecor_6, TRUE, FALSE,
7199 EL_EMC_WALL_9, -1, -1
7202 Xdecor_7, TRUE, FALSE,
7203 EL_EMC_WALL_10, -1, -1
7206 Xdecor_8, TRUE, FALSE,
7207 EL_EMC_WALL_1, -1, -1
7210 Xdecor_9, TRUE, FALSE,
7211 EL_EMC_WALL_2, -1, -1
7214 Xdecor_10, TRUE, FALSE,
7215 EL_EMC_WALL_3, -1, -1
7218 Xdecor_11, TRUE, FALSE,
7219 EL_EMC_WALL_11, -1, -1
7222 Xdecor_12, TRUE, FALSE,
7223 EL_EMC_WALL_12, -1, -1
7227 Xalpha_0, TRUE, FALSE,
7228 EL_CHAR('0'), -1, -1
7231 Xalpha_1, TRUE, FALSE,
7232 EL_CHAR('1'), -1, -1
7235 Xalpha_2, TRUE, FALSE,
7236 EL_CHAR('2'), -1, -1
7239 Xalpha_3, TRUE, FALSE,
7240 EL_CHAR('3'), -1, -1
7243 Xalpha_4, TRUE, FALSE,
7244 EL_CHAR('4'), -1, -1
7247 Xalpha_5, TRUE, FALSE,
7248 EL_CHAR('5'), -1, -1
7251 Xalpha_6, TRUE, FALSE,
7252 EL_CHAR('6'), -1, -1
7255 Xalpha_7, TRUE, FALSE,
7256 EL_CHAR('7'), -1, -1
7259 Xalpha_8, TRUE, FALSE,
7260 EL_CHAR('8'), -1, -1
7263 Xalpha_9, TRUE, FALSE,
7264 EL_CHAR('9'), -1, -1
7267 Xalpha_excla, TRUE, FALSE,
7268 EL_CHAR('!'), -1, -1
7271 Xalpha_apost, TRUE, FALSE,
7272 EL_CHAR('\''), -1, -1
7275 Xalpha_comma, TRUE, FALSE,
7276 EL_CHAR(','), -1, -1
7279 Xalpha_minus, TRUE, FALSE,
7280 EL_CHAR('-'), -1, -1
7283 Xalpha_perio, TRUE, FALSE,
7284 EL_CHAR('.'), -1, -1
7287 Xalpha_colon, TRUE, FALSE,
7288 EL_CHAR(':'), -1, -1
7291 Xalpha_quest, TRUE, FALSE,
7292 EL_CHAR('?'), -1, -1
7295 Xalpha_a, TRUE, FALSE,
7296 EL_CHAR('A'), -1, -1
7299 Xalpha_b, TRUE, FALSE,
7300 EL_CHAR('B'), -1, -1
7303 Xalpha_c, TRUE, FALSE,
7304 EL_CHAR('C'), -1, -1
7307 Xalpha_d, TRUE, FALSE,
7308 EL_CHAR('D'), -1, -1
7311 Xalpha_e, TRUE, FALSE,
7312 EL_CHAR('E'), -1, -1
7315 Xalpha_f, TRUE, FALSE,
7316 EL_CHAR('F'), -1, -1
7319 Xalpha_g, TRUE, FALSE,
7320 EL_CHAR('G'), -1, -1
7323 Xalpha_h, TRUE, FALSE,
7324 EL_CHAR('H'), -1, -1
7327 Xalpha_i, TRUE, FALSE,
7328 EL_CHAR('I'), -1, -1
7331 Xalpha_j, TRUE, FALSE,
7332 EL_CHAR('J'), -1, -1
7335 Xalpha_k, TRUE, FALSE,
7336 EL_CHAR('K'), -1, -1
7339 Xalpha_l, TRUE, FALSE,
7340 EL_CHAR('L'), -1, -1
7343 Xalpha_m, TRUE, FALSE,
7344 EL_CHAR('M'), -1, -1
7347 Xalpha_n, TRUE, FALSE,
7348 EL_CHAR('N'), -1, -1
7351 Xalpha_o, TRUE, FALSE,
7352 EL_CHAR('O'), -1, -1
7355 Xalpha_p, TRUE, FALSE,
7356 EL_CHAR('P'), -1, -1
7359 Xalpha_q, TRUE, FALSE,
7360 EL_CHAR('Q'), -1, -1
7363 Xalpha_r, TRUE, FALSE,
7364 EL_CHAR('R'), -1, -1
7367 Xalpha_s, TRUE, FALSE,
7368 EL_CHAR('S'), -1, -1
7371 Xalpha_t, TRUE, FALSE,
7372 EL_CHAR('T'), -1, -1
7375 Xalpha_u, TRUE, FALSE,
7376 EL_CHAR('U'), -1, -1
7379 Xalpha_v, TRUE, FALSE,
7380 EL_CHAR('V'), -1, -1
7383 Xalpha_w, TRUE, FALSE,
7384 EL_CHAR('W'), -1, -1
7387 Xalpha_x, TRUE, FALSE,
7388 EL_CHAR('X'), -1, -1
7391 Xalpha_y, TRUE, FALSE,
7392 EL_CHAR('Y'), -1, -1
7395 Xalpha_z, TRUE, FALSE,
7396 EL_CHAR('Z'), -1, -1
7399 Xalpha_arrow_e, TRUE, FALSE,
7400 EL_CHAR('>'), -1, -1
7403 Xalpha_arrow_w, TRUE, FALSE,
7404 EL_CHAR('<'), -1, -1
7407 Xalpha_copyr, TRUE, FALSE,
7408 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7412 Ykey_1_blank, FALSE, FALSE,
7413 EL_EM_KEY_1, ACTION_COLLECTING, -1
7416 Ykey_2_blank, FALSE, FALSE,
7417 EL_EM_KEY_2, ACTION_COLLECTING, -1
7420 Ykey_3_blank, FALSE, FALSE,
7421 EL_EM_KEY_3, ACTION_COLLECTING, -1
7424 Ykey_4_blank, FALSE, FALSE,
7425 EL_EM_KEY_4, ACTION_COLLECTING, -1
7428 Ykey_5_blank, FALSE, FALSE,
7429 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7432 Ykey_6_blank, FALSE, FALSE,
7433 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7436 Ykey_7_blank, FALSE, FALSE,
7437 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7440 Ykey_8_blank, FALSE, FALSE,
7441 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7444 Ylenses_blank, FALSE, FALSE,
7445 EL_EMC_LENSES, ACTION_COLLECTING, -1
7448 Ymagnify_blank, FALSE, FALSE,
7449 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7452 Ygrass_blank, FALSE, FALSE,
7453 EL_EMC_GRASS, ACTION_SNAPPING, -1
7456 Ydirt_blank, FALSE, FALSE,
7457 EL_SAND, ACTION_SNAPPING, -1
7466 static struct Mapping_EM_to_RND_player
7475 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7479 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7483 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7487 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7491 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7495 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7499 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7503 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7507 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7511 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7515 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7519 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7523 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7527 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7531 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7535 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7539 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7543 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7547 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7551 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7555 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7559 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7563 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7567 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7571 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7575 EL_PLAYER_1, ACTION_DEFAULT, -1,
7579 EL_PLAYER_2, ACTION_DEFAULT, -1,
7583 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7587 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7591 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7595 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7599 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7603 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7607 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7611 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7615 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7619 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7623 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7627 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7631 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7635 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7639 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7643 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7647 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7651 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7655 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7659 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7663 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7667 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7671 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7675 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7679 EL_PLAYER_3, ACTION_DEFAULT, -1,
7683 EL_PLAYER_4, ACTION_DEFAULT, -1,
7692 int map_element_RND_to_EM_cave(int element_rnd)
7694 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7695 static boolean mapping_initialized = FALSE;
7697 if (!mapping_initialized)
7701 // return "Xalpha_quest" for all undefined elements in mapping array
7702 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7703 mapping_RND_to_EM[i] = Xalpha_quest;
7705 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7706 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7707 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7708 em_object_mapping_list[i].element_em;
7710 mapping_initialized = TRUE;
7713 if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
7715 Warn("invalid RND level element %d", element_rnd);
7720 return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
7723 int map_element_EM_to_RND_cave(int element_em_cave)
7725 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7726 static boolean mapping_initialized = FALSE;
7728 if (!mapping_initialized)
7732 // return "EL_UNKNOWN" for all undefined elements in mapping array
7733 for (i = 0; i < GAME_TILE_MAX; i++)
7734 mapping_EM_to_RND[i] = EL_UNKNOWN;
7736 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7737 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7738 em_object_mapping_list[i].element_rnd;
7740 mapping_initialized = TRUE;
7743 if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
7745 Warn("invalid EM cave element %d", element_em_cave);
7750 return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
7753 int map_element_EM_to_RND_game(int element_em_game)
7755 static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
7756 static boolean mapping_initialized = FALSE;
7758 if (!mapping_initialized)
7762 // return "EL_UNKNOWN" for all undefined elements in mapping array
7763 for (i = 0; i < GAME_TILE_MAX; i++)
7764 mapping_EM_to_RND[i] = EL_UNKNOWN;
7766 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7767 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7768 em_object_mapping_list[i].element_rnd;
7770 mapping_initialized = TRUE;
7773 if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
7775 Warn("invalid EM game element %d", element_em_game);
7780 return mapping_EM_to_RND[element_em_game];
7783 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7785 struct LevelInfo_EM *level_em = level->native_em_level;
7786 struct CAVE *cav = level_em->cav;
7789 for (i = 0; i < GAME_TILE_MAX; i++)
7790 cav->android_array[i] = Cblank;
7792 for (i = 0; i < level->num_android_clone_elements; i++)
7794 int element_rnd = level->android_clone_element[i];
7795 int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
7797 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7798 if (em_object_mapping_list[j].element_rnd == element_rnd)
7799 cav->android_array[em_object_mapping_list[j].element_em] =
7804 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7806 struct LevelInfo_EM *level_em = level->native_em_level;
7807 struct CAVE *cav = level_em->cav;
7810 level->num_android_clone_elements = 0;
7812 for (i = 0; i < GAME_TILE_MAX; i++)
7814 int element_em_cave = cav->android_array[i];
7816 boolean element_found = FALSE;
7818 if (element_em_cave == Cblank)
7821 element_rnd = map_element_EM_to_RND_cave(element_em_cave);
7823 for (j = 0; j < level->num_android_clone_elements; j++)
7824 if (level->android_clone_element[j] == element_rnd)
7825 element_found = TRUE;
7829 level->android_clone_element[level->num_android_clone_elements++] =
7832 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7837 if (level->num_android_clone_elements == 0)
7839 level->num_android_clone_elements = 1;
7840 level->android_clone_element[0] = EL_EMPTY;
7844 int map_direction_RND_to_EM(int direction)
7846 return (direction == MV_UP ? 0 :
7847 direction == MV_RIGHT ? 1 :
7848 direction == MV_DOWN ? 2 :
7849 direction == MV_LEFT ? 3 :
7853 int map_direction_EM_to_RND(int direction)
7855 return (direction == 0 ? MV_UP :
7856 direction == 1 ? MV_RIGHT :
7857 direction == 2 ? MV_DOWN :
7858 direction == 3 ? MV_LEFT :
7862 int map_element_RND_to_SP(int element_rnd)
7864 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7866 if (element_rnd >= EL_SP_START &&
7867 element_rnd <= EL_SP_END)
7868 element_sp = element_rnd - EL_SP_START;
7869 else if (element_rnd == EL_EMPTY_SPACE)
7871 else if (element_rnd == EL_INVISIBLE_WALL)
7877 int map_element_SP_to_RND(int element_sp)
7879 int element_rnd = EL_UNKNOWN;
7881 if (element_sp >= 0x00 &&
7883 element_rnd = EL_SP_START + element_sp;
7884 else if (element_sp == 0x28)
7885 element_rnd = EL_INVISIBLE_WALL;
7890 int map_action_SP_to_RND(int action_sp)
7894 case actActive: return ACTION_ACTIVE;
7895 case actImpact: return ACTION_IMPACT;
7896 case actExploding: return ACTION_EXPLODING;
7897 case actDigging: return ACTION_DIGGING;
7898 case actSnapping: return ACTION_SNAPPING;
7899 case actCollecting: return ACTION_COLLECTING;
7900 case actPassing: return ACTION_PASSING;
7901 case actPushing: return ACTION_PUSHING;
7902 case actDropping: return ACTION_DROPPING;
7904 default: return ACTION_DEFAULT;
7908 int map_element_RND_to_MM(int element_rnd)
7910 return (element_rnd >= EL_MM_START_1 &&
7911 element_rnd <= EL_MM_END_1 ?
7912 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7914 element_rnd >= EL_MM_START_2 &&
7915 element_rnd <= EL_MM_END_2 ?
7916 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7918 element_rnd >= EL_CHAR_START &&
7919 element_rnd <= EL_CHAR_END ?
7920 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7922 element_rnd >= EL_MM_RUNTIME_START &&
7923 element_rnd <= EL_MM_RUNTIME_END ?
7924 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7926 element_rnd >= EL_MM_DUMMY_START &&
7927 element_rnd <= EL_MM_DUMMY_END ?
7928 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7930 EL_MM_EMPTY_NATIVE);
7933 int map_element_MM_to_RND(int element_mm)
7935 return (element_mm == EL_MM_EMPTY_NATIVE ||
7936 element_mm == EL_DF_EMPTY_NATIVE ?
7939 element_mm >= EL_MM_START_1_NATIVE &&
7940 element_mm <= EL_MM_END_1_NATIVE ?
7941 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7943 element_mm >= EL_MM_START_2_NATIVE &&
7944 element_mm <= EL_MM_END_2_NATIVE ?
7945 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7947 element_mm >= EL_MM_CHAR_START_NATIVE &&
7948 element_mm <= EL_MM_CHAR_END_NATIVE ?
7949 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7951 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7952 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7953 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7955 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7956 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7957 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7962 int map_action_MM_to_RND(int action_mm)
7964 // all MM actions are defined to exactly match their RND counterparts
7968 int map_sound_MM_to_RND(int sound_mm)
7972 case SND_MM_GAME_LEVELTIME_CHARGING:
7973 return SND_GAME_LEVELTIME_CHARGING;
7975 case SND_MM_GAME_HEALTH_CHARGING:
7976 return SND_GAME_HEALTH_CHARGING;
7979 return SND_UNDEFINED;
7983 int map_mm_wall_element(int element)
7985 return (element >= EL_MM_STEEL_WALL_START &&
7986 element <= EL_MM_STEEL_WALL_END ?
7989 element >= EL_MM_WOODEN_WALL_START &&
7990 element <= EL_MM_WOODEN_WALL_END ?
7993 element >= EL_MM_ICE_WALL_START &&
7994 element <= EL_MM_ICE_WALL_END ?
7997 element >= EL_MM_AMOEBA_WALL_START &&
7998 element <= EL_MM_AMOEBA_WALL_END ?
8001 element >= EL_DF_STEEL_WALL_START &&
8002 element <= EL_DF_STEEL_WALL_END ?
8005 element >= EL_DF_WOODEN_WALL_START &&
8006 element <= EL_DF_WOODEN_WALL_END ?
8012 int map_mm_wall_element_editor(int element)
8016 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
8017 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
8018 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
8019 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
8020 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
8021 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
8023 default: return element;
8027 int get_next_element(int element)
8031 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8032 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8033 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8034 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8035 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8036 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8037 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8038 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8039 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8040 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8041 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8043 default: return element;
8047 int el2img_mm(int element_mm)
8049 return el2img(map_element_MM_to_RND(element_mm));
8052 int el_act_dir2img(int element, int action, int direction)
8054 element = GFX_ELEMENT(element);
8055 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8057 // direction_graphic[][] == graphic[] for undefined direction graphics
8058 return element_info[element].direction_graphic[action][direction];
8061 static int el_act_dir2crm(int element, int action, int direction)
8063 element = GFX_ELEMENT(element);
8064 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8066 // direction_graphic[][] == graphic[] for undefined direction graphics
8067 return element_info[element].direction_crumbled[action][direction];
8070 int el_act2img(int element, int action)
8072 element = GFX_ELEMENT(element);
8074 return element_info[element].graphic[action];
8077 int el_act2crm(int element, int action)
8079 element = GFX_ELEMENT(element);
8081 return element_info[element].crumbled[action];
8084 int el_dir2img(int element, int direction)
8086 element = GFX_ELEMENT(element);
8088 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8091 int el2baseimg(int element)
8093 return element_info[element].graphic[ACTION_DEFAULT];
8096 int el2img(int element)
8098 element = GFX_ELEMENT(element);
8100 return element_info[element].graphic[ACTION_DEFAULT];
8103 int el2edimg(int element)
8105 element = GFX_ELEMENT(element);
8107 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8110 int el2preimg(int element)
8112 element = GFX_ELEMENT(element);
8114 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8117 int el2panelimg(int element)
8119 element = GFX_ELEMENT(element);
8121 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8124 int font2baseimg(int font_nr)
8126 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8129 int getBeltNrFromBeltElement(int element)
8131 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8132 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8133 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8136 int getBeltNrFromBeltActiveElement(int element)
8138 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8139 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8140 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8143 int getBeltNrFromBeltSwitchElement(int element)
8145 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8146 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8147 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8150 int getBeltDirNrFromBeltElement(int element)
8152 static int belt_base_element[4] =
8154 EL_CONVEYOR_BELT_1_LEFT,
8155 EL_CONVEYOR_BELT_2_LEFT,
8156 EL_CONVEYOR_BELT_3_LEFT,
8157 EL_CONVEYOR_BELT_4_LEFT
8160 int belt_nr = getBeltNrFromBeltElement(element);
8161 int belt_dir_nr = element - belt_base_element[belt_nr];
8163 return (belt_dir_nr % 3);
8166 int getBeltDirNrFromBeltSwitchElement(int element)
8168 static int belt_base_element[4] =
8170 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8171 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8172 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8173 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8176 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8177 int belt_dir_nr = element - belt_base_element[belt_nr];
8179 return (belt_dir_nr % 3);
8182 int getBeltDirFromBeltElement(int element)
8184 static int belt_move_dir[3] =
8191 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8193 return belt_move_dir[belt_dir_nr];
8196 int getBeltDirFromBeltSwitchElement(int element)
8198 static int belt_move_dir[3] =
8205 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8207 return belt_move_dir[belt_dir_nr];
8210 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8212 static int belt_base_element[4] =
8214 EL_CONVEYOR_BELT_1_LEFT,
8215 EL_CONVEYOR_BELT_2_LEFT,
8216 EL_CONVEYOR_BELT_3_LEFT,
8217 EL_CONVEYOR_BELT_4_LEFT
8220 return belt_base_element[belt_nr] + belt_dir_nr;
8223 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8225 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8227 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8230 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8232 static int belt_base_element[4] =
8234 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8235 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8236 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8237 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8240 return belt_base_element[belt_nr] + belt_dir_nr;
8243 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8245 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8247 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8250 boolean swapTiles_EM(boolean is_pre_emc_cave)
8252 return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8255 boolean getTeamMode_EM(void)
8257 return game.team_mode || network_playing;
8260 boolean isActivePlayer_EM(int player_nr)
8262 return stored_player[player_nr].active;
8265 unsigned int InitRND(int seed)
8267 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8268 return InitEngineRandom_EM(seed);
8269 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8270 return InitEngineRandom_SP(seed);
8271 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8272 return InitEngineRandom_MM(seed);
8274 return InitEngineRandom_RND(seed);
8277 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8278 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8280 static int get_effective_element_EM(int tile, int frame_em)
8282 int element = object_mapping[tile].element_rnd;
8283 int action = object_mapping[tile].action;
8284 boolean is_backside = object_mapping[tile].is_backside;
8285 boolean action_removing = (action == ACTION_DIGGING ||
8286 action == ACTION_SNAPPING ||
8287 action == ACTION_COLLECTING);
8295 return (frame_em > 5 ? EL_EMPTY : element);
8301 else // frame_em == 7
8312 case Ydiamond_stone:
8316 case Xdrip_stretchB:
8332 case Ymagnify_blank:
8335 case Xsand_stonein_1:
8336 case Xsand_stonein_2:
8337 case Xsand_stonein_3:
8338 case Xsand_stonein_4:
8342 return (is_backside || action_removing ? EL_EMPTY : element);
8347 static boolean check_linear_animation_EM(int tile)
8351 case Xsand_stonesand_1:
8352 case Xsand_stonesand_quickout_1:
8353 case Xsand_sandstone_1:
8354 case Xsand_stonein_1:
8355 case Xsand_stoneout_1:
8383 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8384 boolean has_crumbled_graphics,
8385 int crumbled, int sync_frame)
8387 // if element can be crumbled, but certain action graphics are just empty
8388 // space (like instantly snapping sand to empty space in 1 frame), do not
8389 // treat these empty space graphics as crumbled graphics in EMC engine
8390 if (crumbled == IMG_EMPTY_SPACE)
8391 has_crumbled_graphics = FALSE;
8393 if (has_crumbled_graphics)
8395 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8396 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8397 g_crumbled->anim_delay,
8398 g_crumbled->anim_mode,
8399 g_crumbled->anim_start_frame,
8402 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8403 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8405 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8406 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8408 g_em->has_crumbled_graphics = TRUE;
8412 g_em->crumbled_bitmap = NULL;
8413 g_em->crumbled_src_x = 0;
8414 g_em->crumbled_src_y = 0;
8415 g_em->crumbled_border_size = 0;
8416 g_em->crumbled_tile_size = 0;
8418 g_em->has_crumbled_graphics = FALSE;
8423 void ResetGfxAnimation_EM(int x, int y, int tile)
8429 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8430 int tile, int frame_em, int x, int y)
8432 int action = object_mapping[tile].action;
8433 int direction = object_mapping[tile].direction;
8434 int effective_element = get_effective_element_EM(tile, frame_em);
8435 int graphic = (direction == MV_NONE ?
8436 el_act2img(effective_element, action) :
8437 el_act_dir2img(effective_element, action, direction));
8438 struct GraphicInfo *g = &graphic_info[graphic];
8440 boolean action_removing = (action == ACTION_DIGGING ||
8441 action == ACTION_SNAPPING ||
8442 action == ACTION_COLLECTING);
8443 boolean action_moving = (action == ACTION_FALLING ||
8444 action == ACTION_MOVING ||
8445 action == ACTION_PUSHING ||
8446 action == ACTION_EATING ||
8447 action == ACTION_FILLING ||
8448 action == ACTION_EMPTYING);
8449 boolean action_falling = (action == ACTION_FALLING ||
8450 action == ACTION_FILLING ||
8451 action == ACTION_EMPTYING);
8453 // special case: graphic uses "2nd movement tile" and has defined
8454 // 7 frames for movement animation (or less) => use default graphic
8455 // for last (8th) frame which ends the movement animation
8456 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8458 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8459 graphic = (direction == MV_NONE ?
8460 el_act2img(effective_element, action) :
8461 el_act_dir2img(effective_element, action, direction));
8463 g = &graphic_info[graphic];
8466 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8470 else if (action_moving)
8472 boolean is_backside = object_mapping[tile].is_backside;
8476 int direction = object_mapping[tile].direction;
8477 int move_dir = (action_falling ? MV_DOWN : direction);
8482 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8483 if (g->double_movement && frame_em == 0)
8487 if (move_dir == MV_LEFT)
8488 GfxFrame[x - 1][y] = GfxFrame[x][y];
8489 else if (move_dir == MV_RIGHT)
8490 GfxFrame[x + 1][y] = GfxFrame[x][y];
8491 else if (move_dir == MV_UP)
8492 GfxFrame[x][y - 1] = GfxFrame[x][y];
8493 else if (move_dir == MV_DOWN)
8494 GfxFrame[x][y + 1] = GfxFrame[x][y];
8501 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8502 if (tile == Xsand_stonesand_quickout_1 ||
8503 tile == Xsand_stonesand_quickout_2)
8507 if (graphic_info[graphic].anim_global_sync)
8508 sync_frame = FrameCounter;
8509 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8510 sync_frame = GfxFrame[x][y];
8512 sync_frame = 0; // playfield border (pseudo steel)
8514 SetRandomAnimationValue(x, y);
8516 int frame = getAnimationFrame(g->anim_frames,
8519 g->anim_start_frame,
8522 g_em->unique_identifier =
8523 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8526 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8527 int tile, int frame_em, int x, int y)
8529 int action = object_mapping[tile].action;
8530 int direction = object_mapping[tile].direction;
8531 boolean is_backside = object_mapping[tile].is_backside;
8532 int effective_element = get_effective_element_EM(tile, frame_em);
8533 int effective_action = action;
8534 int graphic = (direction == MV_NONE ?
8535 el_act2img(effective_element, effective_action) :
8536 el_act_dir2img(effective_element, effective_action,
8538 int crumbled = (direction == MV_NONE ?
8539 el_act2crm(effective_element, effective_action) :
8540 el_act_dir2crm(effective_element, effective_action,
8542 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8543 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8544 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8545 struct GraphicInfo *g = &graphic_info[graphic];
8548 // special case: graphic uses "2nd movement tile" and has defined
8549 // 7 frames for movement animation (or less) => use default graphic
8550 // for last (8th) frame which ends the movement animation
8551 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8553 effective_action = ACTION_DEFAULT;
8554 graphic = (direction == MV_NONE ?
8555 el_act2img(effective_element, effective_action) :
8556 el_act_dir2img(effective_element, effective_action,
8558 crumbled = (direction == MV_NONE ?
8559 el_act2crm(effective_element, effective_action) :
8560 el_act_dir2crm(effective_element, effective_action,
8563 g = &graphic_info[graphic];
8566 if (graphic_info[graphic].anim_global_sync)
8567 sync_frame = FrameCounter;
8568 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8569 sync_frame = GfxFrame[x][y];
8571 sync_frame = 0; // playfield border (pseudo steel)
8573 SetRandomAnimationValue(x, y);
8575 int frame = getAnimationFrame(g->anim_frames,
8578 g->anim_start_frame,
8581 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8582 g->double_movement && is_backside);
8584 // (updating the "crumbled" graphic definitions is probably not really needed,
8585 // as animations for crumbled graphics can't be longer than one EMC cycle)
8586 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8590 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8591 int player_nr, int anim, int frame_em)
8593 int element = player_mapping[player_nr][anim].element_rnd;
8594 int action = player_mapping[player_nr][anim].action;
8595 int direction = player_mapping[player_nr][anim].direction;
8596 int graphic = (direction == MV_NONE ?
8597 el_act2img(element, action) :
8598 el_act_dir2img(element, action, direction));
8599 struct GraphicInfo *g = &graphic_info[graphic];
8602 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8604 stored_player[player_nr].StepFrame = frame_em;
8606 sync_frame = stored_player[player_nr].Frame;
8608 int frame = getAnimationFrame(g->anim_frames,
8611 g->anim_start_frame,
8614 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8615 &g_em->src_x, &g_em->src_y, FALSE);
8618 void InitGraphicInfo_EM(void)
8622 // always start with reliable default values
8623 for (i = 0; i < GAME_TILE_MAX; i++)
8625 object_mapping[i].element_rnd = EL_UNKNOWN;
8626 object_mapping[i].is_backside = FALSE;
8627 object_mapping[i].action = ACTION_DEFAULT;
8628 object_mapping[i].direction = MV_NONE;
8631 // always start with reliable default values
8632 for (p = 0; p < MAX_PLAYERS; p++)
8634 for (i = 0; i < PLY_MAX; i++)
8636 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8637 player_mapping[p][i].action = ACTION_DEFAULT;
8638 player_mapping[p][i].direction = MV_NONE;
8642 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8644 int e = em_object_mapping_list[i].element_em;
8646 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8647 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8649 if (em_object_mapping_list[i].action != -1)
8650 object_mapping[e].action = em_object_mapping_list[i].action;
8652 if (em_object_mapping_list[i].direction != -1)
8653 object_mapping[e].direction =
8654 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8657 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8659 int a = em_player_mapping_list[i].action_em;
8660 int p = em_player_mapping_list[i].player_nr;
8662 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8664 if (em_player_mapping_list[i].action != -1)
8665 player_mapping[p][a].action = em_player_mapping_list[i].action;
8667 if (em_player_mapping_list[i].direction != -1)
8668 player_mapping[p][a].direction =
8669 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8672 for (i = 0; i < GAME_TILE_MAX; i++)
8674 int element = object_mapping[i].element_rnd;
8675 int action = object_mapping[i].action;
8676 int direction = object_mapping[i].direction;
8677 boolean is_backside = object_mapping[i].is_backside;
8678 boolean action_exploding = ((action == ACTION_EXPLODING ||
8679 action == ACTION_SMASHED_BY_ROCK ||
8680 action == ACTION_SMASHED_BY_SPRING) &&
8681 element != EL_DIAMOND);
8682 boolean action_active = (action == ACTION_ACTIVE);
8683 boolean action_other = (action == ACTION_OTHER);
8685 for (j = 0; j < 8; j++)
8687 int effective_element = get_effective_element_EM(i, j);
8688 int effective_action = (j < 7 ? action :
8689 i == Xdrip_stretch ? action :
8690 i == Xdrip_stretchB ? action :
8691 i == Ydrip_1_s ? action :
8692 i == Ydrip_1_sB ? action :
8693 i == Yball_1 ? action :
8694 i == Xball_2 ? action :
8695 i == Yball_2 ? action :
8696 i == Yball_blank ? action :
8697 i == Ykey_1_blank ? action :
8698 i == Ykey_2_blank ? action :
8699 i == Ykey_3_blank ? action :
8700 i == Ykey_4_blank ? action :
8701 i == Ykey_5_blank ? action :
8702 i == Ykey_6_blank ? action :
8703 i == Ykey_7_blank ? action :
8704 i == Ykey_8_blank ? action :
8705 i == Ylenses_blank ? action :
8706 i == Ymagnify_blank ? action :
8707 i == Ygrass_blank ? action :
8708 i == Ydirt_blank ? action :
8709 i == Xsand_stonein_1 ? action :
8710 i == Xsand_stonein_2 ? action :
8711 i == Xsand_stonein_3 ? action :
8712 i == Xsand_stonein_4 ? action :
8713 i == Xsand_stoneout_1 ? action :
8714 i == Xsand_stoneout_2 ? action :
8715 i == Xboom_android ? ACTION_EXPLODING :
8716 action_exploding ? ACTION_EXPLODING :
8717 action_active ? action :
8718 action_other ? action :
8720 int graphic = (el_act_dir2img(effective_element, effective_action,
8722 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8724 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8725 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8726 boolean has_action_graphics = (graphic != base_graphic);
8727 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8728 struct GraphicInfo *g = &graphic_info[graphic];
8729 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8732 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8733 boolean special_animation = (action != ACTION_DEFAULT &&
8734 g->anim_frames == 3 &&
8735 g->anim_delay == 2 &&
8736 g->anim_mode & ANIM_LINEAR);
8737 int sync_frame = (i == Xdrip_stretch ? 7 :
8738 i == Xdrip_stretchB ? 7 :
8739 i == Ydrip_2_s ? j + 8 :
8740 i == Ydrip_2_sB ? j + 8 :
8749 i == Xfake_acid_1 ? 0 :
8750 i == Xfake_acid_2 ? 10 :
8751 i == Xfake_acid_3 ? 20 :
8752 i == Xfake_acid_4 ? 30 :
8753 i == Xfake_acid_5 ? 40 :
8754 i == Xfake_acid_6 ? 50 :
8755 i == Xfake_acid_7 ? 60 :
8756 i == Xfake_acid_8 ? 70 :
8757 i == Xfake_acid_1_player ? 0 :
8758 i == Xfake_acid_2_player ? 10 :
8759 i == Xfake_acid_3_player ? 20 :
8760 i == Xfake_acid_4_player ? 30 :
8761 i == Xfake_acid_5_player ? 40 :
8762 i == Xfake_acid_6_player ? 50 :
8763 i == Xfake_acid_7_player ? 60 :
8764 i == Xfake_acid_8_player ? 70 :
8766 i == Yball_2 ? j + 8 :
8767 i == Yball_blank ? j + 1 :
8768 i == Ykey_1_blank ? j + 1 :
8769 i == Ykey_2_blank ? j + 1 :
8770 i == Ykey_3_blank ? j + 1 :
8771 i == Ykey_4_blank ? j + 1 :
8772 i == Ykey_5_blank ? j + 1 :
8773 i == Ykey_6_blank ? j + 1 :
8774 i == Ykey_7_blank ? j + 1 :
8775 i == Ykey_8_blank ? j + 1 :
8776 i == Ylenses_blank ? j + 1 :
8777 i == Ymagnify_blank ? j + 1 :
8778 i == Ygrass_blank ? j + 1 :
8779 i == Ydirt_blank ? j + 1 :
8780 i == Xamoeba_1 ? 0 :
8781 i == Xamoeba_2 ? 1 :
8782 i == Xamoeba_3 ? 2 :
8783 i == Xamoeba_4 ? 3 :
8784 i == Xamoeba_5 ? 0 :
8785 i == Xamoeba_6 ? 1 :
8786 i == Xamoeba_7 ? 2 :
8787 i == Xamoeba_8 ? 3 :
8788 i == Xexit_2 ? j + 8 :
8789 i == Xexit_3 ? j + 16 :
8790 i == Xdynamite_1 ? 0 :
8791 i == Xdynamite_2 ? 8 :
8792 i == Xdynamite_3 ? 16 :
8793 i == Xdynamite_4 ? 24 :
8794 i == Xsand_stonein_1 ? j + 1 :
8795 i == Xsand_stonein_2 ? j + 9 :
8796 i == Xsand_stonein_3 ? j + 17 :
8797 i == Xsand_stonein_4 ? j + 25 :
8798 i == Xsand_stoneout_1 && j == 0 ? 0 :
8799 i == Xsand_stoneout_1 && j == 1 ? 0 :
8800 i == Xsand_stoneout_1 && j == 2 ? 1 :
8801 i == Xsand_stoneout_1 && j == 3 ? 2 :
8802 i == Xsand_stoneout_1 && j == 4 ? 2 :
8803 i == Xsand_stoneout_1 && j == 5 ? 3 :
8804 i == Xsand_stoneout_1 && j == 6 ? 4 :
8805 i == Xsand_stoneout_1 && j == 7 ? 4 :
8806 i == Xsand_stoneout_2 && j == 0 ? 5 :
8807 i == Xsand_stoneout_2 && j == 1 ? 6 :
8808 i == Xsand_stoneout_2 && j == 2 ? 7 :
8809 i == Xsand_stoneout_2 && j == 3 ? 8 :
8810 i == Xsand_stoneout_2 && j == 4 ? 9 :
8811 i == Xsand_stoneout_2 && j == 5 ? 11 :
8812 i == Xsand_stoneout_2 && j == 6 ? 13 :
8813 i == Xsand_stoneout_2 && j == 7 ? 15 :
8814 i == Xboom_bug && j == 1 ? 2 :
8815 i == Xboom_bug && j == 2 ? 2 :
8816 i == Xboom_bug && j == 3 ? 4 :
8817 i == Xboom_bug && j == 4 ? 4 :
8818 i == Xboom_bug && j == 5 ? 2 :
8819 i == Xboom_bug && j == 6 ? 2 :
8820 i == Xboom_bug && j == 7 ? 0 :
8821 i == Xboom_tank && j == 1 ? 2 :
8822 i == Xboom_tank && j == 2 ? 2 :
8823 i == Xboom_tank && j == 3 ? 4 :
8824 i == Xboom_tank && j == 4 ? 4 :
8825 i == Xboom_tank && j == 5 ? 2 :
8826 i == Xboom_tank && j == 6 ? 2 :
8827 i == Xboom_tank && j == 7 ? 0 :
8828 i == Xboom_android && j == 7 ? 6 :
8829 i == Xboom_1 && j == 1 ? 2 :
8830 i == Xboom_1 && j == 2 ? 2 :
8831 i == Xboom_1 && j == 3 ? 4 :
8832 i == Xboom_1 && j == 4 ? 4 :
8833 i == Xboom_1 && j == 5 ? 6 :
8834 i == Xboom_1 && j == 6 ? 6 :
8835 i == Xboom_1 && j == 7 ? 8 :
8836 i == Xboom_2 && j == 0 ? 8 :
8837 i == Xboom_2 && j == 1 ? 8 :
8838 i == Xboom_2 && j == 2 ? 10 :
8839 i == Xboom_2 && j == 3 ? 10 :
8840 i == Xboom_2 && j == 4 ? 10 :
8841 i == Xboom_2 && j == 5 ? 12 :
8842 i == Xboom_2 && j == 6 ? 12 :
8843 i == Xboom_2 && j == 7 ? 12 :
8844 special_animation && j == 4 ? 3 :
8845 effective_action != action ? 0 :
8847 int frame = getAnimationFrame(g->anim_frames,
8850 g->anim_start_frame,
8853 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8854 g->double_movement && is_backside);
8856 g_em->bitmap = src_bitmap;
8857 g_em->src_x = src_x;
8858 g_em->src_y = src_y;
8859 g_em->src_offset_x = 0;
8860 g_em->src_offset_y = 0;
8861 g_em->dst_offset_x = 0;
8862 g_em->dst_offset_y = 0;
8863 g_em->width = TILEX;
8864 g_em->height = TILEY;
8866 g_em->preserve_background = FALSE;
8868 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8871 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8872 effective_action == ACTION_MOVING ||
8873 effective_action == ACTION_PUSHING ||
8874 effective_action == ACTION_EATING)) ||
8875 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8876 effective_action == ACTION_EMPTYING)))
8879 (effective_action == ACTION_FALLING ||
8880 effective_action == ACTION_FILLING ||
8881 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8882 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8883 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8884 int num_steps = (i == Ydrip_1_s ? 16 :
8885 i == Ydrip_1_sB ? 16 :
8886 i == Ydrip_2_s ? 16 :
8887 i == Ydrip_2_sB ? 16 :
8888 i == Xsand_stonein_1 ? 32 :
8889 i == Xsand_stonein_2 ? 32 :
8890 i == Xsand_stonein_3 ? 32 :
8891 i == Xsand_stonein_4 ? 32 :
8892 i == Xsand_stoneout_1 ? 16 :
8893 i == Xsand_stoneout_2 ? 16 : 8);
8894 int cx = ABS(dx) * (TILEX / num_steps);
8895 int cy = ABS(dy) * (TILEY / num_steps);
8896 int step_frame = (i == Ydrip_2_s ? j + 8 :
8897 i == Ydrip_2_sB ? j + 8 :
8898 i == Xsand_stonein_2 ? j + 8 :
8899 i == Xsand_stonein_3 ? j + 16 :
8900 i == Xsand_stonein_4 ? j + 24 :
8901 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8902 int step = (is_backside ? step_frame : num_steps - step_frame);
8904 if (is_backside) // tile where movement starts
8906 if (dx < 0 || dy < 0)
8908 g_em->src_offset_x = cx * step;
8909 g_em->src_offset_y = cy * step;
8913 g_em->dst_offset_x = cx * step;
8914 g_em->dst_offset_y = cy * step;
8917 else // tile where movement ends
8919 if (dx < 0 || dy < 0)
8921 g_em->dst_offset_x = cx * step;
8922 g_em->dst_offset_y = cy * step;
8926 g_em->src_offset_x = cx * step;
8927 g_em->src_offset_y = cy * step;
8931 g_em->width = TILEX - cx * step;
8932 g_em->height = TILEY - cy * step;
8935 // create unique graphic identifier to decide if tile must be redrawn
8936 /* bit 31 - 16 (16 bit): EM style graphic
8937 bit 15 - 12 ( 4 bit): EM style frame
8938 bit 11 - 6 ( 6 bit): graphic width
8939 bit 5 - 0 ( 6 bit): graphic height */
8940 g_em->unique_identifier =
8941 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8945 for (i = 0; i < GAME_TILE_MAX; i++)
8947 for (j = 0; j < 8; j++)
8949 int element = object_mapping[i].element_rnd;
8950 int action = object_mapping[i].action;
8951 int direction = object_mapping[i].direction;
8952 boolean is_backside = object_mapping[i].is_backside;
8953 int graphic_action = el_act_dir2img(element, action, direction);
8954 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8956 if ((action == ACTION_SMASHED_BY_ROCK ||
8957 action == ACTION_SMASHED_BY_SPRING ||
8958 action == ACTION_EATING) &&
8959 graphic_action == graphic_default)
8961 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8962 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8963 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8964 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8967 // no separate animation for "smashed by rock" -- use rock instead
8968 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
8969 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
8971 g_em->bitmap = g_xx->bitmap;
8972 g_em->src_x = g_xx->src_x;
8973 g_em->src_y = g_xx->src_y;
8974 g_em->src_offset_x = g_xx->src_offset_x;
8975 g_em->src_offset_y = g_xx->src_offset_y;
8976 g_em->dst_offset_x = g_xx->dst_offset_x;
8977 g_em->dst_offset_y = g_xx->dst_offset_y;
8978 g_em->width = g_xx->width;
8979 g_em->height = g_xx->height;
8980 g_em->unique_identifier = g_xx->unique_identifier;
8983 g_em->preserve_background = TRUE;
8988 for (p = 0; p < MAX_PLAYERS; p++)
8990 for (i = 0; i < PLY_MAX; i++)
8992 int element = player_mapping[p][i].element_rnd;
8993 int action = player_mapping[p][i].action;
8994 int direction = player_mapping[p][i].direction;
8996 for (j = 0; j < 8; j++)
8998 int effective_element = element;
8999 int effective_action = action;
9000 int graphic = (direction == MV_NONE ?
9001 el_act2img(effective_element, effective_action) :
9002 el_act_dir2img(effective_element, effective_action,
9004 struct GraphicInfo *g = &graphic_info[graphic];
9005 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9009 int frame = getAnimationFrame(g->anim_frames,
9012 g->anim_start_frame,
9015 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9017 g_em->bitmap = src_bitmap;
9018 g_em->src_x = src_x;
9019 g_em->src_y = src_y;
9020 g_em->src_offset_x = 0;
9021 g_em->src_offset_y = 0;
9022 g_em->dst_offset_x = 0;
9023 g_em->dst_offset_y = 0;
9024 g_em->width = TILEX;
9025 g_em->height = TILEY;
9031 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9032 boolean any_player_moving,
9033 boolean any_player_snapping,
9034 boolean any_player_dropping)
9036 if (frame == 7 && !any_player_dropping)
9038 if (!local_player->was_waiting)
9040 if (!CheckSaveEngineSnapshotToList())
9043 local_player->was_waiting = TRUE;
9046 else if (any_player_moving || any_player_snapping || any_player_dropping)
9048 local_player->was_waiting = FALSE;
9052 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9053 boolean murphy_is_dropping)
9055 if (murphy_is_waiting)
9057 if (!local_player->was_waiting)
9059 if (!CheckSaveEngineSnapshotToList())
9062 local_player->was_waiting = TRUE;
9067 local_player->was_waiting = FALSE;
9071 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9072 boolean button_released)
9074 if (button_released)
9076 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9077 CheckSaveEngineSnapshotToList();
9079 else if (element_clicked)
9081 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9082 CheckSaveEngineSnapshotToList();
9084 game.snapshot.changed_action = TRUE;
9088 boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9089 boolean any_player_moving,
9090 boolean any_player_snapping,
9091 boolean any_player_dropping)
9093 if (tape.single_step && tape.recording && !tape.pausing)
9094 if (frame == 7 && !any_player_dropping)
9095 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9097 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9098 any_player_snapping, any_player_dropping);
9100 return tape.pausing;
9103 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9104 boolean murphy_is_dropping)
9106 boolean murphy_starts_dropping = FALSE;
9109 for (i = 0; i < MAX_PLAYERS; i++)
9110 if (stored_player[i].force_dropping)
9111 murphy_starts_dropping = TRUE;
9113 if (tape.single_step && tape.recording && !tape.pausing)
9114 if (murphy_is_waiting && !murphy_starts_dropping)
9115 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9117 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9120 void CheckSingleStepMode_MM(boolean element_clicked,
9121 boolean button_released)
9123 if (tape.single_step && tape.recording && !tape.pausing)
9124 if (button_released)
9125 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9127 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9130 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9131 int graphic, int sync_frame, int x, int y)
9133 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9135 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9138 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9140 return (IS_NEXT_FRAME(sync_frame, graphic));
9143 int getGraphicInfo_Delay(int graphic)
9145 return graphic_info[graphic].anim_delay;
9148 void PlayMenuSoundExt(int sound)
9150 if (sound == SND_UNDEFINED)
9153 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9154 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9157 if (IS_LOOP_SOUND(sound))
9158 PlaySoundLoop(sound);
9163 void PlayMenuSound(void)
9165 PlayMenuSoundExt(menu.sound[game_status]);
9168 void PlayMenuSoundStereo(int sound, int stereo_position)
9170 if (sound == SND_UNDEFINED)
9173 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9174 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9177 if (IS_LOOP_SOUND(sound))
9178 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9180 PlaySoundStereo(sound, stereo_position);
9183 void PlayMenuSoundIfLoopExt(int sound)
9185 if (sound == SND_UNDEFINED)
9188 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9189 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9192 if (IS_LOOP_SOUND(sound))
9193 PlaySoundLoop(sound);
9196 void PlayMenuSoundIfLoop(void)
9198 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9201 void PlayMenuMusicExt(int music)
9203 if (music == MUS_UNDEFINED)
9206 if (!setup.sound_music)
9209 if (IS_LOOP_MUSIC(music))
9210 PlayMusicLoop(music);
9215 void PlayMenuMusic(void)
9217 char *curr_music = getCurrentlyPlayingMusicFilename();
9218 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9220 if (!strEqual(curr_music, next_music))
9221 PlayMenuMusicExt(menu.music[game_status]);
9224 void PlayMenuSoundsAndMusic(void)
9230 static void FadeMenuSounds(void)
9235 static void FadeMenuMusic(void)
9237 char *curr_music = getCurrentlyPlayingMusicFilename();
9238 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9240 if (!strEqual(curr_music, next_music))
9244 void FadeMenuSoundsAndMusic(void)
9250 void PlaySoundActivating(void)
9253 PlaySound(SND_MENU_ITEM_ACTIVATING);
9257 void PlaySoundSelecting(void)
9260 PlaySound(SND_MENU_ITEM_SELECTING);
9264 void ToggleFullscreenIfNeeded(void)
9266 // if setup and video fullscreen state are already matching, nothing do do
9267 if (setup.fullscreen == video.fullscreen_enabled ||
9268 !video.fullscreen_available)
9271 SDLSetWindowFullscreen(setup.fullscreen);
9273 // set setup value according to successfully changed fullscreen mode
9274 setup.fullscreen = video.fullscreen_enabled;
9277 void ChangeWindowScalingIfNeeded(void)
9279 // if setup and video window scaling are already matching, nothing do do
9280 if (setup.window_scaling_percent == video.window_scaling_percent ||
9281 video.fullscreen_enabled)
9284 SDLSetWindowScaling(setup.window_scaling_percent);
9286 // set setup value according to successfully changed window scaling
9287 setup.window_scaling_percent = video.window_scaling_percent;
9290 void ChangeVsyncModeIfNeeded(void)
9292 int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9293 int video_vsync_mode = video.vsync_mode;
9295 // if setup and video vsync mode are already matching, nothing do do
9296 if (setup_vsync_mode == video_vsync_mode)
9299 // if renderer is using OpenGL, vsync mode can directly be changed
9300 SDLSetScreenVsyncMode(setup.vsync_mode);
9302 // if vsync mode unchanged, try re-creating renderer to set vsync mode
9303 if (video.vsync_mode == video_vsync_mode)
9305 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9307 // save backbuffer content which gets lost when re-creating screen
9308 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9310 // force re-creating screen and renderer to set new vsync mode
9311 video.fullscreen_enabled = !setup.fullscreen;
9313 // when creating new renderer, destroy textures linked to old renderer
9314 FreeAllImageTextures(); // needs old renderer to free the textures
9316 // re-create screen and renderer (including change of vsync mode)
9317 ChangeVideoModeIfNeeded(setup.fullscreen);
9319 // set setup value according to successfully changed fullscreen mode
9320 setup.fullscreen = video.fullscreen_enabled;
9322 // restore backbuffer content from temporary backbuffer backup bitmap
9323 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9324 FreeBitmap(tmp_backbuffer);
9326 // update visible window/screen
9327 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9329 // when changing vsync mode, re-create textures for new renderer
9330 InitImageTextures();
9333 // set setup value according to successfully changed vsync mode
9334 setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9337 static void JoinRectangles(int *x, int *y, int *width, int *height,
9338 int x2, int y2, int width2, int height2)
9340 // do not join with "off-screen" rectangle
9341 if (x2 == -1 || y2 == -1)
9346 *width = MAX(*width, width2);
9347 *height = MAX(*height, height2);
9350 void SetAnimStatus(int anim_status_new)
9352 if (anim_status_new == GAME_MODE_MAIN)
9353 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9354 else if (anim_status_new == GAME_MODE_NAMES)
9355 anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9356 else if (anim_status_new == GAME_MODE_SCORES)
9357 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9359 global.anim_status_next = anim_status_new;
9361 // directly set screen modes that are entered without fading
9362 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9363 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9364 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9365 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9366 (global.anim_status == GAME_MODE_PSEUDO_NAMESONLY &&
9367 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9368 (global.anim_status == GAME_MODE_PSEUDO_TYPENAMES &&
9369 global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9370 global.anim_status = global.anim_status_next;
9373 void SetGameStatus(int game_status_new)
9375 if (game_status_new != game_status)
9376 game_status_last_screen = game_status;
9378 game_status = game_status_new;
9380 SetAnimStatus(game_status_new);
9383 void SetFontStatus(int game_status_new)
9385 static int last_game_status = -1;
9387 if (game_status_new != -1)
9389 // set game status for font use after storing last game status
9390 last_game_status = game_status;
9391 game_status = game_status_new;
9395 // reset game status after font use from last stored game status
9396 game_status = last_game_status;
9400 void ResetFontStatus(void)
9405 void SetLevelSetInfo(char *identifier, int level_nr)
9407 setString(&levelset.identifier, identifier);
9409 levelset.level_nr = level_nr;
9412 boolean CheckIfAllViewportsHaveChanged(void)
9414 // if game status has not changed, viewports have not changed either
9415 if (game_status == game_status_last)
9418 // check if all viewports have changed with current game status
9420 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9421 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9422 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9423 int new_real_sx = vp_playfield->x;
9424 int new_real_sy = vp_playfield->y;
9425 int new_full_sxsize = vp_playfield->width;
9426 int new_full_sysize = vp_playfield->height;
9427 int new_dx = vp_door_1->x;
9428 int new_dy = vp_door_1->y;
9429 int new_dxsize = vp_door_1->width;
9430 int new_dysize = vp_door_1->height;
9431 int new_vx = vp_door_2->x;
9432 int new_vy = vp_door_2->y;
9433 int new_vxsize = vp_door_2->width;
9434 int new_vysize = vp_door_2->height;
9436 boolean playfield_viewport_has_changed =
9437 (new_real_sx != REAL_SX ||
9438 new_real_sy != REAL_SY ||
9439 new_full_sxsize != FULL_SXSIZE ||
9440 new_full_sysize != FULL_SYSIZE);
9442 boolean door_1_viewport_has_changed =
9445 new_dxsize != DXSIZE ||
9446 new_dysize != DYSIZE);
9448 boolean door_2_viewport_has_changed =
9451 new_vxsize != VXSIZE ||
9452 new_vysize != VYSIZE ||
9453 game_status_last == GAME_MODE_EDITOR);
9455 return (playfield_viewport_has_changed &&
9456 door_1_viewport_has_changed &&
9457 door_2_viewport_has_changed);
9460 boolean CheckFadeAll(void)
9462 return (CheckIfGlobalBorderHasChanged() ||
9463 CheckIfAllViewportsHaveChanged());
9466 void ChangeViewportPropertiesIfNeeded(void)
9468 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9469 FALSE : setup.small_game_graphics);
9470 int gfx_game_mode = game_status;
9471 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9473 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9474 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9475 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9476 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9477 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9478 int new_win_xsize = vp_window->width;
9479 int new_win_ysize = vp_window->height;
9480 int border_left = vp_playfield->border_left;
9481 int border_right = vp_playfield->border_right;
9482 int border_top = vp_playfield->border_top;
9483 int border_bottom = vp_playfield->border_bottom;
9484 int new_sx = vp_playfield->x + border_left;
9485 int new_sy = vp_playfield->y + border_top;
9486 int new_sxsize = vp_playfield->width - border_left - border_right;
9487 int new_sysize = vp_playfield->height - border_top - border_bottom;
9488 int new_real_sx = vp_playfield->x;
9489 int new_real_sy = vp_playfield->y;
9490 int new_full_sxsize = vp_playfield->width;
9491 int new_full_sysize = vp_playfield->height;
9492 int new_dx = vp_door_1->x;
9493 int new_dy = vp_door_1->y;
9494 int new_dxsize = vp_door_1->width;
9495 int new_dysize = vp_door_1->height;
9496 int new_vx = vp_door_2->x;
9497 int new_vy = vp_door_2->y;
9498 int new_vxsize = vp_door_2->width;
9499 int new_vysize = vp_door_2->height;
9500 int new_ex = vp_door_3->x;
9501 int new_ey = vp_door_3->y;
9502 int new_exsize = vp_door_3->width;
9503 int new_eysize = vp_door_3->height;
9504 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9505 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9506 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9507 int new_scr_fieldx = new_sxsize / tilesize;
9508 int new_scr_fieldy = new_sysize / tilesize;
9509 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9510 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9511 boolean init_gfx_buffers = FALSE;
9512 boolean init_video_buffer = FALSE;
9513 boolean init_gadgets_and_anims = FALSE;
9514 boolean init_em_graphics = FALSE;
9516 if (new_win_xsize != WIN_XSIZE ||
9517 new_win_ysize != WIN_YSIZE)
9519 WIN_XSIZE = new_win_xsize;
9520 WIN_YSIZE = new_win_ysize;
9522 init_video_buffer = TRUE;
9523 init_gfx_buffers = TRUE;
9524 init_gadgets_and_anims = TRUE;
9526 // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9529 if (new_scr_fieldx != SCR_FIELDX ||
9530 new_scr_fieldy != SCR_FIELDY)
9532 // this always toggles between MAIN and GAME when using small tile size
9534 SCR_FIELDX = new_scr_fieldx;
9535 SCR_FIELDY = new_scr_fieldy;
9537 // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9548 new_sxsize != SXSIZE ||
9549 new_sysize != SYSIZE ||
9550 new_dxsize != DXSIZE ||
9551 new_dysize != DYSIZE ||
9552 new_vxsize != VXSIZE ||
9553 new_vysize != VYSIZE ||
9554 new_exsize != EXSIZE ||
9555 new_eysize != EYSIZE ||
9556 new_real_sx != REAL_SX ||
9557 new_real_sy != REAL_SY ||
9558 new_full_sxsize != FULL_SXSIZE ||
9559 new_full_sysize != FULL_SYSIZE ||
9560 new_tilesize_var != TILESIZE_VAR
9563 // ------------------------------------------------------------------------
9564 // determine next fading area for changed viewport definitions
9565 // ------------------------------------------------------------------------
9567 // start with current playfield area (default fading area)
9570 FADE_SXSIZE = FULL_SXSIZE;
9571 FADE_SYSIZE = FULL_SYSIZE;
9573 // add new playfield area if position or size has changed
9574 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9575 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9577 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9578 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9581 // add current and new door 1 area if position or size has changed
9582 if (new_dx != DX || new_dy != DY ||
9583 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9585 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9586 DX, DY, DXSIZE, DYSIZE);
9587 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9588 new_dx, new_dy, new_dxsize, new_dysize);
9591 // add current and new door 2 area if position or size has changed
9592 if (new_vx != VX || new_vy != VY ||
9593 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9595 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9596 VX, VY, VXSIZE, VYSIZE);
9597 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9598 new_vx, new_vy, new_vxsize, new_vysize);
9601 // ------------------------------------------------------------------------
9602 // handle changed tile size
9603 // ------------------------------------------------------------------------
9605 if (new_tilesize_var != TILESIZE_VAR)
9607 // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9609 // changing tile size invalidates scroll values of engine snapshots
9610 FreeEngineSnapshotSingle();
9612 // changing tile size requires update of graphic mapping for EM engine
9613 init_em_graphics = TRUE;
9624 SXSIZE = new_sxsize;
9625 SYSIZE = new_sysize;
9626 DXSIZE = new_dxsize;
9627 DYSIZE = new_dysize;
9628 VXSIZE = new_vxsize;
9629 VYSIZE = new_vysize;
9630 EXSIZE = new_exsize;
9631 EYSIZE = new_eysize;
9632 REAL_SX = new_real_sx;
9633 REAL_SY = new_real_sy;
9634 FULL_SXSIZE = new_full_sxsize;
9635 FULL_SYSIZE = new_full_sysize;
9636 TILESIZE_VAR = new_tilesize_var;
9638 init_gfx_buffers = TRUE;
9639 init_gadgets_and_anims = TRUE;
9641 // Debug("tools:viewport", "viewports: init_gfx_buffers");
9642 // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9645 if (init_gfx_buffers)
9647 // Debug("tools:viewport", "init_gfx_buffers");
9649 SCR_FIELDX = new_scr_fieldx_buffers;
9650 SCR_FIELDY = new_scr_fieldy_buffers;
9654 SCR_FIELDX = new_scr_fieldx;
9655 SCR_FIELDY = new_scr_fieldy;
9657 SetDrawDeactivationMask(REDRAW_NONE);
9658 SetDrawBackgroundMask(REDRAW_FIELD);
9661 if (init_video_buffer)
9663 // Debug("tools:viewport", "init_video_buffer");
9665 FreeAllImageTextures(); // needs old renderer to free the textures
9667 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9668 InitImageTextures();
9671 if (init_gadgets_and_anims)
9673 // Debug("tools:viewport", "init_gadgets_and_anims");
9676 InitGlobalAnimations();
9679 if (init_em_graphics)
9681 InitGraphicInfo_EM();