1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 // select level set with EMC X11 graphics before activating EM GFX debugging
27 #define DEBUG_EM_GFX FALSE
28 #define DEBUG_FRAME_TIME FALSE
30 // tool button identifiers
31 #define TOOL_CTRL_ID_YES 0
32 #define TOOL_CTRL_ID_NO 1
33 #define TOOL_CTRL_ID_CONFIRM 2
34 #define TOOL_CTRL_ID_PLAYER_1 3
35 #define TOOL_CTRL_ID_PLAYER_2 4
36 #define TOOL_CTRL_ID_PLAYER_3 5
37 #define TOOL_CTRL_ID_PLAYER_4 6
39 #define NUM_TOOL_BUTTONS 7
41 // constants for number of doors and door parts
43 #define NUM_PANELS NUM_DOORS
44 // #define NUM_PANELS 0
45 #define MAX_PARTS_PER_DOOR 8
46 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
47 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
50 struct DoorPartOrderInfo
56 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
58 struct DoorPartControlInfo
62 struct DoorPartPosInfo *pos;
65 static struct DoorPartControlInfo door_part_controls[] =
69 IMG_GFX_DOOR_1_PART_1,
74 IMG_GFX_DOOR_1_PART_2,
79 IMG_GFX_DOOR_1_PART_3,
84 IMG_GFX_DOOR_1_PART_4,
89 IMG_GFX_DOOR_1_PART_5,
94 IMG_GFX_DOOR_1_PART_6,
99 IMG_GFX_DOOR_1_PART_7,
104 IMG_GFX_DOOR_1_PART_8,
110 IMG_GFX_DOOR_2_PART_1,
115 IMG_GFX_DOOR_2_PART_2,
120 IMG_GFX_DOOR_2_PART_3,
125 IMG_GFX_DOOR_2_PART_4,
130 IMG_GFX_DOOR_2_PART_5,
135 IMG_GFX_DOOR_2_PART_6,
140 IMG_GFX_DOOR_2_PART_7,
145 IMG_GFX_DOOR_2_PART_8,
151 IMG_BACKGROUND_PANEL,
168 // forward declaration for internal use
169 static void UnmapToolButtons(void);
170 static void HandleToolButtons(struct GadgetInfo *);
171 static int el_act_dir2crm(int, int, int);
172 static int el_act2crm(int, int);
174 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
175 static int request_gadget_id = -1;
177 static char *print_if_not_empty(int element)
179 static char *s = NULL;
180 char *token_name = element_info[element].token_name;
185 s = checked_malloc(strlen(token_name) + 10 + 1);
187 if (element != EL_EMPTY)
188 sprintf(s, "%d\t['%s']", element, token_name);
190 sprintf(s, "%d", element);
195 int correctLevelPosX_EM(int lx)
198 lx -= (BorderElement != EL_EMPTY ? 1 : 0);
203 int correctLevelPosY_EM(int ly)
206 ly -= (BorderElement != EL_EMPTY ? 1 : 0);
211 static int getFieldbufferOffsetX_RND(void)
213 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
214 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
215 int dx_var = dx * TILESIZE_VAR / TILESIZE;
218 if (EVEN(SCR_FIELDX))
220 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
221 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
223 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
224 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
226 fx += (dx_var > 0 ? TILEX_VAR : 0);
233 if (full_lev_fieldx <= SCR_FIELDX)
235 if (EVEN(SCR_FIELDX))
236 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
238 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
244 static int getFieldbufferOffsetY_RND(void)
246 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
247 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
248 int dy_var = dy * TILESIZE_VAR / TILESIZE;
251 if (EVEN(SCR_FIELDY))
253 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
254 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
256 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
257 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
259 fy += (dy_var > 0 ? TILEY_VAR : 0);
266 if (full_lev_fieldy <= SCR_FIELDY)
268 if (EVEN(SCR_FIELDY))
269 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
271 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
277 static int getLevelFromScreenX_RND(int sx)
279 int fx = getFieldbufferOffsetX_RND();
282 int lx = LEVELX((px + dx) / TILESIZE_VAR);
287 static int getLevelFromScreenY_RND(int sy)
289 int fy = getFieldbufferOffsetY_RND();
292 int ly = LEVELY((py + dy) / TILESIZE_VAR);
297 static int getLevelFromScreenX_EM(int sx)
299 int level_xsize = level.native_em_level->lev->width;
300 int full_xsize = level_xsize * TILESIZE_VAR;
302 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
304 int fx = getFieldbufferOffsetX_EM();
307 int lx = LEVELX((px + dx) / TILESIZE_VAR);
309 lx = correctLevelPosX_EM(lx);
314 static int getLevelFromScreenY_EM(int sy)
316 int level_ysize = level.native_em_level->lev->height;
317 int full_ysize = level_ysize * TILESIZE_VAR;
319 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
321 int fy = getFieldbufferOffsetY_EM();
324 int ly = LEVELY((py + dy) / TILESIZE_VAR);
326 ly = correctLevelPosY_EM(ly);
331 static int getLevelFromScreenX_SP(int sx)
333 int menBorder = setup.sp_show_border_elements;
334 int level_xsize = level.native_sp_level->width;
335 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
337 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
339 int fx = getFieldbufferOffsetX_SP();
342 int lx = LEVELX((px + dx) / TILESIZE_VAR);
347 static int getLevelFromScreenY_SP(int sy)
349 int menBorder = setup.sp_show_border_elements;
350 int level_ysize = level.native_sp_level->height;
351 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
353 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
355 int fy = getFieldbufferOffsetY_SP();
358 int ly = LEVELY((py + dy) / TILESIZE_VAR);
363 static int getLevelFromScreenX_MM(int sx)
365 int level_xsize = level.native_mm_level->fieldx;
366 int full_xsize = level_xsize * TILESIZE_VAR;
368 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
371 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
376 static int getLevelFromScreenY_MM(int sy)
378 int level_ysize = level.native_mm_level->fieldy;
379 int full_ysize = level_ysize * TILESIZE_VAR;
381 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
384 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
389 int getLevelFromScreenX(int x)
391 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
392 return getLevelFromScreenX_EM(x);
393 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
394 return getLevelFromScreenX_SP(x);
395 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
396 return getLevelFromScreenX_MM(x);
398 return getLevelFromScreenX_RND(x);
401 int getLevelFromScreenY(int y)
403 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
404 return getLevelFromScreenY_EM(y);
405 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
406 return getLevelFromScreenY_SP(y);
407 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
408 return getLevelFromScreenY_MM(y);
410 return getLevelFromScreenY_RND(y);
413 void DumpTile(int x, int y)
419 printf_line("-", 79);
420 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
421 printf_line("-", 79);
423 if (!IN_LEV_FIELD(x, y))
425 printf("(not in level field)\n");
431 token_name = element_info[Feld[x][y]].token_name;
433 printf(" Feld: %d\t['%s']\n", Feld[x][y], token_name);
434 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
435 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
436 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
437 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
438 printf(" MovPos: %d\n", MovPos[x][y]);
439 printf(" MovDir: %d\n", MovDir[x][y]);
440 printf(" MovDelay: %d\n", MovDelay[x][y]);
441 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
442 printf(" CustomValue: %d\n", CustomValue[x][y]);
443 printf(" GfxElement: %d\n", GfxElement[x][y]);
444 printf(" GfxAction: %d\n", GfxAction[x][y]);
445 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
446 printf(" Player x/y: %d, %d\n", local_player->jx, local_player->jy);
450 void DumpTileFromScreen(int sx, int sy)
452 int lx = getLevelFromScreenX(sx);
453 int ly = getLevelFromScreenY(sy);
458 void SetDrawtoField(int mode)
460 if (mode == DRAW_TO_FIELDBUFFER)
466 BX2 = SCR_FIELDX + 1;
467 BY2 = SCR_FIELDY + 1;
469 drawto_field = fieldbuffer;
471 else // DRAW_TO_BACKBUFFER
477 BX2 = SCR_FIELDX - 1;
478 BY2 = SCR_FIELDY - 1;
480 drawto_field = backbuffer;
484 static void RedrawPlayfield_RND(void)
486 if (game.envelope_active)
489 DrawLevel(REDRAW_ALL);
493 void RedrawPlayfield(void)
495 if (game_status != GAME_MODE_PLAYING)
498 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
499 RedrawPlayfield_EM(TRUE);
500 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
501 RedrawPlayfield_SP(TRUE);
502 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
503 RedrawPlayfield_MM();
504 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
505 RedrawPlayfield_RND();
507 BlitScreenToBitmap(backbuffer);
509 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
513 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
516 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
517 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
519 if (x == -1 && y == -1)
522 if (draw_target == DRAW_TO_SCREEN)
523 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
525 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
528 static void DrawMaskedBorderExt_FIELD(int draw_target)
530 if (global.border_status >= GAME_MODE_MAIN &&
531 global.border_status <= GAME_MODE_PLAYING &&
532 border.draw_masked[global.border_status])
533 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
537 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
539 // when drawing to backbuffer, never draw border over open doors
540 if (draw_target == DRAW_TO_BACKBUFFER &&
541 (GetDoorState() & DOOR_OPEN_1))
544 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
545 (global.border_status != GAME_MODE_EDITOR ||
546 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
547 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
550 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
552 // when drawing to backbuffer, never draw border over open doors
553 if (draw_target == DRAW_TO_BACKBUFFER &&
554 (GetDoorState() & DOOR_OPEN_2))
557 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
558 global.border_status != GAME_MODE_EDITOR)
559 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
562 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
564 // currently not available
567 static void DrawMaskedBorderExt_ALL(int draw_target)
569 DrawMaskedBorderExt_FIELD(draw_target);
570 DrawMaskedBorderExt_DOOR_1(draw_target);
571 DrawMaskedBorderExt_DOOR_2(draw_target);
572 DrawMaskedBorderExt_DOOR_3(draw_target);
575 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
577 // never draw masked screen borders on borderless screens
578 if (global.border_status == GAME_MODE_LOADING ||
579 global.border_status == GAME_MODE_TITLE)
582 if (redraw_mask & REDRAW_ALL)
583 DrawMaskedBorderExt_ALL(draw_target);
586 if (redraw_mask & REDRAW_FIELD)
587 DrawMaskedBorderExt_FIELD(draw_target);
588 if (redraw_mask & REDRAW_DOOR_1)
589 DrawMaskedBorderExt_DOOR_1(draw_target);
590 if (redraw_mask & REDRAW_DOOR_2)
591 DrawMaskedBorderExt_DOOR_2(draw_target);
592 if (redraw_mask & REDRAW_DOOR_3)
593 DrawMaskedBorderExt_DOOR_3(draw_target);
597 void DrawMaskedBorder_FIELD(void)
599 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
602 void DrawMaskedBorder(int redraw_mask)
604 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
607 void DrawMaskedBorderToTarget(int draw_target)
609 if (draw_target == DRAW_TO_BACKBUFFER ||
610 draw_target == DRAW_TO_SCREEN)
612 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
616 int last_border_status = global.border_status;
618 if (draw_target == DRAW_TO_FADE_SOURCE)
620 global.border_status = gfx.fade_border_source_status;
621 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
623 else if (draw_target == DRAW_TO_FADE_TARGET)
625 global.border_status = gfx.fade_border_target_status;
626 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
629 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
631 global.border_status = last_border_status;
632 gfx.masked_border_bitmap_ptr = backbuffer;
636 void DrawTileCursor(int draw_target)
642 int graphic = IMG_GLOBAL_TILE_CURSOR;
644 int tilesize = TILESIZE_VAR;
645 int width = tilesize;
646 int height = tilesize;
648 if (game_status != GAME_MODE_PLAYING)
651 if (!tile_cursor.enabled ||
655 if (tile_cursor.moving)
657 int step = TILESIZE_VAR / 4;
658 int dx = tile_cursor.target_x - tile_cursor.x;
659 int dy = tile_cursor.target_y - tile_cursor.y;
662 tile_cursor.x = tile_cursor.target_x;
664 tile_cursor.x += SIGN(dx) * step;
667 tile_cursor.y = tile_cursor.target_y;
669 tile_cursor.y += SIGN(dy) * step;
671 if (tile_cursor.x == tile_cursor.target_x &&
672 tile_cursor.y == tile_cursor.target_y)
673 tile_cursor.moving = FALSE;
676 dst_x = tile_cursor.x;
677 dst_y = tile_cursor.y;
679 frame = getGraphicAnimationFrame(graphic, -1);
681 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
684 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
685 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
687 if (draw_target == DRAW_TO_SCREEN)
688 BlitToScreenMasked(src_bitmap, src_x, src_y, width, height, dst_x, dst_y);
690 BlitBitmapMasked(src_bitmap, fade_bitmap, src_x, src_y, width, height,
694 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
696 int fx = getFieldbufferOffsetX_RND();
697 int fy = getFieldbufferOffsetY_RND();
699 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
702 void BlitScreenToBitmap(Bitmap *target_bitmap)
704 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
705 BlitScreenToBitmap_EM(target_bitmap);
706 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
707 BlitScreenToBitmap_SP(target_bitmap);
708 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
709 BlitScreenToBitmap_MM(target_bitmap);
710 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
711 BlitScreenToBitmap_RND(target_bitmap);
713 redraw_mask |= REDRAW_FIELD;
716 static void DrawFramesPerSecond(void)
719 int font_nr = FONT_TEXT_2;
720 int font_width = getFontWidth(font_nr);
721 int draw_deactivation_mask = GetDrawDeactivationMask();
722 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
724 // draw FPS with leading space (needed if field buffer deactivated)
725 sprintf(text, " %04.1f fps", global.frames_per_second);
727 // override draw deactivation mask (required for invisible warp mode)
728 SetDrawDeactivationMask(REDRAW_NONE);
730 // draw opaque FPS if field buffer deactivated, else draw masked FPS
731 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
732 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
734 // set draw deactivation mask to previous value
735 SetDrawDeactivationMask(draw_deactivation_mask);
737 // force full-screen redraw in this frame
738 redraw_mask = REDRAW_ALL;
742 static void PrintFrameTimeDebugging(void)
744 static unsigned int last_counter = 0;
745 unsigned int counter = Counter();
746 int diff_1 = counter - last_counter;
747 int diff_2 = diff_1 - GAME_FRAME_DELAY;
749 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
750 char diff_bar[2 * diff_2_max + 5];
754 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
756 for (i = 0; i < diff_2_max; i++)
757 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
758 i >= diff_2_max - diff_2_cut ? '-' : ' ');
760 diff_bar[pos++] = '|';
762 for (i = 0; i < diff_2_max; i++)
763 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
765 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
767 diff_bar[pos++] = '\0';
769 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
772 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
775 last_counter = counter;
779 static int unifiedRedrawMask(int mask)
781 if (mask & REDRAW_ALL)
784 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
790 static boolean equalRedrawMasks(int mask_1, int mask_2)
792 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
795 void BackToFront(void)
797 static int last_redraw_mask = REDRAW_NONE;
799 // force screen redraw in every frame to continue drawing global animations
800 // (but always use the last redraw mask to prevent unwanted side effects)
801 if (redraw_mask == REDRAW_NONE)
802 redraw_mask = last_redraw_mask;
804 last_redraw_mask = redraw_mask;
807 // masked border now drawn immediately when blitting backbuffer to window
809 // draw masked border to all viewports, if defined
810 DrawMaskedBorder(redraw_mask);
813 // draw frames per second (only if debug mode is enabled)
814 if (redraw_mask & REDRAW_FPS)
815 DrawFramesPerSecond();
817 // remove playfield redraw before potentially merging with doors redraw
818 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
819 redraw_mask &= ~REDRAW_FIELD;
821 // redraw complete window if both playfield and (some) doors need redraw
822 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
823 redraw_mask = REDRAW_ALL;
825 /* although redrawing the whole window would be fine for normal gameplay,
826 being able to only redraw the playfield is required for deactivating
827 certain drawing areas (mainly playfield) to work, which is needed for
828 warp-forward to be fast enough (by skipping redraw of most frames) */
830 if (redraw_mask & REDRAW_ALL)
832 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
834 else if (redraw_mask & REDRAW_FIELD)
836 BlitBitmap(backbuffer, window,
837 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
839 else if (redraw_mask & REDRAW_DOORS)
841 // merge door areas to prevent calling screen redraw more than once
847 if (redraw_mask & REDRAW_DOOR_1)
851 x2 = MAX(x2, DX + DXSIZE);
852 y2 = MAX(y2, DY + DYSIZE);
855 if (redraw_mask & REDRAW_DOOR_2)
859 x2 = MAX(x2, VX + VXSIZE);
860 y2 = MAX(y2, VY + VYSIZE);
863 if (redraw_mask & REDRAW_DOOR_3)
867 x2 = MAX(x2, EX + EXSIZE);
868 y2 = MAX(y2, EY + EYSIZE);
871 // make sure that at least one pixel is blitted, and inside the screen
872 // (else nothing is blitted, causing the animations not to be updated)
873 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
874 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
875 x2 = MIN(MAX(1, x2), WIN_XSIZE);
876 y2 = MIN(MAX(1, y2), WIN_YSIZE);
878 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
881 redraw_mask = REDRAW_NONE;
884 PrintFrameTimeDebugging();
888 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
890 unsigned int frame_delay_value_old = GetVideoFrameDelay();
892 SetVideoFrameDelay(frame_delay_value);
896 SetVideoFrameDelay(frame_delay_value_old);
899 static int fade_type_skip = FADE_TYPE_NONE;
901 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
903 void (*draw_border_function)(void) = NULL;
904 int x, y, width, height;
905 int fade_delay, post_delay;
907 if (fade_type == FADE_TYPE_FADE_OUT)
909 if (fade_type_skip != FADE_TYPE_NONE)
911 // skip all fade operations until specified fade operation
912 if (fade_type & fade_type_skip)
913 fade_type_skip = FADE_TYPE_NONE;
918 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
922 redraw_mask |= fade_mask;
924 if (fade_type == FADE_TYPE_SKIP)
926 fade_type_skip = fade_mode;
931 fade_delay = fading.fade_delay;
932 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
934 if (fade_type_skip != FADE_TYPE_NONE)
936 // skip all fade operations until specified fade operation
937 if (fade_type & fade_type_skip)
938 fade_type_skip = FADE_TYPE_NONE;
943 if (global.autoplay_leveldir)
948 if (fade_mask == REDRAW_FIELD)
953 height = FADE_SYSIZE;
955 if (border.draw_masked_when_fading)
956 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
958 DrawMaskedBorder_FIELD(); // draw once
968 if (!setup.fade_screens ||
970 fading.fade_mode == FADE_MODE_NONE)
972 if (fade_mode == FADE_MODE_FADE_OUT)
975 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
977 redraw_mask &= ~fade_mask;
982 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
983 draw_border_function);
985 redraw_mask &= ~fade_mask;
987 ClearAutoRepeatKeyEvents();
990 static void SetScreenStates_BeforeFadingIn(void)
992 // temporarily set screen mode for animations to screen after fading in
993 global.anim_status = global.anim_status_next;
995 // store backbuffer with all animations that will be started after fading in
996 if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
997 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
999 // set screen mode for animations back to fading
1000 global.anim_status = GAME_MODE_PSEUDO_FADING;
1003 static void SetScreenStates_AfterFadingIn(void)
1005 // store new source screen (to use correct masked border for fading)
1006 gfx.fade_border_source_status = global.border_status;
1008 global.anim_status = global.anim_status_next;
1011 static void SetScreenStates_BeforeFadingOut(void)
1013 // store new target screen (to use correct masked border for fading)
1014 gfx.fade_border_target_status = game_status;
1016 // set screen mode for animations to fading
1017 global.anim_status = GAME_MODE_PSEUDO_FADING;
1019 // store backbuffer with all animations that will be stopped for fading out
1020 if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1021 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
1024 static void SetScreenStates_AfterFadingOut(void)
1026 global.border_status = game_status;
1029 void FadeIn(int fade_mask)
1031 SetScreenStates_BeforeFadingIn();
1034 DrawMaskedBorder(REDRAW_ALL);
1037 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1038 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1040 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1044 FADE_SXSIZE = FULL_SXSIZE;
1045 FADE_SYSIZE = FULL_SYSIZE;
1047 // activate virtual buttons depending on upcoming game status
1048 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1049 game_status == GAME_MODE_PLAYING && !tape.playing)
1050 SetOverlayActive(TRUE);
1052 SetScreenStates_AfterFadingIn();
1054 // force update of global animation status in case of rapid screen changes
1055 redraw_mask = REDRAW_ALL;
1059 void FadeOut(int fade_mask)
1061 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1062 if (!equalRedrawMasks(fade_mask, redraw_mask))
1065 SetScreenStates_BeforeFadingOut();
1067 SetTileCursorActive(FALSE);
1068 SetOverlayActive(FALSE);
1071 DrawMaskedBorder(REDRAW_ALL);
1074 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1075 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1077 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1079 SetScreenStates_AfterFadingOut();
1082 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1084 static struct TitleFadingInfo fading_leave_stored;
1087 fading_leave_stored = fading_leave;
1089 fading = fading_leave_stored;
1092 void FadeSetEnterMenu(void)
1094 fading = menu.enter_menu;
1096 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1099 void FadeSetLeaveMenu(void)
1101 fading = menu.leave_menu;
1103 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1106 void FadeSetEnterScreen(void)
1108 fading = menu.enter_screen[game_status];
1110 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1113 void FadeSetNextScreen(void)
1115 fading = menu.next_screen[game_status];
1117 // (do not overwrite fade mode set by FadeSetEnterScreen)
1118 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1121 void FadeSetLeaveScreen(void)
1123 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1126 void FadeSetFromType(int type)
1128 if (type & TYPE_ENTER_SCREEN)
1129 FadeSetEnterScreen();
1130 else if (type & TYPE_ENTER)
1132 else if (type & TYPE_LEAVE)
1136 void FadeSetDisabled(void)
1138 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1140 fading = fading_none;
1143 void FadeSkipNextFadeIn(void)
1145 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1148 void FadeSkipNextFadeOut(void)
1150 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1153 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1155 if (graphic == IMG_UNDEFINED)
1158 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1160 return (graphic_info[graphic].bitmap != NULL || redefined ?
1161 graphic_info[graphic].bitmap :
1162 graphic_info[default_graphic].bitmap);
1165 static Bitmap *getBackgroundBitmap(int graphic)
1167 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1170 static Bitmap *getGlobalBorderBitmap(int graphic)
1172 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1175 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1178 (status == GAME_MODE_MAIN ||
1179 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1180 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1181 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1182 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1185 return getGlobalBorderBitmap(graphic);
1188 void SetWindowBackgroundImageIfDefined(int graphic)
1190 if (graphic_info[graphic].bitmap)
1191 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1194 void SetMainBackgroundImageIfDefined(int graphic)
1196 if (graphic_info[graphic].bitmap)
1197 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1200 void SetDoorBackgroundImageIfDefined(int graphic)
1202 if (graphic_info[graphic].bitmap)
1203 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1206 void SetWindowBackgroundImage(int graphic)
1208 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1211 void SetMainBackgroundImage(int graphic)
1213 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1216 void SetDoorBackgroundImage(int graphic)
1218 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1221 void SetPanelBackground(void)
1223 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1225 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1226 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1228 SetDoorBackgroundBitmap(bitmap_db_panel);
1231 void DrawBackground(int x, int y, int width, int height)
1233 // "drawto" might still point to playfield buffer here (hall of fame)
1234 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1236 if (IN_GFX_FIELD_FULL(x, y))
1237 redraw_mask |= REDRAW_FIELD;
1238 else if (IN_GFX_DOOR_1(x, y))
1239 redraw_mask |= REDRAW_DOOR_1;
1240 else if (IN_GFX_DOOR_2(x, y))
1241 redraw_mask |= REDRAW_DOOR_2;
1242 else if (IN_GFX_DOOR_3(x, y))
1243 redraw_mask |= REDRAW_DOOR_3;
1246 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1248 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1250 if (font->bitmap == NULL)
1253 DrawBackground(x, y, width, height);
1256 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1258 struct GraphicInfo *g = &graphic_info[graphic];
1260 if (g->bitmap == NULL)
1263 DrawBackground(x, y, width, height);
1266 static int game_status_last = -1;
1267 static Bitmap *global_border_bitmap_last = NULL;
1268 static Bitmap *global_border_bitmap = NULL;
1269 static int real_sx_last = -1, real_sy_last = -1;
1270 static int full_sxsize_last = -1, full_sysize_last = -1;
1271 static int dx_last = -1, dy_last = -1;
1272 static int dxsize_last = -1, dysize_last = -1;
1273 static int vx_last = -1, vy_last = -1;
1274 static int vxsize_last = -1, vysize_last = -1;
1275 static int ex_last = -1, ey_last = -1;
1276 static int exsize_last = -1, eysize_last = -1;
1278 boolean CheckIfGlobalBorderHasChanged(void)
1280 // if game status has not changed, global border has not changed either
1281 if (game_status == game_status_last)
1284 // determine and store new global border bitmap for current game status
1285 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1287 return (global_border_bitmap_last != global_border_bitmap);
1290 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1292 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1293 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1295 // if game status has not changed, nothing has to be redrawn
1296 if (game_status == game_status_last)
1299 // redraw if last screen was title screen
1300 if (game_status_last == GAME_MODE_TITLE)
1303 // redraw if global screen border has changed
1304 if (CheckIfGlobalBorderHasChanged())
1307 // redraw if position or size of playfield area has changed
1308 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1309 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1312 // redraw if position or size of door area has changed
1313 if (dx_last != DX || dy_last != DY ||
1314 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1317 // redraw if position or size of tape area has changed
1318 if (vx_last != VX || vy_last != VY ||
1319 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1322 // redraw if position or size of editor area has changed
1323 if (ex_last != EX || ey_last != EY ||
1324 exsize_last != EXSIZE || eysize_last != EYSIZE)
1331 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1334 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1336 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1339 void RedrawGlobalBorder(void)
1341 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1343 RedrawGlobalBorderFromBitmap(bitmap);
1345 redraw_mask = REDRAW_ALL;
1348 static void RedrawGlobalBorderIfNeeded(void)
1350 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1351 if (game_status == game_status_last)
1355 // copy current draw buffer to later copy back areas that have not changed
1356 if (game_status_last != GAME_MODE_TITLE)
1357 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1359 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1360 if (CheckIfGlobalBorderRedrawIsNeeded())
1363 // redraw global screen border (or clear, if defined to be empty)
1364 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1366 if (game_status == GAME_MODE_EDITOR)
1367 DrawSpecialEditorDoor();
1369 // copy previous playfield and door areas, if they are defined on both
1370 // previous and current screen and if they still have the same size
1372 if (real_sx_last != -1 && real_sy_last != -1 &&
1373 REAL_SX != -1 && REAL_SY != -1 &&
1374 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1375 BlitBitmap(bitmap_db_store_1, backbuffer,
1376 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1379 if (dx_last != -1 && dy_last != -1 &&
1380 DX != -1 && DY != -1 &&
1381 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1382 BlitBitmap(bitmap_db_store_1, backbuffer,
1383 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1385 if (game_status != GAME_MODE_EDITOR)
1387 if (vx_last != -1 && vy_last != -1 &&
1388 VX != -1 && VY != -1 &&
1389 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1390 BlitBitmap(bitmap_db_store_1, backbuffer,
1391 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1395 if (ex_last != -1 && ey_last != -1 &&
1396 EX != -1 && EY != -1 &&
1397 exsize_last == EXSIZE && eysize_last == EYSIZE)
1398 BlitBitmap(bitmap_db_store_1, backbuffer,
1399 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1402 redraw_mask = REDRAW_ALL;
1405 game_status_last = game_status;
1407 global_border_bitmap_last = global_border_bitmap;
1409 real_sx_last = REAL_SX;
1410 real_sy_last = REAL_SY;
1411 full_sxsize_last = FULL_SXSIZE;
1412 full_sysize_last = FULL_SYSIZE;
1415 dxsize_last = DXSIZE;
1416 dysize_last = DYSIZE;
1419 vxsize_last = VXSIZE;
1420 vysize_last = VYSIZE;
1423 exsize_last = EXSIZE;
1424 eysize_last = EYSIZE;
1427 void ClearField(void)
1429 RedrawGlobalBorderIfNeeded();
1431 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1432 // (when entering hall of fame after playing)
1433 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1435 // !!! maybe this should be done before clearing the background !!!
1436 if (game_status == GAME_MODE_PLAYING)
1438 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1439 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1443 SetDrawtoField(DRAW_TO_BACKBUFFER);
1447 void MarkTileDirty(int x, int y)
1449 redraw_mask |= REDRAW_FIELD;
1452 void SetBorderElement(void)
1456 BorderElement = EL_EMPTY;
1458 // the MM game engine does not use a visible border element
1459 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1462 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1464 for (x = 0; x < lev_fieldx; x++)
1466 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1467 BorderElement = EL_STEELWALL;
1469 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1475 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1476 int max_array_fieldx, int max_array_fieldy,
1477 short field[max_array_fieldx][max_array_fieldy],
1478 int max_fieldx, int max_fieldy)
1482 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1483 static int safety = 0;
1485 // check if starting field still has the desired content
1486 if (field[from_x][from_y] == fill_element)
1491 if (safety > max_fieldx * max_fieldy)
1492 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1494 old_element = field[from_x][from_y];
1495 field[from_x][from_y] = fill_element;
1497 for (i = 0; i < 4; i++)
1499 x = from_x + check[i][0];
1500 y = from_y + check[i][1];
1502 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1503 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1504 field, max_fieldx, max_fieldy);
1510 void FloodFillLevel(int from_x, int from_y, int fill_element,
1511 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1512 int max_fieldx, int max_fieldy)
1514 FloodFillLevelExt(from_x, from_y, fill_element,
1515 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1516 max_fieldx, max_fieldy);
1519 void SetRandomAnimationValue(int x, int y)
1521 gfx.anim_random_frame = GfxRandom[x][y];
1524 int getGraphicAnimationFrame(int graphic, int sync_frame)
1526 // animation synchronized with global frame counter, not move position
1527 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1528 sync_frame = FrameCounter;
1530 return getAnimationFrame(graphic_info[graphic].anim_frames,
1531 graphic_info[graphic].anim_delay,
1532 graphic_info[graphic].anim_mode,
1533 graphic_info[graphic].anim_start_frame,
1537 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1539 struct GraphicInfo *g = &graphic_info[graphic];
1540 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1542 if (tilesize == gfx.standard_tile_size)
1543 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1544 else if (tilesize == game.tile_size)
1545 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1547 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1550 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1551 boolean get_backside)
1553 struct GraphicInfo *g = &graphic_info[graphic];
1554 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1555 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1557 if (g->offset_y == 0) // frames are ordered horizontally
1559 int max_width = g->anim_frames_per_line * g->width;
1560 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1562 *x = pos % max_width;
1563 *y = src_y % g->height + pos / max_width * g->height;
1565 else if (g->offset_x == 0) // frames are ordered vertically
1567 int max_height = g->anim_frames_per_line * g->height;
1568 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1570 *x = src_x % g->width + pos / max_height * g->width;
1571 *y = pos % max_height;
1573 else // frames are ordered diagonally
1575 *x = src_x + frame * g->offset_x;
1576 *y = src_y + frame * g->offset_y;
1580 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1581 Bitmap **bitmap, int *x, int *y,
1582 boolean get_backside)
1584 struct GraphicInfo *g = &graphic_info[graphic];
1586 // if no graphics defined at all, use fallback graphics
1587 if (g->bitmaps == NULL)
1588 *g = graphic_info[IMG_CHAR_EXCLAM];
1590 // if no in-game graphics defined, always use standard graphic size
1591 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1592 tilesize = TILESIZE;
1594 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1595 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1597 *x = *x * tilesize / g->tile_size;
1598 *y = *y * tilesize / g->tile_size;
1601 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1602 Bitmap **bitmap, int *x, int *y)
1604 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1607 void getFixedGraphicSource(int graphic, int frame,
1608 Bitmap **bitmap, int *x, int *y)
1610 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1613 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1615 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1618 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1619 int *x, int *y, boolean get_backside)
1621 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1625 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1627 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1630 void DrawGraphic(int x, int y, int graphic, int frame)
1633 if (!IN_SCR_FIELD(x, y))
1635 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1636 printf("DrawGraphic(): This should never happen!\n");
1641 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1644 MarkTileDirty(x, y);
1647 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1650 if (!IN_SCR_FIELD(x, y))
1652 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1653 printf("DrawGraphic(): This should never happen!\n");
1658 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1660 MarkTileDirty(x, y);
1663 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1669 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1671 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1674 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1680 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1681 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1684 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1687 if (!IN_SCR_FIELD(x, y))
1689 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1690 printf("DrawGraphicThruMask(): This should never happen!\n");
1695 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1698 MarkTileDirty(x, y);
1701 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1704 if (!IN_SCR_FIELD(x, y))
1706 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1707 printf("DrawGraphicThruMask(): This should never happen!\n");
1712 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1714 MarkTileDirty(x, y);
1717 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1723 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1725 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1729 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1730 int graphic, int frame)
1735 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1737 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1741 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1743 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1745 MarkTileDirty(x / tilesize, y / tilesize);
1748 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1751 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1752 graphic, frame, tilesize);
1753 MarkTileDirty(x / tilesize, y / tilesize);
1756 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1762 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1763 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1766 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1767 int frame, int tilesize)
1772 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1773 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1776 void DrawMiniGraphic(int x, int y, int graphic)
1778 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1779 MarkTileDirty(x / 2, y / 2);
1782 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1787 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1788 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1791 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1792 int graphic, int frame,
1793 int cut_mode, int mask_mode)
1798 int width = TILEX, height = TILEY;
1801 if (dx || dy) // shifted graphic
1803 if (x < BX1) // object enters playfield from the left
1810 else if (x > BX2) // object enters playfield from the right
1816 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1822 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1824 else if (dx) // general horizontal movement
1825 MarkTileDirty(x + SIGN(dx), y);
1827 if (y < BY1) // object enters playfield from the top
1829 if (cut_mode == CUT_BELOW) // object completely above top border
1837 else if (y > BY2) // object enters playfield from the bottom
1843 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1849 else if (dy > 0 && cut_mode == CUT_ABOVE)
1851 if (y == BY2) // object completely above bottom border
1857 MarkTileDirty(x, y + 1);
1858 } // object leaves playfield to the bottom
1859 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1861 else if (dy) // general vertical movement
1862 MarkTileDirty(x, y + SIGN(dy));
1866 if (!IN_SCR_FIELD(x, y))
1868 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1869 printf("DrawGraphicShifted(): This should never happen!\n");
1874 width = width * TILESIZE_VAR / TILESIZE;
1875 height = height * TILESIZE_VAR / TILESIZE;
1876 cx = cx * TILESIZE_VAR / TILESIZE;
1877 cy = cy * TILESIZE_VAR / TILESIZE;
1878 dx = dx * TILESIZE_VAR / TILESIZE;
1879 dy = dy * TILESIZE_VAR / TILESIZE;
1881 if (width > 0 && height > 0)
1883 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1888 dst_x = FX + x * TILEX_VAR + dx;
1889 dst_y = FY + y * TILEY_VAR + dy;
1891 if (mask_mode == USE_MASKING)
1892 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1895 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1898 MarkTileDirty(x, y);
1902 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1903 int graphic, int frame,
1904 int cut_mode, int mask_mode)
1909 int width = TILEX_VAR, height = TILEY_VAR;
1912 int x2 = x + SIGN(dx);
1913 int y2 = y + SIGN(dy);
1915 // movement with two-tile animations must be sync'ed with movement position,
1916 // not with current GfxFrame (which can be higher when using slow movement)
1917 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1918 int anim_frames = graphic_info[graphic].anim_frames;
1920 // (we also need anim_delay here for movement animations with less frames)
1921 int anim_delay = graphic_info[graphic].anim_delay;
1922 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1924 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1925 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1927 // re-calculate animation frame for two-tile movement animation
1928 frame = getGraphicAnimationFrame(graphic, sync_frame);
1930 // check if movement start graphic inside screen area and should be drawn
1931 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1933 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1935 dst_x = FX + x1 * TILEX_VAR;
1936 dst_y = FY + y1 * TILEY_VAR;
1938 if (mask_mode == USE_MASKING)
1939 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1942 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1945 MarkTileDirty(x1, y1);
1948 // check if movement end graphic inside screen area and should be drawn
1949 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1951 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1953 dst_x = FX + x2 * TILEX_VAR;
1954 dst_y = FY + y2 * TILEY_VAR;
1956 if (mask_mode == USE_MASKING)
1957 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1960 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1963 MarkTileDirty(x2, y2);
1967 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1968 int graphic, int frame,
1969 int cut_mode, int mask_mode)
1973 DrawGraphic(x, y, graphic, frame);
1978 if (graphic_info[graphic].double_movement) // EM style movement images
1979 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1981 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1984 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1985 int graphic, int frame, int cut_mode)
1987 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1990 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1991 int cut_mode, int mask_mode)
1993 int lx = LEVELX(x), ly = LEVELY(y);
1997 if (IN_LEV_FIELD(lx, ly))
1999 SetRandomAnimationValue(lx, ly);
2001 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2002 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2004 // do not use double (EM style) movement graphic when not moving
2005 if (graphic_info[graphic].double_movement && !dx && !dy)
2007 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2008 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2011 else // border element
2013 graphic = el2img(element);
2014 frame = getGraphicAnimationFrame(graphic, -1);
2017 if (element == EL_EXPANDABLE_WALL)
2019 boolean left_stopped = FALSE, right_stopped = FALSE;
2021 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
2022 left_stopped = TRUE;
2023 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
2024 right_stopped = TRUE;
2026 if (left_stopped && right_stopped)
2028 else if (left_stopped)
2030 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2031 frame = graphic_info[graphic].anim_frames - 1;
2033 else if (right_stopped)
2035 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2036 frame = graphic_info[graphic].anim_frames - 1;
2041 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2042 else if (mask_mode == USE_MASKING)
2043 DrawGraphicThruMask(x, y, graphic, frame);
2045 DrawGraphic(x, y, graphic, frame);
2048 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2049 int cut_mode, int mask_mode)
2051 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2052 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2053 cut_mode, mask_mode);
2056 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2059 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2062 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2065 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2068 void DrawLevelElementThruMask(int x, int y, int element)
2070 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2073 void DrawLevelFieldThruMask(int x, int y)
2075 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
2078 // !!! implementation of quicksand is totally broken !!!
2079 #define IS_CRUMBLED_TILE(x, y, e) \
2080 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2081 !IS_MOVING(x, y) || \
2082 (e) == EL_QUICKSAND_EMPTYING || \
2083 (e) == EL_QUICKSAND_FAST_EMPTYING))
2085 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2090 int width, height, cx, cy;
2091 int sx = SCREENX(x), sy = SCREENY(y);
2092 int crumbled_border_size = graphic_info[graphic].border_size;
2093 int crumbled_tile_size = graphic_info[graphic].tile_size;
2094 int crumbled_border_size_var =
2095 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2098 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2100 for (i = 1; i < 4; i++)
2102 int dxx = (i & 1 ? dx : 0);
2103 int dyy = (i & 2 ? dy : 0);
2106 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2109 // check if neighbour field is of same crumble type
2110 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2111 graphic_info[graphic].class ==
2112 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2114 // return if check prevents inner corner
2115 if (same == (dxx == dx && dyy == dy))
2119 // if we reach this point, we have an inner corner
2121 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2123 width = crumbled_border_size_var;
2124 height = crumbled_border_size_var;
2125 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2126 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2128 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2129 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2132 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2137 int width, height, bx, by, cx, cy;
2138 int sx = SCREENX(x), sy = SCREENY(y);
2139 int crumbled_border_size = graphic_info[graphic].border_size;
2140 int crumbled_tile_size = graphic_info[graphic].tile_size;
2141 int crumbled_border_size_var =
2142 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2143 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2146 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2148 // draw simple, sloppy, non-corner-accurate crumbled border
2150 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2151 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2152 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2153 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2155 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2156 FX + sx * TILEX_VAR + cx,
2157 FY + sy * TILEY_VAR + cy);
2159 // (remaining middle border part must be at least as big as corner part)
2160 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2161 crumbled_border_size_var >= TILESIZE_VAR / 3)
2164 // correct corners of crumbled border, if needed
2166 for (i = -1; i <= 1; i += 2)
2168 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2169 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2170 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2173 // check if neighbour field is of same crumble type
2174 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2175 graphic_info[graphic].class ==
2176 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2178 // no crumbled corner, but continued crumbled border
2180 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2181 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2182 int b1 = (i == 1 ? crumbled_border_size_var :
2183 TILESIZE_VAR - 2 * crumbled_border_size_var);
2185 width = crumbled_border_size_var;
2186 height = crumbled_border_size_var;
2188 if (dir == 1 || dir == 2)
2203 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2205 FX + sx * TILEX_VAR + cx,
2206 FY + sy * TILEY_VAR + cy);
2211 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2213 int sx = SCREENX(x), sy = SCREENY(y);
2216 static int xy[4][2] =
2224 if (!IN_LEV_FIELD(x, y))
2227 element = TILE_GFX_ELEMENT(x, y);
2229 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2231 if (!IN_SCR_FIELD(sx, sy))
2234 // crumble field borders towards direct neighbour fields
2235 for (i = 0; i < 4; i++)
2237 int xx = x + xy[i][0];
2238 int yy = y + xy[i][1];
2240 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2243 // check if neighbour field is of same crumble type
2244 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2245 graphic_info[graphic].class ==
2246 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2249 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2252 // crumble inner field corners towards corner neighbour fields
2253 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2254 graphic_info[graphic].anim_frames == 2)
2256 for (i = 0; i < 4; i++)
2258 int dx = (i & 1 ? +1 : -1);
2259 int dy = (i & 2 ? +1 : -1);
2261 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2265 MarkTileDirty(sx, sy);
2267 else // center field is not crumbled -- crumble neighbour fields
2269 // crumble field borders of direct neighbour fields
2270 for (i = 0; i < 4; i++)
2272 int xx = x + xy[i][0];
2273 int yy = y + xy[i][1];
2274 int sxx = sx + xy[i][0];
2275 int syy = sy + xy[i][1];
2277 if (!IN_LEV_FIELD(xx, yy) ||
2278 !IN_SCR_FIELD(sxx, syy))
2281 // do not crumble fields that are being digged or snapped
2282 if (Feld[xx][yy] == EL_EMPTY ||
2283 Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2286 element = TILE_GFX_ELEMENT(xx, yy);
2288 if (!IS_CRUMBLED_TILE(xx, yy, element))
2291 graphic = el_act2crm(element, ACTION_DEFAULT);
2293 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2295 MarkTileDirty(sxx, syy);
2298 // crumble inner field corners of corner neighbour fields
2299 for (i = 0; i < 4; i++)
2301 int dx = (i & 1 ? +1 : -1);
2302 int dy = (i & 2 ? +1 : -1);
2308 if (!IN_LEV_FIELD(xx, yy) ||
2309 !IN_SCR_FIELD(sxx, syy))
2312 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2315 element = TILE_GFX_ELEMENT(xx, yy);
2317 if (!IS_CRUMBLED_TILE(xx, yy, element))
2320 graphic = el_act2crm(element, ACTION_DEFAULT);
2322 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2323 graphic_info[graphic].anim_frames == 2)
2324 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2326 MarkTileDirty(sxx, syy);
2331 void DrawLevelFieldCrumbled(int x, int y)
2335 if (!IN_LEV_FIELD(x, y))
2338 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2339 GfxElement[x][y] != EL_UNDEFINED &&
2340 GFX_CRUMBLED(GfxElement[x][y]))
2342 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2347 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2349 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2352 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2355 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2356 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2357 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2358 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2359 int sx = SCREENX(x), sy = SCREENY(y);
2361 DrawGraphic(sx, sy, graphic1, frame1);
2362 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2365 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2367 int sx = SCREENX(x), sy = SCREENY(y);
2368 static int xy[4][2] =
2377 // crumble direct neighbour fields (required for field borders)
2378 for (i = 0; i < 4; i++)
2380 int xx = x + xy[i][0];
2381 int yy = y + xy[i][1];
2382 int sxx = sx + xy[i][0];
2383 int syy = sy + xy[i][1];
2385 if (!IN_LEV_FIELD(xx, yy) ||
2386 !IN_SCR_FIELD(sxx, syy) ||
2387 !GFX_CRUMBLED(Feld[xx][yy]) ||
2391 DrawLevelField(xx, yy);
2394 // crumble corner neighbour fields (required for inner field corners)
2395 for (i = 0; i < 4; i++)
2397 int dx = (i & 1 ? +1 : -1);
2398 int dy = (i & 2 ? +1 : -1);
2404 if (!IN_LEV_FIELD(xx, yy) ||
2405 !IN_SCR_FIELD(sxx, syy) ||
2406 !GFX_CRUMBLED(Feld[xx][yy]) ||
2410 int element = TILE_GFX_ELEMENT(xx, yy);
2411 int graphic = el_act2crm(element, ACTION_DEFAULT);
2413 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2414 graphic_info[graphic].anim_frames == 2)
2415 DrawLevelField(xx, yy);
2419 static int getBorderElement(int x, int y)
2423 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2424 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2425 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2426 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2427 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2428 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2429 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2431 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2432 int steel_position = (x == -1 && y == -1 ? 0 :
2433 x == lev_fieldx && y == -1 ? 1 :
2434 x == -1 && y == lev_fieldy ? 2 :
2435 x == lev_fieldx && y == lev_fieldy ? 3 :
2436 x == -1 || x == lev_fieldx ? 4 :
2437 y == -1 || y == lev_fieldy ? 5 : 6);
2439 return border[steel_position][steel_type];
2442 void DrawScreenElement(int x, int y, int element)
2444 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2445 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2448 void DrawLevelElement(int x, int y, int element)
2450 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2451 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2454 void DrawScreenField(int x, int y)
2456 int lx = LEVELX(x), ly = LEVELY(y);
2457 int element, content;
2459 if (!IN_LEV_FIELD(lx, ly))
2461 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2464 element = getBorderElement(lx, ly);
2466 DrawScreenElement(x, y, element);
2471 element = Feld[lx][ly];
2472 content = Store[lx][ly];
2474 if (IS_MOVING(lx, ly))
2476 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2477 boolean cut_mode = NO_CUTTING;
2479 if (element == EL_QUICKSAND_EMPTYING ||
2480 element == EL_QUICKSAND_FAST_EMPTYING ||
2481 element == EL_MAGIC_WALL_EMPTYING ||
2482 element == EL_BD_MAGIC_WALL_EMPTYING ||
2483 element == EL_DC_MAGIC_WALL_EMPTYING ||
2484 element == EL_AMOEBA_DROPPING)
2485 cut_mode = CUT_ABOVE;
2486 else if (element == EL_QUICKSAND_FILLING ||
2487 element == EL_QUICKSAND_FAST_FILLING ||
2488 element == EL_MAGIC_WALL_FILLING ||
2489 element == EL_BD_MAGIC_WALL_FILLING ||
2490 element == EL_DC_MAGIC_WALL_FILLING)
2491 cut_mode = CUT_BELOW;
2493 if (cut_mode == CUT_ABOVE)
2494 DrawScreenElement(x, y, element);
2496 DrawScreenElement(x, y, EL_EMPTY);
2499 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2500 else if (cut_mode == NO_CUTTING)
2501 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2504 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2506 if (cut_mode == CUT_BELOW &&
2507 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2508 DrawLevelElement(lx, ly + 1, element);
2511 if (content == EL_ACID)
2513 int dir = MovDir[lx][ly];
2514 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2515 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2517 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2519 // prevent target field from being drawn again (but without masking)
2520 // (this would happen if target field is scanned after moving element)
2521 Stop[newlx][newly] = TRUE;
2524 else if (IS_BLOCKED(lx, ly))
2529 boolean cut_mode = NO_CUTTING;
2530 int element_old, content_old;
2532 Blocked2Moving(lx, ly, &oldx, &oldy);
2535 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2536 MovDir[oldx][oldy] == MV_RIGHT);
2538 element_old = Feld[oldx][oldy];
2539 content_old = Store[oldx][oldy];
2541 if (element_old == EL_QUICKSAND_EMPTYING ||
2542 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2543 element_old == EL_MAGIC_WALL_EMPTYING ||
2544 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2545 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2546 element_old == EL_AMOEBA_DROPPING)
2547 cut_mode = CUT_ABOVE;
2549 DrawScreenElement(x, y, EL_EMPTY);
2552 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2554 else if (cut_mode == NO_CUTTING)
2555 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2558 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2561 else if (IS_DRAWABLE(element))
2562 DrawScreenElement(x, y, element);
2564 DrawScreenElement(x, y, EL_EMPTY);
2567 void DrawLevelField(int x, int y)
2569 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2570 DrawScreenField(SCREENX(x), SCREENY(y));
2571 else if (IS_MOVING(x, y))
2575 Moving2Blocked(x, y, &newx, &newy);
2576 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2577 DrawScreenField(SCREENX(newx), SCREENY(newy));
2579 else if (IS_BLOCKED(x, y))
2583 Blocked2Moving(x, y, &oldx, &oldy);
2584 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2585 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2589 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2590 int (*el2img_function)(int), boolean masked,
2591 int element_bits_draw)
2593 int element_base = map_mm_wall_element(element);
2594 int element_bits = (IS_DF_WALL(element) ?
2595 element - EL_DF_WALL_START :
2596 IS_MM_WALL(element) ?
2597 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2598 int graphic = el2img_function(element_base);
2599 int tilesize_draw = tilesize / 2;
2604 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2606 for (i = 0; i < 4; i++)
2608 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2609 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2611 if (!(element_bits_draw & (1 << i)))
2614 if (element_bits & (1 << i))
2617 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2618 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2620 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2621 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2626 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2627 tilesize_draw, tilesize_draw);
2632 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2633 boolean masked, int element_bits_draw)
2635 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2636 element, tilesize, el2edimg, masked, element_bits_draw);
2639 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2640 int (*el2img_function)(int))
2642 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2646 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2649 if (IS_MM_WALL(element))
2651 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2652 element, tilesize, el2edimg, masked, 0x000f);
2656 int graphic = el2edimg(element);
2659 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2661 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2665 void DrawSizedElement(int x, int y, int element, int tilesize)
2667 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2670 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2672 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2675 void DrawMiniElement(int x, int y, int element)
2679 graphic = el2edimg(element);
2680 DrawMiniGraphic(x, y, graphic);
2683 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2686 int x = sx + scroll_x, y = sy + scroll_y;
2688 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2689 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2690 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2691 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2693 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2696 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2698 int x = sx + scroll_x, y = sy + scroll_y;
2700 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2701 DrawMiniElement(sx, sy, EL_EMPTY);
2702 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2703 DrawMiniElement(sx, sy, Feld[x][y]);
2705 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2708 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2709 int x, int y, int xsize, int ysize,
2710 int tile_width, int tile_height)
2714 int dst_x = startx + x * tile_width;
2715 int dst_y = starty + y * tile_height;
2716 int width = graphic_info[graphic].width;
2717 int height = graphic_info[graphic].height;
2718 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2719 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2720 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2721 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2722 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2723 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2724 boolean draw_masked = graphic_info[graphic].draw_masked;
2726 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2728 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2730 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2734 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2735 inner_sx + (x - 1) * tile_width % inner_width);
2736 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2737 inner_sy + (y - 1) * tile_height % inner_height);
2740 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2743 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2747 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2748 int x, int y, int xsize, int ysize,
2751 int font_width = getFontWidth(font_nr);
2752 int font_height = getFontHeight(font_nr);
2754 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2755 font_width, font_height);
2758 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2760 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2761 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2762 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2763 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2764 boolean no_delay = (tape.warp_forward);
2765 unsigned int anim_delay = 0;
2766 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2767 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2768 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2769 int font_width = getFontWidth(font_nr);
2770 int font_height = getFontHeight(font_nr);
2771 int max_xsize = level.envelope[envelope_nr].xsize;
2772 int max_ysize = level.envelope[envelope_nr].ysize;
2773 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2774 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2775 int xend = max_xsize;
2776 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2777 int xstep = (xstart < xend ? 1 : 0);
2778 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2780 int end = MAX(xend - xstart, yend - ystart);
2783 for (i = start; i <= end; i++)
2785 int last_frame = end; // last frame of this "for" loop
2786 int x = xstart + i * xstep;
2787 int y = ystart + i * ystep;
2788 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2789 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2790 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2791 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2794 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2796 BlitScreenToBitmap(backbuffer);
2798 SetDrawtoField(DRAW_TO_BACKBUFFER);
2800 for (yy = 0; yy < ysize; yy++)
2801 for (xx = 0; xx < xsize; xx++)
2802 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2804 DrawTextBuffer(sx + font_width, sy + font_height,
2805 level.envelope[envelope_nr].text, font_nr, max_xsize,
2806 xsize - 2, ysize - 2, 0, mask_mode,
2807 level.envelope[envelope_nr].autowrap,
2808 level.envelope[envelope_nr].centered, FALSE);
2810 redraw_mask |= REDRAW_FIELD;
2813 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2816 ClearAutoRepeatKeyEvents();
2819 void ShowEnvelope(int envelope_nr)
2821 int element = EL_ENVELOPE_1 + envelope_nr;
2822 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2823 int sound_opening = element_info[element].sound[ACTION_OPENING];
2824 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2825 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2826 boolean no_delay = (tape.warp_forward);
2827 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2828 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2829 int anim_mode = graphic_info[graphic].anim_mode;
2830 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2831 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2833 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2835 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2837 if (anim_mode == ANIM_DEFAULT)
2838 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2840 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2843 Delay(wait_delay_value);
2845 WaitForEventToContinue();
2847 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2849 if (anim_mode != ANIM_NONE)
2850 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2852 if (anim_mode == ANIM_DEFAULT)
2853 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2855 game.envelope_active = FALSE;
2857 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2859 redraw_mask |= REDRAW_FIELD;
2863 static void setRequestBasePosition(int *x, int *y)
2865 int sx_base, sy_base;
2867 if (request.x != -1)
2868 sx_base = request.x;
2869 else if (request.align == ALIGN_LEFT)
2871 else if (request.align == ALIGN_RIGHT)
2872 sx_base = SX + SXSIZE;
2874 sx_base = SX + SXSIZE / 2;
2876 if (request.y != -1)
2877 sy_base = request.y;
2878 else if (request.valign == VALIGN_TOP)
2880 else if (request.valign == VALIGN_BOTTOM)
2881 sy_base = SY + SYSIZE;
2883 sy_base = SY + SYSIZE / 2;
2889 static void setRequestPositionExt(int *x, int *y, int width, int height,
2890 boolean add_border_size)
2892 int border_size = request.border_size;
2893 int sx_base, sy_base;
2896 setRequestBasePosition(&sx_base, &sy_base);
2898 if (request.align == ALIGN_LEFT)
2900 else if (request.align == ALIGN_RIGHT)
2901 sx = sx_base - width;
2903 sx = sx_base - width / 2;
2905 if (request.valign == VALIGN_TOP)
2907 else if (request.valign == VALIGN_BOTTOM)
2908 sy = sy_base - height;
2910 sy = sy_base - height / 2;
2912 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2913 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2915 if (add_border_size)
2925 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2927 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2930 static void DrawEnvelopeRequest(char *text)
2932 char *text_final = text;
2933 char *text_door_style = NULL;
2934 int graphic = IMG_BACKGROUND_REQUEST;
2935 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2936 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2937 int font_nr = FONT_REQUEST;
2938 int font_width = getFontWidth(font_nr);
2939 int font_height = getFontHeight(font_nr);
2940 int border_size = request.border_size;
2941 int line_spacing = request.line_spacing;
2942 int line_height = font_height + line_spacing;
2943 int max_text_width = request.width - 2 * border_size;
2944 int max_text_height = request.height - 2 * border_size;
2945 int line_length = max_text_width / font_width;
2946 int max_lines = max_text_height / line_height;
2947 int text_width = line_length * font_width;
2948 int width = request.width;
2949 int height = request.height;
2950 int tile_size = MAX(request.step_offset, 1);
2951 int x_steps = width / tile_size;
2952 int y_steps = height / tile_size;
2953 int sx_offset = border_size;
2954 int sy_offset = border_size;
2958 if (request.centered)
2959 sx_offset = (request.width - text_width) / 2;
2961 if (request.wrap_single_words && !request.autowrap)
2963 char *src_text_ptr, *dst_text_ptr;
2965 text_door_style = checked_malloc(2 * strlen(text) + 1);
2967 src_text_ptr = text;
2968 dst_text_ptr = text_door_style;
2970 while (*src_text_ptr)
2972 if (*src_text_ptr == ' ' ||
2973 *src_text_ptr == '?' ||
2974 *src_text_ptr == '!')
2975 *dst_text_ptr++ = '\n';
2977 if (*src_text_ptr != ' ')
2978 *dst_text_ptr++ = *src_text_ptr;
2983 *dst_text_ptr = '\0';
2985 text_final = text_door_style;
2988 setRequestPosition(&sx, &sy, FALSE);
2990 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2992 for (y = 0; y < y_steps; y++)
2993 for (x = 0; x < x_steps; x++)
2994 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2995 x, y, x_steps, y_steps,
2996 tile_size, tile_size);
2998 // force DOOR font inside door area
2999 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3001 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3002 line_length, -1, max_lines, line_spacing, mask_mode,
3003 request.autowrap, request.centered, FALSE);
3007 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3008 RedrawGadget(tool_gadget[i]);
3010 // store readily prepared envelope request for later use when animating
3011 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3013 if (text_door_style)
3014 free(text_door_style);
3017 static void AnimateEnvelopeRequest(int anim_mode, int action)
3019 int graphic = IMG_BACKGROUND_REQUEST;
3020 boolean draw_masked = graphic_info[graphic].draw_masked;
3021 int delay_value_normal = request.step_delay;
3022 int delay_value_fast = delay_value_normal / 2;
3023 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3024 boolean no_delay = (tape.warp_forward);
3025 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3026 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3027 unsigned int anim_delay = 0;
3029 int tile_size = MAX(request.step_offset, 1);
3030 int max_xsize = request.width / tile_size;
3031 int max_ysize = request.height / tile_size;
3032 int max_xsize_inner = max_xsize - 2;
3033 int max_ysize_inner = max_ysize - 2;
3035 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3036 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3037 int xend = max_xsize_inner;
3038 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3039 int xstep = (xstart < xend ? 1 : 0);
3040 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3042 int end = MAX(xend - xstart, yend - ystart);
3045 if (setup.quick_doors)
3052 for (i = start; i <= end; i++)
3054 int last_frame = end; // last frame of this "for" loop
3055 int x = xstart + i * xstep;
3056 int y = ystart + i * ystep;
3057 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3058 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3059 int xsize_size_left = (xsize - 1) * tile_size;
3060 int ysize_size_top = (ysize - 1) * tile_size;
3061 int max_xsize_pos = (max_xsize - 1) * tile_size;
3062 int max_ysize_pos = (max_ysize - 1) * tile_size;
3063 int width = xsize * tile_size;
3064 int height = ysize * tile_size;
3069 setRequestPosition(&src_x, &src_y, FALSE);
3070 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3072 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3074 for (yy = 0; yy < 2; yy++)
3076 for (xx = 0; xx < 2; xx++)
3078 int src_xx = src_x + xx * max_xsize_pos;
3079 int src_yy = src_y + yy * max_ysize_pos;
3080 int dst_xx = dst_x + xx * xsize_size_left;
3081 int dst_yy = dst_y + yy * ysize_size_top;
3082 int xx_size = (xx ? tile_size : xsize_size_left);
3083 int yy_size = (yy ? tile_size : ysize_size_top);
3086 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3087 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3089 BlitBitmap(bitmap_db_store_2, backbuffer,
3090 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3094 redraw_mask |= REDRAW_FIELD;
3098 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3101 ClearAutoRepeatKeyEvents();
3104 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3106 int graphic = IMG_BACKGROUND_REQUEST;
3107 int sound_opening = SND_REQUEST_OPENING;
3108 int sound_closing = SND_REQUEST_CLOSING;
3109 int anim_mode_1 = request.anim_mode; // (higher priority)
3110 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3111 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3112 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3113 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3115 if (game_status == GAME_MODE_PLAYING)
3116 BlitScreenToBitmap(backbuffer);
3118 SetDrawtoField(DRAW_TO_BACKBUFFER);
3120 // SetDrawBackgroundMask(REDRAW_NONE);
3122 if (action == ACTION_OPENING)
3124 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3126 if (req_state & REQ_ASK)
3128 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3129 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3131 else if (req_state & REQ_CONFIRM)
3133 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3135 else if (req_state & REQ_PLAYER)
3137 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3138 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3139 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3140 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3143 DrawEnvelopeRequest(text);
3146 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3148 if (action == ACTION_OPENING)
3150 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3152 if (anim_mode == ANIM_DEFAULT)
3153 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3155 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3159 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3161 if (anim_mode != ANIM_NONE)
3162 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3164 if (anim_mode == ANIM_DEFAULT)
3165 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3168 game.envelope_active = FALSE;
3170 if (action == ACTION_CLOSING)
3171 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3173 // SetDrawBackgroundMask(last_draw_background_mask);
3175 redraw_mask |= REDRAW_FIELD;
3179 if (action == ACTION_CLOSING &&
3180 game_status == GAME_MODE_PLAYING &&
3181 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3182 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3185 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3187 if (IS_MM_WALL(element))
3189 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3195 int graphic = el2preimg(element);
3197 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3198 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3203 void DrawLevel(int draw_background_mask)
3207 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3208 SetDrawBackgroundMask(draw_background_mask);
3212 for (x = BX1; x <= BX2; x++)
3213 for (y = BY1; y <= BY2; y++)
3214 DrawScreenField(x, y);
3216 redraw_mask |= REDRAW_FIELD;
3219 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3224 for (x = 0; x < size_x; x++)
3225 for (y = 0; y < size_y; y++)
3226 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3228 redraw_mask |= REDRAW_FIELD;
3231 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3235 for (x = 0; x < size_x; x++)
3236 for (y = 0; y < size_y; y++)
3237 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3239 redraw_mask |= REDRAW_FIELD;
3242 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3244 boolean show_level_border = (BorderElement != EL_EMPTY);
3245 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3246 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3247 int tile_size = preview.tile_size;
3248 int preview_width = preview.xsize * tile_size;
3249 int preview_height = preview.ysize * tile_size;
3250 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3251 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3252 int real_preview_width = real_preview_xsize * tile_size;
3253 int real_preview_height = real_preview_ysize * tile_size;
3254 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3255 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3258 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3261 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3263 dst_x += (preview_width - real_preview_width) / 2;
3264 dst_y += (preview_height - real_preview_height) / 2;
3266 for (x = 0; x < real_preview_xsize; x++)
3268 for (y = 0; y < real_preview_ysize; y++)
3270 int lx = from_x + x + (show_level_border ? -1 : 0);
3271 int ly = from_y + y + (show_level_border ? -1 : 0);
3272 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3273 getBorderElement(lx, ly));
3275 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3276 element, tile_size);
3280 redraw_mask |= REDRAW_FIELD;
3283 #define MICROLABEL_EMPTY 0
3284 #define MICROLABEL_LEVEL_NAME 1
3285 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3286 #define MICROLABEL_LEVEL_AUTHOR 3
3287 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3288 #define MICROLABEL_IMPORTED_FROM 5
3289 #define MICROLABEL_IMPORTED_BY_HEAD 6
3290 #define MICROLABEL_IMPORTED_BY 7
3292 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3294 int max_text_width = SXSIZE;
3295 int font_width = getFontWidth(font_nr);
3297 if (pos->align == ALIGN_CENTER)
3298 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3299 else if (pos->align == ALIGN_RIGHT)
3300 max_text_width = pos->x;
3302 max_text_width = SXSIZE - pos->x;
3304 return max_text_width / font_width;
3307 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3309 char label_text[MAX_OUTPUT_LINESIZE + 1];
3310 int max_len_label_text;
3311 int font_nr = pos->font;
3314 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3317 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3318 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3319 mode == MICROLABEL_IMPORTED_BY_HEAD)
3320 font_nr = pos->font_alt;
3322 max_len_label_text = getMaxTextLength(pos, font_nr);
3324 if (pos->size != -1)
3325 max_len_label_text = pos->size;
3327 for (i = 0; i < max_len_label_text; i++)
3328 label_text[i] = ' ';
3329 label_text[max_len_label_text] = '\0';
3331 if (strlen(label_text) > 0)
3332 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3335 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3336 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3337 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3338 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3339 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3340 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3341 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3342 max_len_label_text);
3343 label_text[max_len_label_text] = '\0';
3345 if (strlen(label_text) > 0)
3346 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3348 redraw_mask |= REDRAW_FIELD;
3351 static void DrawPreviewLevelLabel(int mode)
3353 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3356 static void DrawPreviewLevelInfo(int mode)
3358 if (mode == MICROLABEL_LEVEL_NAME)
3359 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3360 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3361 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3364 static void DrawPreviewLevelExt(boolean restart)
3366 static unsigned int scroll_delay = 0;
3367 static unsigned int label_delay = 0;
3368 static int from_x, from_y, scroll_direction;
3369 static int label_state, label_counter;
3370 unsigned int scroll_delay_value = preview.step_delay;
3371 boolean show_level_border = (BorderElement != EL_EMPTY);
3372 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3373 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3380 if (preview.anim_mode == ANIM_CENTERED)
3382 if (level_xsize > preview.xsize)
3383 from_x = (level_xsize - preview.xsize) / 2;
3384 if (level_ysize > preview.ysize)
3385 from_y = (level_ysize - preview.ysize) / 2;
3388 from_x += preview.xoffset;
3389 from_y += preview.yoffset;
3391 scroll_direction = MV_RIGHT;
3395 DrawPreviewLevelPlayfield(from_x, from_y);
3396 DrawPreviewLevelLabel(label_state);
3398 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3399 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3401 // initialize delay counters
3402 DelayReached(&scroll_delay, 0);
3403 DelayReached(&label_delay, 0);
3405 if (leveldir_current->name)
3407 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3408 char label_text[MAX_OUTPUT_LINESIZE + 1];
3409 int font_nr = pos->font;
3410 int max_len_label_text = getMaxTextLength(pos, font_nr);
3412 if (pos->size != -1)
3413 max_len_label_text = pos->size;
3415 strncpy(label_text, leveldir_current->name, max_len_label_text);
3416 label_text[max_len_label_text] = '\0';
3418 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3419 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3425 // scroll preview level, if needed
3426 if (preview.anim_mode != ANIM_NONE &&
3427 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3428 DelayReached(&scroll_delay, scroll_delay_value))
3430 switch (scroll_direction)
3435 from_x -= preview.step_offset;
3436 from_x = (from_x < 0 ? 0 : from_x);
3439 scroll_direction = MV_UP;
3443 if (from_x < level_xsize - preview.xsize)
3445 from_x += preview.step_offset;
3446 from_x = (from_x > level_xsize - preview.xsize ?
3447 level_xsize - preview.xsize : from_x);
3450 scroll_direction = MV_DOWN;
3456 from_y -= preview.step_offset;
3457 from_y = (from_y < 0 ? 0 : from_y);
3460 scroll_direction = MV_RIGHT;
3464 if (from_y < level_ysize - preview.ysize)
3466 from_y += preview.step_offset;
3467 from_y = (from_y > level_ysize - preview.ysize ?
3468 level_ysize - preview.ysize : from_y);
3471 scroll_direction = MV_LEFT;
3478 DrawPreviewLevelPlayfield(from_x, from_y);
3481 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3482 // redraw micro level label, if needed
3483 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3484 !strEqual(level.author, ANONYMOUS_NAME) &&
3485 !strEqual(level.author, leveldir_current->name) &&
3486 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3488 int max_label_counter = 23;
3490 if (leveldir_current->imported_from != NULL &&
3491 strlen(leveldir_current->imported_from) > 0)
3492 max_label_counter += 14;
3493 if (leveldir_current->imported_by != NULL &&
3494 strlen(leveldir_current->imported_by) > 0)
3495 max_label_counter += 14;
3497 label_counter = (label_counter + 1) % max_label_counter;
3498 label_state = (label_counter >= 0 && label_counter <= 7 ?
3499 MICROLABEL_LEVEL_NAME :
3500 label_counter >= 9 && label_counter <= 12 ?
3501 MICROLABEL_LEVEL_AUTHOR_HEAD :
3502 label_counter >= 14 && label_counter <= 21 ?
3503 MICROLABEL_LEVEL_AUTHOR :
3504 label_counter >= 23 && label_counter <= 26 ?
3505 MICROLABEL_IMPORTED_FROM_HEAD :
3506 label_counter >= 28 && label_counter <= 35 ?
3507 MICROLABEL_IMPORTED_FROM :
3508 label_counter >= 37 && label_counter <= 40 ?
3509 MICROLABEL_IMPORTED_BY_HEAD :
3510 label_counter >= 42 && label_counter <= 49 ?
3511 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3513 if (leveldir_current->imported_from == NULL &&
3514 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3515 label_state == MICROLABEL_IMPORTED_FROM))
3516 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3517 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3519 DrawPreviewLevelLabel(label_state);
3523 void DrawPreviewPlayers(void)
3525 if (game_status != GAME_MODE_MAIN)
3528 // do not draw preview players if level preview redefined, but players aren't
3529 if (preview.redefined && !menu.main.preview_players.redefined)
3532 boolean player_found[MAX_PLAYERS];
3533 int num_players = 0;
3536 for (i = 0; i < MAX_PLAYERS; i++)
3537 player_found[i] = FALSE;
3539 // check which players can be found in the level (simple approach)
3540 for (x = 0; x < lev_fieldx; x++)
3542 for (y = 0; y < lev_fieldy; y++)
3544 int element = level.field[x][y];
3546 if (ELEM_IS_PLAYER(element))
3548 int player_nr = GET_PLAYER_NR(element);
3550 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3552 if (!player_found[player_nr])
3555 player_found[player_nr] = TRUE;
3560 struct TextPosInfo *pos = &menu.main.preview_players;
3561 int tile_size = pos->tile_size;
3562 int border_size = pos->border_size;
3563 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3564 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3565 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3566 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3567 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3568 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3569 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3570 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3571 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3572 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3573 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3574 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3576 // clear area in which the players will be drawn
3577 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3578 max_players_width, max_players_height);
3580 if (!network.enabled && !setup.team_mode)
3583 // only draw players if level is suited for team mode
3584 if (num_players < 2)
3587 // draw all players that were found in the level
3588 for (i = 0; i < MAX_PLAYERS; i++)
3590 if (player_found[i])
3592 int graphic = el2img(EL_PLAYER_1 + i);
3594 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3596 xpos += player_xoffset;
3597 ypos += player_yoffset;
3602 void DrawPreviewLevelInitial(void)
3604 DrawPreviewLevelExt(TRUE);
3605 DrawPreviewPlayers();
3608 void DrawPreviewLevelAnimation(void)
3610 DrawPreviewLevelExt(FALSE);
3613 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3614 int border_size, int font_nr)
3616 int graphic = el2img(EL_PLAYER_1 + player_nr);
3617 int font_height = getFontHeight(font_nr);
3618 int player_height = MAX(tile_size, font_height);
3619 int xoffset_text = tile_size + border_size;
3620 int yoffset_text = (player_height - font_height) / 2;
3621 int yoffset_graphic = (player_height - tile_size) / 2;
3622 char *player_name = getNetworkPlayerName(player_nr + 1);
3624 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3626 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3629 static void DrawNetworkPlayersExt(boolean force)
3631 if (game_status != GAME_MODE_MAIN)
3634 if (!network.connected && !force)
3637 // do not draw network players if level preview redefined, but players aren't
3638 if (preview.redefined && !menu.main.network_players.redefined)
3641 int num_players = 0;
3644 for (i = 0; i < MAX_PLAYERS; i++)
3645 if (stored_player[i].connected_network)
3648 struct TextPosInfo *pos = &menu.main.network_players;
3649 int tile_size = pos->tile_size;
3650 int border_size = pos->border_size;
3651 int xoffset_text = tile_size + border_size;
3652 int font_nr = pos->font;
3653 int font_width = getFontWidth(font_nr);
3654 int font_height = getFontHeight(font_nr);
3655 int player_height = MAX(tile_size, font_height);
3656 int player_yoffset = player_height + border_size;
3657 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3658 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3659 int all_players_height = num_players * player_yoffset - border_size;
3660 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3661 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3662 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3664 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3665 max_players_width, max_players_height);
3667 // first draw local network player ...
3668 for (i = 0; i < MAX_PLAYERS; i++)
3670 if (stored_player[i].connected_network &&
3671 stored_player[i].connected_locally)
3673 char *player_name = getNetworkPlayerName(i + 1);
3674 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3675 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3677 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3679 ypos += player_yoffset;
3683 // ... then draw all other network players
3684 for (i = 0; i < MAX_PLAYERS; i++)
3686 if (stored_player[i].connected_network &&
3687 !stored_player[i].connected_locally)
3689 char *player_name = getNetworkPlayerName(i + 1);
3690 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3691 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3693 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3695 ypos += player_yoffset;
3700 void DrawNetworkPlayers(void)
3702 DrawNetworkPlayersExt(FALSE);
3705 void ClearNetworkPlayers(void)
3707 DrawNetworkPlayersExt(TRUE);
3710 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3711 int graphic, int sync_frame,
3714 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3716 if (mask_mode == USE_MASKING)
3717 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3719 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3722 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3723 int graphic, int sync_frame, int mask_mode)
3725 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3727 if (mask_mode == USE_MASKING)
3728 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3730 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3733 static void DrawGraphicAnimation(int x, int y, int graphic)
3735 int lx = LEVELX(x), ly = LEVELY(y);
3737 if (!IN_SCR_FIELD(x, y))
3740 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3741 graphic, GfxFrame[lx][ly], NO_MASKING);
3743 MarkTileDirty(x, y);
3746 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3748 int lx = LEVELX(x), ly = LEVELY(y);
3750 if (!IN_SCR_FIELD(x, y))
3753 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3754 graphic, GfxFrame[lx][ly], NO_MASKING);
3755 MarkTileDirty(x, y);
3758 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3760 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3763 void DrawLevelElementAnimation(int x, int y, int element)
3765 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3767 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3770 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3772 int sx = SCREENX(x), sy = SCREENY(y);
3774 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3777 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3780 DrawGraphicAnimation(sx, sy, graphic);
3783 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3784 DrawLevelFieldCrumbled(x, y);
3786 if (GFX_CRUMBLED(Feld[x][y]))
3787 DrawLevelFieldCrumbled(x, y);
3791 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3793 int sx = SCREENX(x), sy = SCREENY(y);
3796 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3799 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3801 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3804 DrawGraphicAnimation(sx, sy, graphic);
3806 if (GFX_CRUMBLED(element))
3807 DrawLevelFieldCrumbled(x, y);
3810 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3812 if (player->use_murphy)
3814 // this works only because currently only one player can be "murphy" ...
3815 static int last_horizontal_dir = MV_LEFT;
3816 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3818 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3819 last_horizontal_dir = move_dir;
3821 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3823 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3825 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3831 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3834 static boolean equalGraphics(int graphic1, int graphic2)
3836 struct GraphicInfo *g1 = &graphic_info[graphic1];
3837 struct GraphicInfo *g2 = &graphic_info[graphic2];
3839 return (g1->bitmap == g2->bitmap &&
3840 g1->src_x == g2->src_x &&
3841 g1->src_y == g2->src_y &&
3842 g1->anim_frames == g2->anim_frames &&
3843 g1->anim_delay == g2->anim_delay &&
3844 g1->anim_mode == g2->anim_mode);
3847 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3851 DRAW_PLAYER_STAGE_INIT = 0,
3852 DRAW_PLAYER_STAGE_LAST_FIELD,
3853 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
3854 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3855 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3856 DRAW_PLAYER_STAGE_PLAYER,
3858 DRAW_PLAYER_STAGE_PLAYER,
3859 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3861 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
3862 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
3864 NUM_DRAW_PLAYER_STAGES
3867 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
3869 static int static_last_player_graphic[MAX_PLAYERS];
3870 static int static_last_player_frame[MAX_PLAYERS];
3871 static boolean static_player_is_opaque[MAX_PLAYERS];
3872 static boolean draw_player[MAX_PLAYERS];
3873 int pnr = player->index_nr;
3875 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3877 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
3878 static_last_player_frame[pnr] = player->Frame;
3879 static_player_is_opaque[pnr] = FALSE;
3881 draw_player[pnr] = TRUE;
3884 if (!draw_player[pnr])
3888 if (!IN_LEV_FIELD(player->jx, player->jy))
3890 printf("DrawPlayerField(): x = %d, y = %d\n", player->jx, player->jy);
3891 printf("DrawPlayerField(): This should never happen!\n");
3893 draw_player[pnr] = FALSE;
3899 int last_player_graphic = static_last_player_graphic[pnr];
3900 int last_player_frame = static_last_player_frame[pnr];
3901 boolean player_is_opaque = static_player_is_opaque[pnr];
3903 int jx = player->jx;
3904 int jy = player->jy;
3905 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
3906 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3907 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3908 int last_jx = (player->is_moving ? jx - dx : jx);
3909 int last_jy = (player->is_moving ? jy - dy : jy);
3910 int next_jx = jx + dx;
3911 int next_jy = jy + dy;
3912 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
3913 int sx = SCREENX(jx);
3914 int sy = SCREENY(jy);
3915 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
3916 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
3917 int element = Feld[jx][jy];
3918 int last_element = Feld[last_jx][last_jy];
3919 int action = (player->is_pushing ? ACTION_PUSHING :
3920 player->is_digging ? ACTION_DIGGING :
3921 player->is_collecting ? ACTION_COLLECTING :
3922 player->is_moving ? ACTION_MOVING :
3923 player->is_snapping ? ACTION_SNAPPING :
3924 player->is_dropping ? ACTION_DROPPING :
3925 player->is_waiting ? player->action_waiting :
3928 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3930 // ------------------------------------------------------------------------
3931 // initialize drawing the player
3932 // ------------------------------------------------------------------------
3934 draw_player[pnr] = FALSE;
3936 // GfxElement[][] is set to the element the player is digging or collecting;
3937 // remove also for off-screen player if the player is not moving anymore
3938 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3939 GfxElement[jx][jy] = EL_UNDEFINED;
3941 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3944 if (element == EL_EXPLOSION)
3947 InitPlayerGfxAnimation(player, action, move_dir);
3949 draw_player[pnr] = TRUE;
3951 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
3953 // ------------------------------------------------------------------------
3954 // draw things in the field the player is leaving, if needed
3955 // ------------------------------------------------------------------------
3957 if (!IN_SCR_FIELD(sx, sy))
3958 draw_player[pnr] = FALSE;
3960 if (!player->is_moving)
3963 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3965 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3967 if (last_element == EL_DYNAMITE_ACTIVE ||
3968 last_element == EL_EM_DYNAMITE_ACTIVE ||
3969 last_element == EL_SP_DISK_RED_ACTIVE)
3970 DrawDynamite(last_jx, last_jy);
3972 DrawLevelFieldThruMask(last_jx, last_jy);
3974 else if (last_element == EL_DYNAMITE_ACTIVE ||
3975 last_element == EL_EM_DYNAMITE_ACTIVE ||
3976 last_element == EL_SP_DISK_RED_ACTIVE)
3977 DrawDynamite(last_jx, last_jy);
3979 DrawLevelField(last_jx, last_jy);
3981 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3982 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3984 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
3986 // ------------------------------------------------------------------------
3987 // draw things behind the player, if needed
3988 // ------------------------------------------------------------------------
3992 DrawLevelElement(jx, jy, Back[jx][jy]);
3997 if (IS_ACTIVE_BOMB(element))
3999 DrawLevelElement(jx, jy, EL_EMPTY);
4004 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4006 int old_element = GfxElement[jx][jy];
4007 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4008 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4010 if (GFX_CRUMBLED(old_element))
4011 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4013 DrawGraphic(sx, sy, old_graphic, frame);
4015 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4016 static_player_is_opaque[pnr] = TRUE;
4020 GfxElement[jx][jy] = EL_UNDEFINED;
4022 // make sure that pushed elements are drawn with correct frame rate
4023 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4025 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4026 GfxFrame[jx][jy] = player->StepFrame;
4028 DrawLevelField(jx, jy);
4031 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4033 // ------------------------------------------------------------------------
4034 // draw things the player is pushing, if needed
4035 // ------------------------------------------------------------------------
4037 if (!player->is_pushing || !player->is_moving)
4040 int gfx_frame = GfxFrame[jx][jy];
4042 if (!IS_MOVING(jx, jy)) // push movement already finished
4044 element = Feld[next_jx][next_jy];
4045 gfx_frame = GfxFrame[next_jx][next_jy];
4048 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4049 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4050 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4052 // draw background element under pushed element (like the Sokoban field)
4053 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4055 // this allows transparent pushing animation over non-black background
4058 DrawLevelElement(jx, jy, Back[jx][jy]);
4060 DrawLevelElement(jx, jy, EL_EMPTY);
4062 if (Back[next_jx][next_jy])
4063 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4065 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4067 else if (Back[next_jx][next_jy])
4068 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4070 int px = SCREENX(jx), py = SCREENY(jy);
4071 int pxx = (TILEX - ABS(sxx)) * dx;
4072 int pyy = (TILEY - ABS(syy)) * dy;
4075 // do not draw (EM style) pushing animation when pushing is finished
4076 // (two-tile animations usually do not contain start and end frame)
4077 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4078 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
4080 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4082 // masked drawing is needed for EMC style (double) movement graphics
4083 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4084 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4087 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4089 // ------------------------------------------------------------------------
4090 // draw player himself
4091 // ------------------------------------------------------------------------
4093 int graphic = getPlayerGraphic(player, move_dir);
4095 // in the case of changed player action or direction, prevent the current
4096 // animation frame from being restarted for identical animations
4097 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4098 player->Frame = last_player_frame;
4100 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4102 if (player_is_opaque)
4103 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4105 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4107 if (SHIELD_ON(player))
4109 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4110 IMG_SHIELD_NORMAL_ACTIVE);
4111 frame = getGraphicAnimationFrame(graphic, -1);
4113 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4116 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4118 // ------------------------------------------------------------------------
4119 // draw things in front of player (active dynamite or dynabombs)
4120 // ------------------------------------------------------------------------
4122 if (IS_ACTIVE_BOMB(element))
4124 int graphic = el2img(element);
4125 int frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4127 if (game.emulation == EMU_SUPAPLEX)
4128 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4130 DrawGraphicThruMask(sx, sy, graphic, frame);
4133 if (player_is_moving && last_element == EL_EXPLOSION)
4135 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4136 GfxElement[last_jx][last_jy] : EL_EMPTY);
4137 int graphic = el_act2img(element, ACTION_EXPLODING);
4138 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4139 int phase = ExplodePhase[last_jx][last_jy] - 1;
4140 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4143 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4146 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4148 // ------------------------------------------------------------------------
4149 // draw elements the player is just walking/passing through/under
4150 // ------------------------------------------------------------------------
4152 if (player_is_moving)
4154 // handle the field the player is leaving ...
4155 if (IS_ACCESSIBLE_INSIDE(last_element))
4156 DrawLevelField(last_jx, last_jy);
4157 else if (IS_ACCESSIBLE_UNDER(last_element))
4158 DrawLevelFieldThruMask(last_jx, last_jy);
4161 // do not redraw accessible elements if the player is just pushing them
4162 if (!player_is_moving || !player->is_pushing)
4164 // ... and the field the player is entering
4165 if (IS_ACCESSIBLE_INSIDE(element))
4166 DrawLevelField(jx, jy);
4167 else if (IS_ACCESSIBLE_UNDER(element))
4168 DrawLevelFieldThruMask(jx, jy);
4171 MarkTileDirty(sx, sy);
4175 void DrawPlayer(struct PlayerInfo *player)
4179 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4180 DrawPlayerExt(player, i);
4183 void DrawAllPlayers(void)
4187 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4188 for (j = 0; j < MAX_PLAYERS; j++)
4189 if (stored_player[j].active)
4190 DrawPlayerExt(&stored_player[j], i);
4193 void DrawPlayerField(int x, int y)
4195 if (!IS_PLAYER(x, y))
4198 DrawPlayer(PLAYERINFO(x, y));
4201 // ----------------------------------------------------------------------------
4203 void WaitForEventToContinue(void)
4205 boolean still_wait = TRUE;
4207 if (program.headless)
4210 // simulate releasing mouse button over last gadget, if still pressed
4212 HandleGadgets(-1, -1, 0);
4214 button_status = MB_RELEASED;
4222 if (NextValidEvent(&event))
4226 case EVENT_BUTTONRELEASE:
4227 case EVENT_KEYPRESS:
4228 case SDL_CONTROLLERBUTTONDOWN:
4229 case SDL_JOYBUTTONDOWN:
4233 case EVENT_KEYRELEASE:
4234 ClearPlayerAction();
4238 HandleOtherEvents(&event);
4242 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4251 #define MAX_REQUEST_LINES 13
4252 #define MAX_REQUEST_LINE_FONT1_LEN 7
4253 #define MAX_REQUEST_LINE_FONT2_LEN 10
4255 static int RequestHandleEvents(unsigned int req_state)
4257 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4259 int width = request.width;
4260 int height = request.height;
4264 // when showing request dialog after game ended, deactivate game panel
4265 if (game_just_ended)
4266 game.panel.active = FALSE;
4268 game.request_active = TRUE;
4270 setRequestPosition(&sx, &sy, FALSE);
4272 button_status = MB_RELEASED;
4274 request_gadget_id = -1;
4279 if (game_just_ended)
4281 // the MM game engine does not use a special (scrollable) field buffer
4282 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4283 SetDrawtoField(DRAW_TO_FIELDBUFFER);
4285 HandleGameActions();
4287 SetDrawtoField(DRAW_TO_BACKBUFFER);
4289 if (global.use_envelope_request)
4291 // copy current state of request area to middle of playfield area
4292 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4300 while (NextValidEvent(&event))
4304 case EVENT_BUTTONPRESS:
4305 case EVENT_BUTTONRELEASE:
4306 case EVENT_MOTIONNOTIFY:
4310 if (event.type == EVENT_MOTIONNOTIFY)
4315 motion_status = TRUE;
4316 mx = ((MotionEvent *) &event)->x;
4317 my = ((MotionEvent *) &event)->y;
4321 motion_status = FALSE;
4322 mx = ((ButtonEvent *) &event)->x;
4323 my = ((ButtonEvent *) &event)->y;
4324 if (event.type == EVENT_BUTTONPRESS)
4325 button_status = ((ButtonEvent *) &event)->button;
4327 button_status = MB_RELEASED;
4330 // this sets 'request_gadget_id'
4331 HandleGadgets(mx, my, button_status);
4333 switch (request_gadget_id)
4335 case TOOL_CTRL_ID_YES:
4338 case TOOL_CTRL_ID_NO:
4341 case TOOL_CTRL_ID_CONFIRM:
4342 result = TRUE | FALSE;
4345 case TOOL_CTRL_ID_PLAYER_1:
4348 case TOOL_CTRL_ID_PLAYER_2:
4351 case TOOL_CTRL_ID_PLAYER_3:
4354 case TOOL_CTRL_ID_PLAYER_4:
4365 case SDL_WINDOWEVENT:
4366 HandleWindowEvent((WindowEvent *) &event);
4369 case SDL_APP_WILLENTERBACKGROUND:
4370 case SDL_APP_DIDENTERBACKGROUND:
4371 case SDL_APP_WILLENTERFOREGROUND:
4372 case SDL_APP_DIDENTERFOREGROUND:
4373 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4376 case EVENT_KEYPRESS:
4378 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4383 if (req_state & REQ_CONFIRM)
4392 #if defined(KSYM_Rewind)
4393 case KSYM_Rewind: // for Amazon Fire TV remote
4402 #if defined(KSYM_FastForward)
4403 case KSYM_FastForward: // for Amazon Fire TV remote
4409 HandleKeysDebug(key, KEY_PRESSED);
4413 if (req_state & REQ_PLAYER)
4415 int old_player_nr = setup.network_player_nr;
4418 result = old_player_nr + 1;
4423 result = old_player_nr + 1;
4454 case EVENT_KEYRELEASE:
4455 ClearPlayerAction();
4458 case SDL_CONTROLLERBUTTONDOWN:
4459 switch (event.cbutton.button)
4461 case SDL_CONTROLLER_BUTTON_A:
4462 case SDL_CONTROLLER_BUTTON_X:
4463 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4464 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4468 case SDL_CONTROLLER_BUTTON_B:
4469 case SDL_CONTROLLER_BUTTON_Y:
4470 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4471 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4472 case SDL_CONTROLLER_BUTTON_BACK:
4477 if (req_state & REQ_PLAYER)
4479 int old_player_nr = setup.network_player_nr;
4482 result = old_player_nr + 1;
4484 switch (event.cbutton.button)
4486 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4487 case SDL_CONTROLLER_BUTTON_Y:
4491 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4492 case SDL_CONTROLLER_BUTTON_B:
4496 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4497 case SDL_CONTROLLER_BUTTON_A:
4501 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4502 case SDL_CONTROLLER_BUTTON_X:
4513 case SDL_CONTROLLERBUTTONUP:
4514 HandleJoystickEvent(&event);
4515 ClearPlayerAction();
4519 HandleOtherEvents(&event);
4524 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4526 int joy = AnyJoystick();
4528 if (joy & JOY_BUTTON_1)
4530 else if (joy & JOY_BUTTON_2)
4533 else if (AnyJoystick())
4535 int joy = AnyJoystick();
4537 if (req_state & REQ_PLAYER)
4541 else if (joy & JOY_RIGHT)
4543 else if (joy & JOY_DOWN)
4545 else if (joy & JOY_LEFT)
4550 if (game_just_ended)
4552 if (global.use_envelope_request)
4554 // copy back current state of pressed buttons inside request area
4555 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4562 game.request_active = FALSE;
4567 static boolean RequestDoor(char *text, unsigned int req_state)
4569 unsigned int old_door_state;
4570 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4571 int font_nr = FONT_TEXT_2;
4576 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4578 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4579 font_nr = FONT_TEXT_1;
4582 if (game_status == GAME_MODE_PLAYING)
4583 BlitScreenToBitmap(backbuffer);
4585 // disable deactivated drawing when quick-loading level tape recording
4586 if (tape.playing && tape.deactivate_display)
4587 TapeDeactivateDisplayOff(TRUE);
4589 SetMouseCursor(CURSOR_DEFAULT);
4591 // pause network game while waiting for request to answer
4592 if (network.enabled &&
4593 game_status == GAME_MODE_PLAYING &&
4594 !game.all_players_gone &&
4595 req_state & REQUEST_WAIT_FOR_INPUT)
4596 SendToServer_PausePlaying();
4598 old_door_state = GetDoorState();
4600 // simulate releasing mouse button over last gadget, if still pressed
4602 HandleGadgets(-1, -1, 0);
4606 // draw released gadget before proceeding
4609 if (old_door_state & DOOR_OPEN_1)
4611 CloseDoor(DOOR_CLOSE_1);
4613 // save old door content
4614 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4615 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4618 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4619 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4621 // clear door drawing field
4622 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4624 // force DOOR font inside door area
4625 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4627 // write text for request
4628 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4630 char text_line[max_request_line_len + 1];
4636 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4638 tc = *(text_ptr + tx);
4639 // if (!tc || tc == ' ')
4640 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4644 if ((tc == '?' || tc == '!') && tl == 0)
4654 strncpy(text_line, text_ptr, tl);
4657 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4658 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4659 text_line, font_nr);
4661 text_ptr += tl + (tc == ' ' ? 1 : 0);
4662 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4667 if (req_state & REQ_ASK)
4669 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4670 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4672 else if (req_state & REQ_CONFIRM)
4674 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4676 else if (req_state & REQ_PLAYER)
4678 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4679 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4680 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4681 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4684 // copy request gadgets to door backbuffer
4685 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4687 OpenDoor(DOOR_OPEN_1);
4689 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4691 if (game_status == GAME_MODE_PLAYING)
4693 SetPanelBackground();
4694 SetDrawBackgroundMask(REDRAW_DOOR_1);
4698 SetDrawBackgroundMask(REDRAW_FIELD);
4704 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4706 // ---------- handle request buttons ----------
4707 result = RequestHandleEvents(req_state);
4711 if (!(req_state & REQ_STAY_OPEN))
4713 CloseDoor(DOOR_CLOSE_1);
4715 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4716 (req_state & REQ_REOPEN))
4717 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4722 if (game_status == GAME_MODE_PLAYING)
4724 SetPanelBackground();
4725 SetDrawBackgroundMask(REDRAW_DOOR_1);
4729 SetDrawBackgroundMask(REDRAW_FIELD);
4732 // continue network game after request
4733 if (network.enabled &&
4734 game_status == GAME_MODE_PLAYING &&
4735 !game.all_players_gone &&
4736 req_state & REQUEST_WAIT_FOR_INPUT)
4737 SendToServer_ContinuePlaying();
4739 // restore deactivated drawing when quick-loading level tape recording
4740 if (tape.playing && tape.deactivate_display)
4741 TapeDeactivateDisplayOn();
4746 static boolean RequestEnvelope(char *text, unsigned int req_state)
4750 if (game_status == GAME_MODE_PLAYING)
4751 BlitScreenToBitmap(backbuffer);
4753 // disable deactivated drawing when quick-loading level tape recording
4754 if (tape.playing && tape.deactivate_display)
4755 TapeDeactivateDisplayOff(TRUE);
4757 SetMouseCursor(CURSOR_DEFAULT);
4759 // pause network game while waiting for request to answer
4760 if (network.enabled &&
4761 game_status == GAME_MODE_PLAYING &&
4762 !game.all_players_gone &&
4763 req_state & REQUEST_WAIT_FOR_INPUT)
4764 SendToServer_PausePlaying();
4766 // simulate releasing mouse button over last gadget, if still pressed
4768 HandleGadgets(-1, -1, 0);
4772 // (replace with setting corresponding request background)
4773 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4774 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4776 // clear door drawing field
4777 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4779 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4781 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4783 if (game_status == GAME_MODE_PLAYING)
4785 SetPanelBackground();
4786 SetDrawBackgroundMask(REDRAW_DOOR_1);
4790 SetDrawBackgroundMask(REDRAW_FIELD);
4796 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4798 // ---------- handle request buttons ----------
4799 result = RequestHandleEvents(req_state);
4803 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4807 if (game_status == GAME_MODE_PLAYING)
4809 SetPanelBackground();
4810 SetDrawBackgroundMask(REDRAW_DOOR_1);
4814 SetDrawBackgroundMask(REDRAW_FIELD);
4817 // continue network game after request
4818 if (network.enabled &&
4819 game_status == GAME_MODE_PLAYING &&
4820 !game.all_players_gone &&
4821 req_state & REQUEST_WAIT_FOR_INPUT)
4822 SendToServer_ContinuePlaying();
4824 // restore deactivated drawing when quick-loading level tape recording
4825 if (tape.playing && tape.deactivate_display)
4826 TapeDeactivateDisplayOn();
4831 boolean Request(char *text, unsigned int req_state)
4833 boolean overlay_enabled = GetOverlayEnabled();
4836 SetOverlayEnabled(FALSE);
4838 if (global.use_envelope_request)
4839 result = RequestEnvelope(text, req_state);
4841 result = RequestDoor(text, req_state);
4843 SetOverlayEnabled(overlay_enabled);
4848 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4850 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4851 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4854 if (dpo1->sort_priority != dpo2->sort_priority)
4855 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4857 compare_result = dpo1->nr - dpo2->nr;
4859 return compare_result;
4862 void InitGraphicCompatibilityInfo_Doors(void)
4868 struct DoorInfo *door;
4872 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4873 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4875 { -1, -1, -1, NULL }
4877 struct Rect door_rect_list[] =
4879 { DX, DY, DXSIZE, DYSIZE },
4880 { VX, VY, VXSIZE, VYSIZE }
4884 for (i = 0; doors[i].door_token != -1; i++)
4886 int door_token = doors[i].door_token;
4887 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4888 int part_1 = doors[i].part_1;
4889 int part_8 = doors[i].part_8;
4890 int part_2 = part_1 + 1;
4891 int part_3 = part_1 + 2;
4892 struct DoorInfo *door = doors[i].door;
4893 struct Rect *door_rect = &door_rect_list[door_index];
4894 boolean door_gfx_redefined = FALSE;
4896 // check if any door part graphic definitions have been redefined
4898 for (j = 0; door_part_controls[j].door_token != -1; j++)
4900 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4901 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4903 if (dpc->door_token == door_token && fi->redefined)
4904 door_gfx_redefined = TRUE;
4907 // check for old-style door graphic/animation modifications
4909 if (!door_gfx_redefined)
4911 if (door->anim_mode & ANIM_STATIC_PANEL)
4913 door->panel.step_xoffset = 0;
4914 door->panel.step_yoffset = 0;
4917 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4919 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4920 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4921 int num_door_steps, num_panel_steps;
4923 // remove door part graphics other than the two default wings
4925 for (j = 0; door_part_controls[j].door_token != -1; j++)
4927 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4928 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4930 if (dpc->graphic >= part_3 &&
4931 dpc->graphic <= part_8)
4935 // set graphics and screen positions of the default wings
4937 g_part_1->width = door_rect->width;
4938 g_part_1->height = door_rect->height;
4939 g_part_2->width = door_rect->width;
4940 g_part_2->height = door_rect->height;
4941 g_part_2->src_x = door_rect->width;
4942 g_part_2->src_y = g_part_1->src_y;
4944 door->part_2.x = door->part_1.x;
4945 door->part_2.y = door->part_1.y;
4947 if (door->width != -1)
4949 g_part_1->width = door->width;
4950 g_part_2->width = door->width;
4952 // special treatment for graphics and screen position of right wing
4953 g_part_2->src_x += door_rect->width - door->width;
4954 door->part_2.x += door_rect->width - door->width;
4957 if (door->height != -1)
4959 g_part_1->height = door->height;
4960 g_part_2->height = door->height;
4962 // special treatment for graphics and screen position of bottom wing
4963 g_part_2->src_y += door_rect->height - door->height;
4964 door->part_2.y += door_rect->height - door->height;
4967 // set animation delays for the default wings and panels
4969 door->part_1.step_delay = door->step_delay;
4970 door->part_2.step_delay = door->step_delay;
4971 door->panel.step_delay = door->step_delay;
4973 // set animation draw order for the default wings
4975 door->part_1.sort_priority = 2; // draw left wing over ...
4976 door->part_2.sort_priority = 1; // ... right wing
4978 // set animation draw offset for the default wings
4980 if (door->anim_mode & ANIM_HORIZONTAL)
4982 door->part_1.step_xoffset = door->step_offset;
4983 door->part_1.step_yoffset = 0;
4984 door->part_2.step_xoffset = door->step_offset * -1;
4985 door->part_2.step_yoffset = 0;
4987 num_door_steps = g_part_1->width / door->step_offset;
4989 else // ANIM_VERTICAL
4991 door->part_1.step_xoffset = 0;
4992 door->part_1.step_yoffset = door->step_offset;
4993 door->part_2.step_xoffset = 0;
4994 door->part_2.step_yoffset = door->step_offset * -1;
4996 num_door_steps = g_part_1->height / door->step_offset;
4999 // set animation draw offset for the default panels
5001 if (door->step_offset > 1)
5003 num_panel_steps = 2 * door_rect->height / door->step_offset;
5004 door->panel.start_step = num_panel_steps - num_door_steps;
5005 door->panel.start_step_closing = door->panel.start_step;
5009 num_panel_steps = door_rect->height / door->step_offset;
5010 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5011 door->panel.start_step_closing = door->panel.start_step;
5012 door->panel.step_delay *= 2;
5019 void InitDoors(void)
5023 for (i = 0; door_part_controls[i].door_token != -1; i++)
5025 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5026 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5028 // initialize "start_step_opening" and "start_step_closing", if needed
5029 if (dpc->pos->start_step_opening == 0 &&
5030 dpc->pos->start_step_closing == 0)
5032 // dpc->pos->start_step_opening = dpc->pos->start_step;
5033 dpc->pos->start_step_closing = dpc->pos->start_step;
5036 // fill structure for door part draw order (sorted below)
5038 dpo->sort_priority = dpc->pos->sort_priority;
5041 // sort door part controls according to sort_priority and graphic number
5042 qsort(door_part_order, MAX_DOOR_PARTS,
5043 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5046 unsigned int OpenDoor(unsigned int door_state)
5048 if (door_state & DOOR_COPY_BACK)
5050 if (door_state & DOOR_OPEN_1)
5051 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5052 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5054 if (door_state & DOOR_OPEN_2)
5055 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5056 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5058 door_state &= ~DOOR_COPY_BACK;
5061 return MoveDoor(door_state);
5064 unsigned int CloseDoor(unsigned int door_state)
5066 unsigned int old_door_state = GetDoorState();
5068 if (!(door_state & DOOR_NO_COPY_BACK))
5070 if (old_door_state & DOOR_OPEN_1)
5071 BlitBitmap(backbuffer, bitmap_db_door_1,
5072 DX, DY, DXSIZE, DYSIZE, 0, 0);
5074 if (old_door_state & DOOR_OPEN_2)
5075 BlitBitmap(backbuffer, bitmap_db_door_2,
5076 VX, VY, VXSIZE, VYSIZE, 0, 0);
5078 door_state &= ~DOOR_NO_COPY_BACK;
5081 return MoveDoor(door_state);
5084 unsigned int GetDoorState(void)
5086 return MoveDoor(DOOR_GET_STATE);
5089 unsigned int SetDoorState(unsigned int door_state)
5091 return MoveDoor(door_state | DOOR_SET_STATE);
5094 static int euclid(int a, int b)
5096 return (b ? euclid(b, a % b) : a);
5099 unsigned int MoveDoor(unsigned int door_state)
5101 struct Rect door_rect_list[] =
5103 { DX, DY, DXSIZE, DYSIZE },
5104 { VX, VY, VXSIZE, VYSIZE }
5106 static int door1 = DOOR_CLOSE_1;
5107 static int door2 = DOOR_CLOSE_2;
5108 unsigned int door_delay = 0;
5109 unsigned int door_delay_value;
5112 if (door_state == DOOR_GET_STATE)
5113 return (door1 | door2);
5115 if (door_state & DOOR_SET_STATE)
5117 if (door_state & DOOR_ACTION_1)
5118 door1 = door_state & DOOR_ACTION_1;
5119 if (door_state & DOOR_ACTION_2)
5120 door2 = door_state & DOOR_ACTION_2;
5122 return (door1 | door2);
5125 if (!(door_state & DOOR_FORCE_REDRAW))
5127 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5128 door_state &= ~DOOR_OPEN_1;
5129 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5130 door_state &= ~DOOR_CLOSE_1;
5131 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5132 door_state &= ~DOOR_OPEN_2;
5133 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5134 door_state &= ~DOOR_CLOSE_2;
5137 if (global.autoplay_leveldir)
5139 door_state |= DOOR_NO_DELAY;
5140 door_state &= ~DOOR_CLOSE_ALL;
5143 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5144 door_state |= DOOR_NO_DELAY;
5146 if (door_state & DOOR_ACTION)
5148 boolean door_panel_drawn[NUM_DOORS];
5149 boolean panel_has_doors[NUM_DOORS];
5150 boolean door_part_skip[MAX_DOOR_PARTS];
5151 boolean door_part_done[MAX_DOOR_PARTS];
5152 boolean door_part_done_all;
5153 int num_steps[MAX_DOOR_PARTS];
5154 int max_move_delay = 0; // delay for complete animations of all doors
5155 int max_step_delay = 0; // delay (ms) between two animation frames
5156 int num_move_steps = 0; // number of animation steps for all doors
5157 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5158 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5159 int current_move_delay = 0;
5163 for (i = 0; i < NUM_DOORS; i++)
5164 panel_has_doors[i] = FALSE;
5166 for (i = 0; i < MAX_DOOR_PARTS; i++)
5168 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5169 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5170 int door_token = dpc->door_token;
5172 door_part_done[i] = FALSE;
5173 door_part_skip[i] = (!(door_state & door_token) ||
5177 for (i = 0; i < MAX_DOOR_PARTS; i++)
5179 int nr = door_part_order[i].nr;
5180 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5181 struct DoorPartPosInfo *pos = dpc->pos;
5182 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5183 int door_token = dpc->door_token;
5184 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5185 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5186 int step_xoffset = ABS(pos->step_xoffset);
5187 int step_yoffset = ABS(pos->step_yoffset);
5188 int step_delay = pos->step_delay;
5189 int current_door_state = door_state & door_token;
5190 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5191 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5192 boolean part_opening = (is_panel ? door_closing : door_opening);
5193 int start_step = (part_opening ? pos->start_step_opening :
5194 pos->start_step_closing);
5195 float move_xsize = (step_xoffset ? g->width : 0);
5196 float move_ysize = (step_yoffset ? g->height : 0);
5197 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5198 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5199 int move_steps = (move_xsteps && move_ysteps ?
5200 MIN(move_xsteps, move_ysteps) :
5201 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5202 int move_delay = move_steps * step_delay;
5204 if (door_part_skip[nr])
5207 max_move_delay = MAX(max_move_delay, move_delay);
5208 max_step_delay = (max_step_delay == 0 ? step_delay :
5209 euclid(max_step_delay, step_delay));
5210 num_steps[nr] = move_steps;
5214 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5216 panel_has_doors[door_index] = TRUE;
5220 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5222 num_move_steps = max_move_delay / max_step_delay;
5223 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5225 door_delay_value = max_step_delay;
5227 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5229 start = num_move_steps - 1;
5233 // opening door sound has priority over simultaneously closing door
5234 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5236 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5238 if (door_state & DOOR_OPEN_1)
5239 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5240 if (door_state & DOOR_OPEN_2)
5241 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5243 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5245 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5247 if (door_state & DOOR_CLOSE_1)
5248 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5249 if (door_state & DOOR_CLOSE_2)
5250 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5254 for (k = start; k < num_move_steps; k++)
5256 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5258 door_part_done_all = TRUE;
5260 for (i = 0; i < NUM_DOORS; i++)
5261 door_panel_drawn[i] = FALSE;
5263 for (i = 0; i < MAX_DOOR_PARTS; i++)
5265 int nr = door_part_order[i].nr;
5266 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5267 struct DoorPartPosInfo *pos = dpc->pos;
5268 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5269 int door_token = dpc->door_token;
5270 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5271 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5272 boolean is_panel_and_door_has_closed = FALSE;
5273 struct Rect *door_rect = &door_rect_list[door_index];
5274 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5276 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5277 int current_door_state = door_state & door_token;
5278 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5279 boolean door_closing = !door_opening;
5280 boolean part_opening = (is_panel ? door_closing : door_opening);
5281 boolean part_closing = !part_opening;
5282 int start_step = (part_opening ? pos->start_step_opening :
5283 pos->start_step_closing);
5284 int step_delay = pos->step_delay;
5285 int step_factor = step_delay / max_step_delay;
5286 int k1 = (step_factor ? k / step_factor + 1 : k);
5287 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5288 int kk = MAX(0, k2);
5291 int src_x, src_y, src_xx, src_yy;
5292 int dst_x, dst_y, dst_xx, dst_yy;
5295 if (door_part_skip[nr])
5298 if (!(door_state & door_token))
5306 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5307 int kk_door = MAX(0, k2_door);
5308 int sync_frame = kk_door * door_delay_value;
5309 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5311 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5312 &g_src_x, &g_src_y);
5317 if (!door_panel_drawn[door_index])
5319 ClearRectangle(drawto, door_rect->x, door_rect->y,
5320 door_rect->width, door_rect->height);
5322 door_panel_drawn[door_index] = TRUE;
5325 // draw opening or closing door parts
5327 if (pos->step_xoffset < 0) // door part on right side
5330 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5333 if (dst_xx + width > door_rect->width)
5334 width = door_rect->width - dst_xx;
5336 else // door part on left side
5339 dst_xx = pos->x - kk * pos->step_xoffset;
5343 src_xx = ABS(dst_xx);
5347 width = g->width - src_xx;
5349 if (width > door_rect->width)
5350 width = door_rect->width;
5352 // printf("::: k == %d [%d] \n", k, start_step);
5355 if (pos->step_yoffset < 0) // door part on bottom side
5358 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5361 if (dst_yy + height > door_rect->height)
5362 height = door_rect->height - dst_yy;
5364 else // door part on top side
5367 dst_yy = pos->y - kk * pos->step_yoffset;
5371 src_yy = ABS(dst_yy);
5375 height = g->height - src_yy;
5378 src_x = g_src_x + src_xx;
5379 src_y = g_src_y + src_yy;
5381 dst_x = door_rect->x + dst_xx;
5382 dst_y = door_rect->y + dst_yy;
5384 is_panel_and_door_has_closed =
5387 panel_has_doors[door_index] &&
5388 k >= num_move_steps_doors_only - 1);
5390 if (width >= 0 && width <= g->width &&
5391 height >= 0 && height <= g->height &&
5392 !is_panel_and_door_has_closed)
5394 if (is_panel || !pos->draw_masked)
5395 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5398 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5402 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5404 if ((part_opening && (width < 0 || height < 0)) ||
5405 (part_closing && (width >= g->width && height >= g->height)))
5406 door_part_done[nr] = TRUE;
5408 // continue door part animations, but not panel after door has closed
5409 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5410 door_part_done_all = FALSE;
5413 if (!(door_state & DOOR_NO_DELAY))
5417 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5419 current_move_delay += max_step_delay;
5421 // prevent OS (Windows) from complaining about program not responding
5425 if (door_part_done_all)
5429 if (!(door_state & DOOR_NO_DELAY))
5431 // wait for specified door action post delay
5432 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5433 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5434 else if (door_state & DOOR_ACTION_1)
5435 door_delay_value = door_1.post_delay;
5436 else if (door_state & DOOR_ACTION_2)
5437 door_delay_value = door_2.post_delay;
5439 while (!DelayReached(&door_delay, door_delay_value))
5444 if (door_state & DOOR_ACTION_1)
5445 door1 = door_state & DOOR_ACTION_1;
5446 if (door_state & DOOR_ACTION_2)
5447 door2 = door_state & DOOR_ACTION_2;
5449 // draw masked border over door area
5450 DrawMaskedBorder(REDRAW_DOOR_1);
5451 DrawMaskedBorder(REDRAW_DOOR_2);
5453 ClearAutoRepeatKeyEvents();
5455 return (door1 | door2);
5458 static boolean useSpecialEditorDoor(void)
5460 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5461 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5463 // do not draw special editor door if editor border defined or redefined
5464 if (graphic_info[graphic].bitmap != NULL || redefined)
5467 // do not draw special editor door if global border defined to be empty
5468 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5471 // do not draw special editor door if viewport definitions do not match
5475 EY + EYSIZE != VY + VYSIZE)
5481 void DrawSpecialEditorDoor(void)
5483 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5484 int top_border_width = gfx1->width;
5485 int top_border_height = gfx1->height;
5486 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5487 int ex = EX - outer_border;
5488 int ey = EY - outer_border;
5489 int vy = VY - outer_border;
5490 int exsize = EXSIZE + 2 * outer_border;
5492 if (!useSpecialEditorDoor())
5495 // draw bigger level editor toolbox window
5496 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5497 top_border_width, top_border_height, ex, ey - top_border_height);
5498 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5499 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5501 redraw_mask |= REDRAW_ALL;
5504 void UndrawSpecialEditorDoor(void)
5506 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5507 int top_border_width = gfx1->width;
5508 int top_border_height = gfx1->height;
5509 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5510 int ex = EX - outer_border;
5511 int ey = EY - outer_border;
5512 int ey_top = ey - top_border_height;
5513 int exsize = EXSIZE + 2 * outer_border;
5514 int eysize = EYSIZE + 2 * outer_border;
5516 if (!useSpecialEditorDoor())
5519 // draw normal tape recorder window
5520 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5522 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5523 ex, ey_top, top_border_width, top_border_height,
5525 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5526 ex, ey, exsize, eysize, ex, ey);
5530 // if screen background is set to "[NONE]", clear editor toolbox window
5531 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5532 ClearRectangle(drawto, ex, ey, exsize, eysize);
5535 redraw_mask |= REDRAW_ALL;
5539 // ---------- new tool button stuff -------------------------------------------
5544 struct TextPosInfo *pos;
5547 } toolbutton_info[NUM_TOOL_BUTTONS] =
5550 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5551 TOOL_CTRL_ID_YES, "yes"
5554 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5555 TOOL_CTRL_ID_NO, "no"
5558 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5559 TOOL_CTRL_ID_CONFIRM, "confirm"
5562 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5563 TOOL_CTRL_ID_PLAYER_1, "player 1"
5566 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5567 TOOL_CTRL_ID_PLAYER_2, "player 2"
5570 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5571 TOOL_CTRL_ID_PLAYER_3, "player 3"
5574 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5575 TOOL_CTRL_ID_PLAYER_4, "player 4"
5579 void CreateToolButtons(void)
5583 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5585 int graphic = toolbutton_info[i].graphic;
5586 struct GraphicInfo *gfx = &graphic_info[graphic];
5587 struct TextPosInfo *pos = toolbutton_info[i].pos;
5588 struct GadgetInfo *gi;
5589 Bitmap *deco_bitmap = None;
5590 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5591 unsigned int event_mask = GD_EVENT_RELEASED;
5594 int gd_x = gfx->src_x;
5595 int gd_y = gfx->src_y;
5596 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5597 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5602 if (global.use_envelope_request)
5604 setRequestPosition(&dx, &dy, TRUE);
5606 // check if request buttons are outside of envelope and fix, if needed
5607 if (x < 0 || x + gfx->width > request.width ||
5608 y < 0 || y + gfx->height > request.height)
5610 if (id == TOOL_CTRL_ID_YES)
5613 y = request.height - 2 * request.border_size - gfx->height;
5615 else if (id == TOOL_CTRL_ID_NO)
5617 x = request.width - 2 * request.border_size - gfx->width;
5618 y = request.height - 2 * request.border_size - gfx->height;
5620 else if (id == TOOL_CTRL_ID_CONFIRM)
5622 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5623 y = request.height - 2 * request.border_size - gfx->height;
5625 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5627 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5629 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5630 y = request.height - 2 * request.border_size - gfx->height * 2;
5632 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5633 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5638 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5640 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5642 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5643 pos->size, &deco_bitmap, &deco_x, &deco_y);
5644 deco_xpos = (gfx->width - pos->size) / 2;
5645 deco_ypos = (gfx->height - pos->size) / 2;
5648 gi = CreateGadget(GDI_CUSTOM_ID, id,
5649 GDI_IMAGE_ID, graphic,
5650 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5653 GDI_WIDTH, gfx->width,
5654 GDI_HEIGHT, gfx->height,
5655 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5656 GDI_STATE, GD_BUTTON_UNPRESSED,
5657 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5658 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5659 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5660 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5661 GDI_DECORATION_SIZE, pos->size, pos->size,
5662 GDI_DECORATION_SHIFTING, 1, 1,
5663 GDI_DIRECT_DRAW, FALSE,
5664 GDI_EVENT_MASK, event_mask,
5665 GDI_CALLBACK_ACTION, HandleToolButtons,
5669 Error(ERR_EXIT, "cannot create gadget");
5671 tool_gadget[id] = gi;
5675 void FreeToolButtons(void)
5679 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5680 FreeGadget(tool_gadget[i]);
5683 static void UnmapToolButtons(void)
5687 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5688 UnmapGadget(tool_gadget[i]);
5691 static void HandleToolButtons(struct GadgetInfo *gi)
5693 request_gadget_id = gi->custom_id;
5696 static struct Mapping_EM_to_RND_object
5699 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5700 boolean is_backside; // backside of moving element
5706 em_object_mapping_list[] =
5709 Xblank, TRUE, FALSE,
5713 Yacid_splash_eB, FALSE, FALSE,
5714 EL_ACID_SPLASH_RIGHT, -1, -1
5717 Yacid_splash_wB, FALSE, FALSE,
5718 EL_ACID_SPLASH_LEFT, -1, -1
5721 #ifdef EM_ENGINE_BAD_ROLL
5723 Xstone_force_e, FALSE, FALSE,
5724 EL_ROCK, -1, MV_BIT_RIGHT
5727 Xstone_force_w, FALSE, FALSE,
5728 EL_ROCK, -1, MV_BIT_LEFT
5731 Xnut_force_e, FALSE, FALSE,
5732 EL_NUT, -1, MV_BIT_RIGHT
5735 Xnut_force_w, FALSE, FALSE,
5736 EL_NUT, -1, MV_BIT_LEFT
5739 Xspring_force_e, FALSE, FALSE,
5740 EL_SPRING, -1, MV_BIT_RIGHT
5743 Xspring_force_w, FALSE, FALSE,
5744 EL_SPRING, -1, MV_BIT_LEFT
5747 Xemerald_force_e, FALSE, FALSE,
5748 EL_EMERALD, -1, MV_BIT_RIGHT
5751 Xemerald_force_w, FALSE, FALSE,
5752 EL_EMERALD, -1, MV_BIT_LEFT
5755 Xdiamond_force_e, FALSE, FALSE,
5756 EL_DIAMOND, -1, MV_BIT_RIGHT
5759 Xdiamond_force_w, FALSE, FALSE,
5760 EL_DIAMOND, -1, MV_BIT_LEFT
5763 Xbomb_force_e, FALSE, FALSE,
5764 EL_BOMB, -1, MV_BIT_RIGHT
5767 Xbomb_force_w, FALSE, FALSE,
5768 EL_BOMB, -1, MV_BIT_LEFT
5770 #endif // EM_ENGINE_BAD_ROLL
5773 Xstone, TRUE, FALSE,
5777 Xstone_pause, FALSE, FALSE,
5781 Xstone_fall, FALSE, FALSE,
5785 Ystone_s, FALSE, FALSE,
5786 EL_ROCK, ACTION_FALLING, -1
5789 Ystone_sB, FALSE, TRUE,
5790 EL_ROCK, ACTION_FALLING, -1
5793 Ystone_e, FALSE, FALSE,
5794 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5797 Ystone_eB, FALSE, TRUE,
5798 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5801 Ystone_w, FALSE, FALSE,
5802 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5805 Ystone_wB, FALSE, TRUE,
5806 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5813 Xnut_pause, FALSE, FALSE,
5817 Xnut_fall, FALSE, FALSE,
5821 Ynut_s, FALSE, FALSE,
5822 EL_NUT, ACTION_FALLING, -1
5825 Ynut_sB, FALSE, TRUE,
5826 EL_NUT, ACTION_FALLING, -1
5829 Ynut_e, FALSE, FALSE,
5830 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5833 Ynut_eB, FALSE, TRUE,
5834 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5837 Ynut_w, FALSE, FALSE,
5838 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5841 Ynut_wB, FALSE, TRUE,
5842 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5845 Xbug_n, TRUE, FALSE,
5849 Xbug_e, TRUE, FALSE,
5850 EL_BUG_RIGHT, -1, -1
5853 Xbug_s, TRUE, FALSE,
5857 Xbug_w, TRUE, FALSE,
5861 Xbug_gon, FALSE, FALSE,
5865 Xbug_goe, FALSE, FALSE,
5866 EL_BUG_RIGHT, -1, -1
5869 Xbug_gos, FALSE, FALSE,
5873 Xbug_gow, FALSE, FALSE,
5877 Ybug_n, FALSE, FALSE,
5878 EL_BUG, ACTION_MOVING, MV_BIT_UP
5881 Ybug_nB, FALSE, TRUE,
5882 EL_BUG, ACTION_MOVING, MV_BIT_UP
5885 Ybug_e, FALSE, FALSE,
5886 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5889 Ybug_eB, FALSE, TRUE,
5890 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5893 Ybug_s, FALSE, FALSE,
5894 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5897 Ybug_sB, FALSE, TRUE,
5898 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5901 Ybug_w, FALSE, FALSE,
5902 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5905 Ybug_wB, FALSE, TRUE,
5906 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5909 Ybug_w_n, FALSE, FALSE,
5910 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5913 Ybug_n_e, FALSE, FALSE,
5914 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5917 Ybug_e_s, FALSE, FALSE,
5918 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5921 Ybug_s_w, FALSE, FALSE,
5922 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5925 Ybug_e_n, FALSE, FALSE,
5926 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5929 Ybug_s_e, FALSE, FALSE,
5930 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5933 Ybug_w_s, FALSE, FALSE,
5934 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5937 Ybug_n_w, FALSE, FALSE,
5938 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5941 Ybug_stone, FALSE, FALSE,
5942 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5945 Ybug_spring, FALSE, FALSE,
5946 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5949 Xtank_n, TRUE, FALSE,
5950 EL_SPACESHIP_UP, -1, -1
5953 Xtank_e, TRUE, FALSE,
5954 EL_SPACESHIP_RIGHT, -1, -1
5957 Xtank_s, TRUE, FALSE,
5958 EL_SPACESHIP_DOWN, -1, -1
5961 Xtank_w, TRUE, FALSE,
5962 EL_SPACESHIP_LEFT, -1, -1
5965 Xtank_gon, FALSE, FALSE,
5966 EL_SPACESHIP_UP, -1, -1
5969 Xtank_goe, FALSE, FALSE,
5970 EL_SPACESHIP_RIGHT, -1, -1
5973 Xtank_gos, FALSE, FALSE,
5974 EL_SPACESHIP_DOWN, -1, -1
5977 Xtank_gow, FALSE, FALSE,
5978 EL_SPACESHIP_LEFT, -1, -1
5981 Ytank_n, FALSE, FALSE,
5982 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5985 Ytank_nB, FALSE, TRUE,
5986 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5989 Ytank_e, FALSE, FALSE,
5990 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5993 Ytank_eB, FALSE, TRUE,
5994 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5997 Ytank_s, FALSE, FALSE,
5998 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6001 Ytank_sB, FALSE, TRUE,
6002 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6005 Ytank_w, FALSE, FALSE,
6006 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6009 Ytank_wB, FALSE, TRUE,
6010 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6013 Ytank_w_n, FALSE, FALSE,
6014 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6017 Ytank_n_e, FALSE, FALSE,
6018 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6021 Ytank_e_s, FALSE, FALSE,
6022 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6025 Ytank_s_w, FALSE, FALSE,
6026 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6029 Ytank_e_n, FALSE, FALSE,
6030 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6033 Ytank_s_e, FALSE, FALSE,
6034 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6037 Ytank_w_s, FALSE, FALSE,
6038 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6041 Ytank_n_w, FALSE, FALSE,
6042 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6045 Ytank_stone, FALSE, FALSE,
6046 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6049 Ytank_spring, FALSE, FALSE,
6050 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6053 Xandroid, TRUE, FALSE,
6054 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6057 Xandroid_1_n, FALSE, FALSE,
6058 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6061 Xandroid_2_n, FALSE, FALSE,
6062 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6065 Xandroid_1_e, FALSE, FALSE,
6066 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6069 Xandroid_2_e, FALSE, FALSE,
6070 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6073 Xandroid_1_w, FALSE, FALSE,
6074 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6077 Xandroid_2_w, FALSE, FALSE,
6078 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6081 Xandroid_1_s, FALSE, FALSE,
6082 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6085 Xandroid_2_s, FALSE, FALSE,
6086 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6089 Yandroid_n, FALSE, FALSE,
6090 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6093 Yandroid_nB, FALSE, TRUE,
6094 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6097 Yandroid_ne, FALSE, FALSE,
6098 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6101 Yandroid_neB, FALSE, TRUE,
6102 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6105 Yandroid_e, FALSE, FALSE,
6106 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6109 Yandroid_eB, FALSE, TRUE,
6110 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6113 Yandroid_se, FALSE, FALSE,
6114 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6117 Yandroid_seB, FALSE, TRUE,
6118 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6121 Yandroid_s, FALSE, FALSE,
6122 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6125 Yandroid_sB, FALSE, TRUE,
6126 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6129 Yandroid_sw, FALSE, FALSE,
6130 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6133 Yandroid_swB, FALSE, TRUE,
6134 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6137 Yandroid_w, FALSE, FALSE,
6138 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6141 Yandroid_wB, FALSE, TRUE,
6142 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6145 Yandroid_nw, FALSE, FALSE,
6146 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6149 Yandroid_nwB, FALSE, TRUE,
6150 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6153 Xspring, TRUE, FALSE,
6157 Xspring_pause, FALSE, FALSE,
6161 Xspring_e, FALSE, FALSE,
6165 Xspring_w, FALSE, FALSE,
6169 Xspring_fall, FALSE, FALSE,
6173 Yspring_s, FALSE, FALSE,
6174 EL_SPRING, ACTION_FALLING, -1
6177 Yspring_sB, FALSE, TRUE,
6178 EL_SPRING, ACTION_FALLING, -1
6181 Yspring_e, FALSE, FALSE,
6182 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6185 Yspring_eB, FALSE, TRUE,
6186 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6189 Yspring_w, FALSE, FALSE,
6190 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6193 Yspring_wB, FALSE, TRUE,
6194 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6197 Yspring_kill_e, FALSE, FALSE,
6198 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6201 Yspring_kill_eB, FALSE, TRUE,
6202 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6205 Yspring_kill_w, FALSE, FALSE,
6206 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6209 Yspring_kill_wB, FALSE, TRUE,
6210 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6213 Xeater_n, TRUE, FALSE,
6214 EL_YAMYAM_UP, -1, -1
6217 Xeater_e, TRUE, FALSE,
6218 EL_YAMYAM_RIGHT, -1, -1
6221 Xeater_w, TRUE, FALSE,
6222 EL_YAMYAM_LEFT, -1, -1
6225 Xeater_s, TRUE, FALSE,
6226 EL_YAMYAM_DOWN, -1, -1
6229 Yeater_n, FALSE, FALSE,
6230 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6233 Yeater_nB, FALSE, TRUE,
6234 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6237 Yeater_e, FALSE, FALSE,
6238 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6241 Yeater_eB, FALSE, TRUE,
6242 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6245 Yeater_s, FALSE, FALSE,
6246 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6249 Yeater_sB, FALSE, TRUE,
6250 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6253 Yeater_w, FALSE, FALSE,
6254 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6257 Yeater_wB, FALSE, TRUE,
6258 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6261 Yeater_stone, FALSE, FALSE,
6262 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6265 Yeater_spring, FALSE, FALSE,
6266 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6269 Xalien, TRUE, FALSE,
6273 Xalien_pause, FALSE, FALSE,
6277 Yalien_n, FALSE, FALSE,
6278 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6281 Yalien_nB, FALSE, TRUE,
6282 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6285 Yalien_e, FALSE, FALSE,
6286 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6289 Yalien_eB, FALSE, TRUE,
6290 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6293 Yalien_s, FALSE, FALSE,
6294 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6297 Yalien_sB, FALSE, TRUE,
6298 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6301 Yalien_w, FALSE, FALSE,
6302 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6305 Yalien_wB, FALSE, TRUE,
6306 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6309 Yalien_stone, FALSE, FALSE,
6310 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6313 Yalien_spring, FALSE, FALSE,
6314 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6317 Xemerald, TRUE, FALSE,
6321 Xemerald_pause, FALSE, FALSE,
6325 Xemerald_fall, FALSE, FALSE,
6329 Xemerald_shine, FALSE, FALSE,
6330 EL_EMERALD, ACTION_TWINKLING, -1
6333 Yemerald_s, FALSE, FALSE,
6334 EL_EMERALD, ACTION_FALLING, -1
6337 Yemerald_sB, FALSE, TRUE,
6338 EL_EMERALD, ACTION_FALLING, -1
6341 Yemerald_e, FALSE, FALSE,
6342 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6345 Yemerald_eB, FALSE, TRUE,
6346 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6349 Yemerald_w, FALSE, FALSE,
6350 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6353 Yemerald_wB, FALSE, TRUE,
6354 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6357 Yemerald_eat, FALSE, FALSE,
6358 EL_EMERALD, ACTION_COLLECTING, -1
6361 Yemerald_stone, FALSE, FALSE,
6362 EL_NUT, ACTION_BREAKING, -1
6365 Xdiamond, TRUE, FALSE,
6369 Xdiamond_pause, FALSE, FALSE,
6373 Xdiamond_fall, FALSE, FALSE,
6377 Xdiamond_shine, FALSE, FALSE,
6378 EL_DIAMOND, ACTION_TWINKLING, -1
6381 Ydiamond_s, FALSE, FALSE,
6382 EL_DIAMOND, ACTION_FALLING, -1
6385 Ydiamond_sB, FALSE, TRUE,
6386 EL_DIAMOND, ACTION_FALLING, -1
6389 Ydiamond_e, FALSE, FALSE,
6390 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6393 Ydiamond_eB, FALSE, TRUE,
6394 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6397 Ydiamond_w, FALSE, FALSE,
6398 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6401 Ydiamond_wB, FALSE, TRUE,
6402 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6405 Ydiamond_eat, FALSE, FALSE,
6406 EL_DIAMOND, ACTION_COLLECTING, -1
6409 Ydiamond_stone, FALSE, FALSE,
6410 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6413 Xdrip_fall, TRUE, FALSE,
6414 EL_AMOEBA_DROP, -1, -1
6417 Xdrip_stretch, FALSE, FALSE,
6418 EL_AMOEBA_DROP, ACTION_FALLING, -1
6421 Xdrip_stretchB, FALSE, TRUE,
6422 EL_AMOEBA_DROP, ACTION_FALLING, -1
6425 Xdrip_eat, FALSE, FALSE,
6426 EL_AMOEBA_DROP, ACTION_GROWING, -1
6429 Ydrip_s1, FALSE, FALSE,
6430 EL_AMOEBA_DROP, ACTION_FALLING, -1
6433 Ydrip_s1B, FALSE, TRUE,
6434 EL_AMOEBA_DROP, ACTION_FALLING, -1
6437 Ydrip_s2, FALSE, FALSE,
6438 EL_AMOEBA_DROP, ACTION_FALLING, -1
6441 Ydrip_s2B, FALSE, TRUE,
6442 EL_AMOEBA_DROP, ACTION_FALLING, -1
6449 Xbomb_pause, FALSE, FALSE,
6453 Xbomb_fall, FALSE, FALSE,
6457 Ybomb_s, FALSE, FALSE,
6458 EL_BOMB, ACTION_FALLING, -1
6461 Ybomb_sB, FALSE, TRUE,
6462 EL_BOMB, ACTION_FALLING, -1
6465 Ybomb_e, FALSE, FALSE,
6466 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6469 Ybomb_eB, FALSE, TRUE,
6470 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6473 Ybomb_w, FALSE, FALSE,
6474 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6477 Ybomb_wB, FALSE, TRUE,
6478 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6481 Ybomb_eat, FALSE, FALSE,
6482 EL_BOMB, ACTION_ACTIVATING, -1
6485 Xballoon, TRUE, FALSE,
6489 Yballoon_n, FALSE, FALSE,
6490 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6493 Yballoon_nB, FALSE, TRUE,
6494 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6497 Yballoon_e, FALSE, FALSE,
6498 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6501 Yballoon_eB, FALSE, TRUE,
6502 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6505 Yballoon_s, FALSE, FALSE,
6506 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6509 Yballoon_sB, FALSE, TRUE,
6510 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6513 Yballoon_w, FALSE, FALSE,
6514 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6517 Yballoon_wB, FALSE, TRUE,
6518 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6521 Xgrass, TRUE, FALSE,
6522 EL_EMC_GRASS, -1, -1
6525 Ygrass_nB, FALSE, FALSE,
6526 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6529 Ygrass_eB, FALSE, FALSE,
6530 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6533 Ygrass_sB, FALSE, FALSE,
6534 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6537 Ygrass_wB, FALSE, FALSE,
6538 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6545 Ydirt_nB, FALSE, FALSE,
6546 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6549 Ydirt_eB, FALSE, FALSE,
6550 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6553 Ydirt_sB, FALSE, FALSE,
6554 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6557 Ydirt_wB, FALSE, FALSE,
6558 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6561 Xacid_ne, TRUE, FALSE,
6562 EL_ACID_POOL_TOPRIGHT, -1, -1
6565 Xacid_se, TRUE, FALSE,
6566 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6569 Xacid_s, TRUE, FALSE,
6570 EL_ACID_POOL_BOTTOM, -1, -1
6573 Xacid_sw, TRUE, FALSE,
6574 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6577 Xacid_nw, TRUE, FALSE,
6578 EL_ACID_POOL_TOPLEFT, -1, -1
6581 Xacid_1, TRUE, FALSE,
6585 Xacid_2, FALSE, FALSE,
6589 Xacid_3, FALSE, FALSE,
6593 Xacid_4, FALSE, FALSE,
6597 Xacid_5, FALSE, FALSE,
6601 Xacid_6, FALSE, FALSE,
6605 Xacid_7, FALSE, FALSE,
6609 Xacid_8, FALSE, FALSE,
6613 Xball_1, TRUE, FALSE,
6614 EL_EMC_MAGIC_BALL, -1, -1
6617 Xball_1B, FALSE, FALSE,
6618 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6621 Xball_2, FALSE, FALSE,
6622 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6625 Xball_2B, FALSE, FALSE,
6626 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6629 Yball_eat, FALSE, FALSE,
6630 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6633 Ykey_1_eat, FALSE, FALSE,
6634 EL_EM_KEY_1, ACTION_COLLECTING, -1
6637 Ykey_2_eat, FALSE, FALSE,
6638 EL_EM_KEY_2, ACTION_COLLECTING, -1
6641 Ykey_3_eat, FALSE, FALSE,
6642 EL_EM_KEY_3, ACTION_COLLECTING, -1
6645 Ykey_4_eat, FALSE, FALSE,
6646 EL_EM_KEY_4, ACTION_COLLECTING, -1
6649 Ykey_5_eat, FALSE, FALSE,
6650 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6653 Ykey_6_eat, FALSE, FALSE,
6654 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6657 Ykey_7_eat, FALSE, FALSE,
6658 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6661 Ykey_8_eat, FALSE, FALSE,
6662 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6665 Ylenses_eat, FALSE, FALSE,
6666 EL_EMC_LENSES, ACTION_COLLECTING, -1
6669 Ymagnify_eat, FALSE, FALSE,
6670 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6673 Ygrass_eat, FALSE, FALSE,
6674 EL_EMC_GRASS, ACTION_SNAPPING, -1
6677 Ydirt_eat, FALSE, FALSE,
6678 EL_SAND, ACTION_SNAPPING, -1
6681 Xgrow_ns, TRUE, FALSE,
6682 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6685 Ygrow_ns_eat, FALSE, FALSE,
6686 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6689 Xgrow_ew, TRUE, FALSE,
6690 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6693 Ygrow_ew_eat, FALSE, FALSE,
6694 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6697 Xwonderwall, TRUE, FALSE,
6698 EL_MAGIC_WALL, -1, -1
6701 XwonderwallB, FALSE, FALSE,
6702 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6705 Xamoeba_1, TRUE, FALSE,
6706 EL_AMOEBA_DRY, ACTION_OTHER, -1
6709 Xamoeba_2, FALSE, FALSE,
6710 EL_AMOEBA_DRY, ACTION_OTHER, -1
6713 Xamoeba_3, FALSE, FALSE,
6714 EL_AMOEBA_DRY, ACTION_OTHER, -1
6717 Xamoeba_4, FALSE, FALSE,
6718 EL_AMOEBA_DRY, ACTION_OTHER, -1
6721 Xamoeba_5, TRUE, FALSE,
6722 EL_AMOEBA_WET, ACTION_OTHER, -1
6725 Xamoeba_6, FALSE, FALSE,
6726 EL_AMOEBA_WET, ACTION_OTHER, -1
6729 Xamoeba_7, FALSE, FALSE,
6730 EL_AMOEBA_WET, ACTION_OTHER, -1
6733 Xamoeba_8, FALSE, FALSE,
6734 EL_AMOEBA_WET, ACTION_OTHER, -1
6737 Xdoor_1, TRUE, FALSE,
6738 EL_EM_GATE_1, -1, -1
6741 Xdoor_2, TRUE, FALSE,
6742 EL_EM_GATE_2, -1, -1
6745 Xdoor_3, TRUE, FALSE,
6746 EL_EM_GATE_3, -1, -1
6749 Xdoor_4, TRUE, FALSE,
6750 EL_EM_GATE_4, -1, -1
6753 Xdoor_5, TRUE, FALSE,
6754 EL_EMC_GATE_5, -1, -1
6757 Xdoor_6, TRUE, FALSE,
6758 EL_EMC_GATE_6, -1, -1
6761 Xdoor_7, TRUE, FALSE,
6762 EL_EMC_GATE_7, -1, -1
6765 Xdoor_8, TRUE, FALSE,
6766 EL_EMC_GATE_8, -1, -1
6769 Xkey_1, TRUE, FALSE,
6773 Xkey_2, TRUE, FALSE,
6777 Xkey_3, TRUE, FALSE,
6781 Xkey_4, TRUE, FALSE,
6785 Xkey_5, TRUE, FALSE,
6786 EL_EMC_KEY_5, -1, -1
6789 Xkey_6, TRUE, FALSE,
6790 EL_EMC_KEY_6, -1, -1
6793 Xkey_7, TRUE, FALSE,
6794 EL_EMC_KEY_7, -1, -1
6797 Xkey_8, TRUE, FALSE,
6798 EL_EMC_KEY_8, -1, -1
6801 Xwind_n, TRUE, FALSE,
6802 EL_BALLOON_SWITCH_UP, -1, -1
6805 Xwind_e, TRUE, FALSE,
6806 EL_BALLOON_SWITCH_RIGHT, -1, -1
6809 Xwind_s, TRUE, FALSE,
6810 EL_BALLOON_SWITCH_DOWN, -1, -1
6813 Xwind_w, TRUE, FALSE,
6814 EL_BALLOON_SWITCH_LEFT, -1, -1
6817 Xwind_nesw, TRUE, FALSE,
6818 EL_BALLOON_SWITCH_ANY, -1, -1
6821 Xwind_stop, TRUE, FALSE,
6822 EL_BALLOON_SWITCH_NONE, -1, -1
6826 EL_EM_EXIT_CLOSED, -1, -1
6829 Xexit_1, TRUE, FALSE,
6830 EL_EM_EXIT_OPEN, -1, -1
6833 Xexit_2, FALSE, FALSE,
6834 EL_EM_EXIT_OPEN, -1, -1
6837 Xexit_3, FALSE, FALSE,
6838 EL_EM_EXIT_OPEN, -1, -1
6841 Xdynamite, TRUE, FALSE,
6842 EL_EM_DYNAMITE, -1, -1
6845 Ydynamite_eat, FALSE, FALSE,
6846 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6849 Xdynamite_1, TRUE, FALSE,
6850 EL_EM_DYNAMITE_ACTIVE, -1, -1
6853 Xdynamite_2, FALSE, FALSE,
6854 EL_EM_DYNAMITE_ACTIVE, -1, -1
6857 Xdynamite_3, FALSE, FALSE,
6858 EL_EM_DYNAMITE_ACTIVE, -1, -1
6861 Xdynamite_4, FALSE, FALSE,
6862 EL_EM_DYNAMITE_ACTIVE, -1, -1
6865 Xbumper, TRUE, FALSE,
6866 EL_EMC_SPRING_BUMPER, -1, -1
6869 XbumperB, FALSE, FALSE,
6870 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6873 Xwheel, TRUE, FALSE,
6874 EL_ROBOT_WHEEL, -1, -1
6877 XwheelB, FALSE, FALSE,
6878 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6881 Xswitch, TRUE, FALSE,
6882 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6885 XswitchB, FALSE, FALSE,
6886 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6890 EL_QUICKSAND_EMPTY, -1, -1
6893 Xsand_stone, TRUE, FALSE,
6894 EL_QUICKSAND_FULL, -1, -1
6897 Xsand_stonein_1, FALSE, TRUE,
6898 EL_ROCK, ACTION_FILLING, -1
6901 Xsand_stonein_2, FALSE, TRUE,
6902 EL_ROCK, ACTION_FILLING, -1
6905 Xsand_stonein_3, FALSE, TRUE,
6906 EL_ROCK, ACTION_FILLING, -1
6909 Xsand_stonein_4, FALSE, TRUE,
6910 EL_ROCK, ACTION_FILLING, -1
6913 Xsand_stonesand_1, FALSE, FALSE,
6914 EL_QUICKSAND_EMPTYING, -1, -1
6917 Xsand_stonesand_2, FALSE, FALSE,
6918 EL_QUICKSAND_EMPTYING, -1, -1
6921 Xsand_stonesand_3, FALSE, FALSE,
6922 EL_QUICKSAND_EMPTYING, -1, -1
6925 Xsand_stonesand_4, FALSE, FALSE,
6926 EL_QUICKSAND_EMPTYING, -1, -1
6929 Xsand_stonesand_quickout_1, FALSE, FALSE,
6930 EL_QUICKSAND_EMPTYING, -1, -1
6933 Xsand_stonesand_quickout_2, FALSE, FALSE,
6934 EL_QUICKSAND_EMPTYING, -1, -1
6937 Xsand_stoneout_1, FALSE, FALSE,
6938 EL_ROCK, ACTION_EMPTYING, -1
6941 Xsand_stoneout_2, FALSE, FALSE,
6942 EL_ROCK, ACTION_EMPTYING, -1
6945 Xsand_sandstone_1, FALSE, FALSE,
6946 EL_QUICKSAND_FILLING, -1, -1
6949 Xsand_sandstone_2, FALSE, FALSE,
6950 EL_QUICKSAND_FILLING, -1, -1
6953 Xsand_sandstone_3, FALSE, FALSE,
6954 EL_QUICKSAND_FILLING, -1, -1
6957 Xsand_sandstone_4, FALSE, FALSE,
6958 EL_QUICKSAND_FILLING, -1, -1
6961 Xplant, TRUE, FALSE,
6962 EL_EMC_PLANT, -1, -1
6965 Yplant, FALSE, FALSE,
6966 EL_EMC_PLANT, -1, -1
6969 Xlenses, TRUE, FALSE,
6970 EL_EMC_LENSES, -1, -1
6973 Xmagnify, TRUE, FALSE,
6974 EL_EMC_MAGNIFIER, -1, -1
6977 Xdripper, TRUE, FALSE,
6978 EL_EMC_DRIPPER, -1, -1
6981 XdripperB, FALSE, FALSE,
6982 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6985 Xfake_blank, TRUE, FALSE,
6986 EL_INVISIBLE_WALL, -1, -1
6989 Xfake_blankB, FALSE, FALSE,
6990 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6993 Xfake_grass, TRUE, FALSE,
6994 EL_EMC_FAKE_GRASS, -1, -1
6997 Xfake_grassB, FALSE, FALSE,
6998 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7001 Xfake_door_1, TRUE, FALSE,
7002 EL_EM_GATE_1_GRAY, -1, -1
7005 Xfake_door_2, TRUE, FALSE,
7006 EL_EM_GATE_2_GRAY, -1, -1
7009 Xfake_door_3, TRUE, FALSE,
7010 EL_EM_GATE_3_GRAY, -1, -1
7013 Xfake_door_4, TRUE, FALSE,
7014 EL_EM_GATE_4_GRAY, -1, -1
7017 Xfake_door_5, TRUE, FALSE,
7018 EL_EMC_GATE_5_GRAY, -1, -1
7021 Xfake_door_6, TRUE, FALSE,
7022 EL_EMC_GATE_6_GRAY, -1, -1
7025 Xfake_door_7, TRUE, FALSE,
7026 EL_EMC_GATE_7_GRAY, -1, -1
7029 Xfake_door_8, TRUE, FALSE,
7030 EL_EMC_GATE_8_GRAY, -1, -1
7033 Xfake_acid_1, TRUE, FALSE,
7034 EL_EMC_FAKE_ACID, -1, -1
7037 Xfake_acid_2, FALSE, FALSE,
7038 EL_EMC_FAKE_ACID, -1, -1
7041 Xfake_acid_3, FALSE, FALSE,
7042 EL_EMC_FAKE_ACID, -1, -1
7045 Xfake_acid_4, FALSE, FALSE,
7046 EL_EMC_FAKE_ACID, -1, -1
7049 Xfake_acid_5, FALSE, FALSE,
7050 EL_EMC_FAKE_ACID, -1, -1
7053 Xfake_acid_6, FALSE, FALSE,
7054 EL_EMC_FAKE_ACID, -1, -1
7057 Xfake_acid_7, FALSE, FALSE,
7058 EL_EMC_FAKE_ACID, -1, -1
7061 Xfake_acid_8, FALSE, FALSE,
7062 EL_EMC_FAKE_ACID, -1, -1
7065 Xsteel_1, TRUE, FALSE,
7066 EL_STEELWALL, -1, -1
7069 Xsteel_2, TRUE, FALSE,
7070 EL_EMC_STEELWALL_2, -1, -1
7073 Xsteel_3, TRUE, FALSE,
7074 EL_EMC_STEELWALL_3, -1, -1
7077 Xsteel_4, TRUE, FALSE,
7078 EL_EMC_STEELWALL_4, -1, -1
7081 Xwall_1, TRUE, FALSE,
7085 Xwall_2, TRUE, FALSE,
7086 EL_EMC_WALL_14, -1, -1
7089 Xwall_3, TRUE, FALSE,
7090 EL_EMC_WALL_15, -1, -1
7093 Xwall_4, TRUE, FALSE,
7094 EL_EMC_WALL_16, -1, -1
7097 Xround_wall_1, TRUE, FALSE,
7098 EL_WALL_SLIPPERY, -1, -1
7101 Xround_wall_2, TRUE, FALSE,
7102 EL_EMC_WALL_SLIPPERY_2, -1, -1
7105 Xround_wall_3, TRUE, FALSE,
7106 EL_EMC_WALL_SLIPPERY_3, -1, -1
7109 Xround_wall_4, TRUE, FALSE,
7110 EL_EMC_WALL_SLIPPERY_4, -1, -1
7113 Xdecor_1, TRUE, FALSE,
7114 EL_EMC_WALL_8, -1, -1
7117 Xdecor_2, TRUE, FALSE,
7118 EL_EMC_WALL_6, -1, -1
7121 Xdecor_3, TRUE, FALSE,
7122 EL_EMC_WALL_4, -1, -1
7125 Xdecor_4, TRUE, FALSE,
7126 EL_EMC_WALL_7, -1, -1
7129 Xdecor_5, TRUE, FALSE,
7130 EL_EMC_WALL_5, -1, -1
7133 Xdecor_6, TRUE, FALSE,
7134 EL_EMC_WALL_9, -1, -1
7137 Xdecor_7, TRUE, FALSE,
7138 EL_EMC_WALL_10, -1, -1
7141 Xdecor_8, TRUE, FALSE,
7142 EL_EMC_WALL_1, -1, -1
7145 Xdecor_9, TRUE, FALSE,
7146 EL_EMC_WALL_2, -1, -1
7149 Xdecor_10, TRUE, FALSE,
7150 EL_EMC_WALL_3, -1, -1
7153 Xdecor_11, TRUE, FALSE,
7154 EL_EMC_WALL_11, -1, -1
7157 Xdecor_12, TRUE, FALSE,
7158 EL_EMC_WALL_12, -1, -1
7161 Xalpha_0, TRUE, FALSE,
7162 EL_CHAR('0'), -1, -1
7165 Xalpha_1, TRUE, FALSE,
7166 EL_CHAR('1'), -1, -1
7169 Xalpha_2, TRUE, FALSE,
7170 EL_CHAR('2'), -1, -1
7173 Xalpha_3, TRUE, FALSE,
7174 EL_CHAR('3'), -1, -1
7177 Xalpha_4, TRUE, FALSE,
7178 EL_CHAR('4'), -1, -1
7181 Xalpha_5, TRUE, FALSE,
7182 EL_CHAR('5'), -1, -1
7185 Xalpha_6, TRUE, FALSE,
7186 EL_CHAR('6'), -1, -1
7189 Xalpha_7, TRUE, FALSE,
7190 EL_CHAR('7'), -1, -1
7193 Xalpha_8, TRUE, FALSE,
7194 EL_CHAR('8'), -1, -1
7197 Xalpha_9, TRUE, FALSE,
7198 EL_CHAR('9'), -1, -1
7201 Xalpha_excla, TRUE, FALSE,
7202 EL_CHAR('!'), -1, -1
7205 Xalpha_quote, TRUE, FALSE,
7206 EL_CHAR('"'), -1, -1
7209 Xalpha_comma, TRUE, FALSE,
7210 EL_CHAR(','), -1, -1
7213 Xalpha_minus, TRUE, FALSE,
7214 EL_CHAR('-'), -1, -1
7217 Xalpha_perio, TRUE, FALSE,
7218 EL_CHAR('.'), -1, -1
7221 Xalpha_colon, TRUE, FALSE,
7222 EL_CHAR(':'), -1, -1
7225 Xalpha_quest, TRUE, FALSE,
7226 EL_CHAR('?'), -1, -1
7229 Xalpha_a, TRUE, FALSE,
7230 EL_CHAR('A'), -1, -1
7233 Xalpha_b, TRUE, FALSE,
7234 EL_CHAR('B'), -1, -1
7237 Xalpha_c, TRUE, FALSE,
7238 EL_CHAR('C'), -1, -1
7241 Xalpha_d, TRUE, FALSE,
7242 EL_CHAR('D'), -1, -1
7245 Xalpha_e, TRUE, FALSE,
7246 EL_CHAR('E'), -1, -1
7249 Xalpha_f, TRUE, FALSE,
7250 EL_CHAR('F'), -1, -1
7253 Xalpha_g, TRUE, FALSE,
7254 EL_CHAR('G'), -1, -1
7257 Xalpha_h, TRUE, FALSE,
7258 EL_CHAR('H'), -1, -1
7261 Xalpha_i, TRUE, FALSE,
7262 EL_CHAR('I'), -1, -1
7265 Xalpha_j, TRUE, FALSE,
7266 EL_CHAR('J'), -1, -1
7269 Xalpha_k, TRUE, FALSE,
7270 EL_CHAR('K'), -1, -1
7273 Xalpha_l, TRUE, FALSE,
7274 EL_CHAR('L'), -1, -1
7277 Xalpha_m, TRUE, FALSE,
7278 EL_CHAR('M'), -1, -1
7281 Xalpha_n, TRUE, FALSE,
7282 EL_CHAR('N'), -1, -1
7285 Xalpha_o, TRUE, FALSE,
7286 EL_CHAR('O'), -1, -1
7289 Xalpha_p, TRUE, FALSE,
7290 EL_CHAR('P'), -1, -1
7293 Xalpha_q, TRUE, FALSE,
7294 EL_CHAR('Q'), -1, -1
7297 Xalpha_r, TRUE, FALSE,
7298 EL_CHAR('R'), -1, -1
7301 Xalpha_s, TRUE, FALSE,
7302 EL_CHAR('S'), -1, -1
7305 Xalpha_t, TRUE, FALSE,
7306 EL_CHAR('T'), -1, -1
7309 Xalpha_u, TRUE, FALSE,
7310 EL_CHAR('U'), -1, -1
7313 Xalpha_v, TRUE, FALSE,
7314 EL_CHAR('V'), -1, -1
7317 Xalpha_w, TRUE, FALSE,
7318 EL_CHAR('W'), -1, -1
7321 Xalpha_x, TRUE, FALSE,
7322 EL_CHAR('X'), -1, -1
7325 Xalpha_y, TRUE, FALSE,
7326 EL_CHAR('Y'), -1, -1
7329 Xalpha_z, TRUE, FALSE,
7330 EL_CHAR('Z'), -1, -1
7333 Xalpha_arrow_e, TRUE, FALSE,
7334 EL_CHAR('>'), -1, -1
7337 Xalpha_arrow_w, TRUE, FALSE,
7338 EL_CHAR('<'), -1, -1
7341 Xalpha_copyr, TRUE, FALSE,
7342 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7346 Xboom_bug, FALSE, FALSE,
7347 EL_BUG, ACTION_EXPLODING, -1
7350 Xboom_bomb, FALSE, FALSE,
7351 EL_BOMB, ACTION_EXPLODING, -1
7354 Xboom_android, FALSE, FALSE,
7355 EL_EMC_ANDROID, ACTION_OTHER, -1
7358 Xboom_1, FALSE, FALSE,
7359 EL_DEFAULT, ACTION_EXPLODING, -1
7362 Xboom_2, FALSE, FALSE,
7363 EL_DEFAULT, ACTION_EXPLODING, -1
7366 Znormal, FALSE, FALSE,
7370 Zdynamite, FALSE, FALSE,
7374 Zplayer, FALSE, FALSE,
7378 ZBORDER, FALSE, FALSE,
7388 static struct Mapping_EM_to_RND_player
7397 em_player_mapping_list[] =
7401 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7405 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7409 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7413 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7417 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7421 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7425 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7429 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7433 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7437 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7441 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7445 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7449 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7453 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7457 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7461 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7465 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7469 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7473 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7477 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7481 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7485 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7489 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7493 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7497 EL_PLAYER_1, ACTION_DEFAULT, -1,
7501 EL_PLAYER_2, ACTION_DEFAULT, -1,
7505 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7509 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7513 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7517 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7521 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7525 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7529 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7533 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7537 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7541 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7545 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7549 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7553 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7557 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7561 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7565 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7569 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7573 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7577 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7581 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7585 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7589 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7593 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7597 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7601 EL_PLAYER_3, ACTION_DEFAULT, -1,
7605 EL_PLAYER_4, ACTION_DEFAULT, -1,
7614 int map_element_RND_to_EM(int element_rnd)
7616 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7617 static boolean mapping_initialized = FALSE;
7619 if (!mapping_initialized)
7623 // return "Xalpha_quest" for all undefined elements in mapping array
7624 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7625 mapping_RND_to_EM[i] = Xalpha_quest;
7627 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7628 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7629 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7630 em_object_mapping_list[i].element_em;
7632 mapping_initialized = TRUE;
7635 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7636 return mapping_RND_to_EM[element_rnd];
7638 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7643 int map_element_EM_to_RND(int element_em)
7645 static unsigned short mapping_EM_to_RND[TILE_MAX];
7646 static boolean mapping_initialized = FALSE;
7648 if (!mapping_initialized)
7652 // return "EL_UNKNOWN" for all undefined elements in mapping array
7653 for (i = 0; i < TILE_MAX; i++)
7654 mapping_EM_to_RND[i] = EL_UNKNOWN;
7656 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7657 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7658 em_object_mapping_list[i].element_rnd;
7660 mapping_initialized = TRUE;
7663 if (element_em >= 0 && element_em < TILE_MAX)
7664 return mapping_EM_to_RND[element_em];
7666 Error(ERR_WARN, "invalid EM level element %d", element_em);
7671 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7673 struct LevelInfo_EM *level_em = level->native_em_level;
7674 struct LEVEL *lev = level_em->lev;
7677 for (i = 0; i < TILE_MAX; i++)
7678 lev->android_array[i] = Xblank;
7680 for (i = 0; i < level->num_android_clone_elements; i++)
7682 int element_rnd = level->android_clone_element[i];
7683 int element_em = map_element_RND_to_EM(element_rnd);
7685 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7686 if (em_object_mapping_list[j].element_rnd == element_rnd)
7687 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7691 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7693 struct LevelInfo_EM *level_em = level->native_em_level;
7694 struct LEVEL *lev = level_em->lev;
7697 level->num_android_clone_elements = 0;
7699 for (i = 0; i < TILE_MAX; i++)
7701 int element_em = lev->android_array[i];
7703 boolean element_found = FALSE;
7705 if (element_em == Xblank)
7708 element_rnd = map_element_EM_to_RND(element_em);
7710 for (j = 0; j < level->num_android_clone_elements; j++)
7711 if (level->android_clone_element[j] == element_rnd)
7712 element_found = TRUE;
7716 level->android_clone_element[level->num_android_clone_elements++] =
7719 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7724 if (level->num_android_clone_elements == 0)
7726 level->num_android_clone_elements = 1;
7727 level->android_clone_element[0] = EL_EMPTY;
7731 int map_direction_RND_to_EM(int direction)
7733 return (direction == MV_UP ? 0 :
7734 direction == MV_RIGHT ? 1 :
7735 direction == MV_DOWN ? 2 :
7736 direction == MV_LEFT ? 3 :
7740 int map_direction_EM_to_RND(int direction)
7742 return (direction == 0 ? MV_UP :
7743 direction == 1 ? MV_RIGHT :
7744 direction == 2 ? MV_DOWN :
7745 direction == 3 ? MV_LEFT :
7749 int map_element_RND_to_SP(int element_rnd)
7751 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7753 if (element_rnd >= EL_SP_START &&
7754 element_rnd <= EL_SP_END)
7755 element_sp = element_rnd - EL_SP_START;
7756 else if (element_rnd == EL_EMPTY_SPACE)
7758 else if (element_rnd == EL_INVISIBLE_WALL)
7764 int map_element_SP_to_RND(int element_sp)
7766 int element_rnd = EL_UNKNOWN;
7768 if (element_sp >= 0x00 &&
7770 element_rnd = EL_SP_START + element_sp;
7771 else if (element_sp == 0x28)
7772 element_rnd = EL_INVISIBLE_WALL;
7777 int map_action_SP_to_RND(int action_sp)
7781 case actActive: return ACTION_ACTIVE;
7782 case actImpact: return ACTION_IMPACT;
7783 case actExploding: return ACTION_EXPLODING;
7784 case actDigging: return ACTION_DIGGING;
7785 case actSnapping: return ACTION_SNAPPING;
7786 case actCollecting: return ACTION_COLLECTING;
7787 case actPassing: return ACTION_PASSING;
7788 case actPushing: return ACTION_PUSHING;
7789 case actDropping: return ACTION_DROPPING;
7791 default: return ACTION_DEFAULT;
7795 int map_element_RND_to_MM(int element_rnd)
7797 return (element_rnd >= EL_MM_START_1 &&
7798 element_rnd <= EL_MM_END_1 ?
7799 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7801 element_rnd >= EL_MM_START_2 &&
7802 element_rnd <= EL_MM_END_2 ?
7803 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7805 element_rnd >= EL_CHAR_START &&
7806 element_rnd <= EL_CHAR_END ?
7807 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7809 element_rnd >= EL_MM_RUNTIME_START &&
7810 element_rnd <= EL_MM_RUNTIME_END ?
7811 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7813 element_rnd >= EL_MM_DUMMY_START &&
7814 element_rnd <= EL_MM_DUMMY_END ?
7815 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7817 EL_MM_EMPTY_NATIVE);
7820 int map_element_MM_to_RND(int element_mm)
7822 return (element_mm == EL_MM_EMPTY_NATIVE ||
7823 element_mm == EL_DF_EMPTY_NATIVE ?
7826 element_mm >= EL_MM_START_1_NATIVE &&
7827 element_mm <= EL_MM_END_1_NATIVE ?
7828 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7830 element_mm >= EL_MM_START_2_NATIVE &&
7831 element_mm <= EL_MM_END_2_NATIVE ?
7832 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7834 element_mm >= EL_MM_CHAR_START_NATIVE &&
7835 element_mm <= EL_MM_CHAR_END_NATIVE ?
7836 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7838 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7839 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7840 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7842 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7843 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7844 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7849 int map_action_MM_to_RND(int action_mm)
7851 // all MM actions are defined to exactly match their RND counterparts
7855 int map_sound_MM_to_RND(int sound_mm)
7859 case SND_MM_GAME_LEVELTIME_CHARGING:
7860 return SND_GAME_LEVELTIME_CHARGING;
7862 case SND_MM_GAME_HEALTH_CHARGING:
7863 return SND_GAME_HEALTH_CHARGING;
7866 return SND_UNDEFINED;
7870 int map_mm_wall_element(int element)
7872 return (element >= EL_MM_STEEL_WALL_START &&
7873 element <= EL_MM_STEEL_WALL_END ?
7876 element >= EL_MM_WOODEN_WALL_START &&
7877 element <= EL_MM_WOODEN_WALL_END ?
7880 element >= EL_MM_ICE_WALL_START &&
7881 element <= EL_MM_ICE_WALL_END ?
7884 element >= EL_MM_AMOEBA_WALL_START &&
7885 element <= EL_MM_AMOEBA_WALL_END ?
7888 element >= EL_DF_STEEL_WALL_START &&
7889 element <= EL_DF_STEEL_WALL_END ?
7892 element >= EL_DF_WOODEN_WALL_START &&
7893 element <= EL_DF_WOODEN_WALL_END ?
7899 int map_mm_wall_element_editor(int element)
7903 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
7904 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
7905 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
7906 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
7907 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
7908 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
7910 default: return element;
7914 int get_next_element(int element)
7918 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7919 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7920 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7921 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7922 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7923 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7924 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7925 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7926 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7927 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7928 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7930 default: return element;
7934 int el2img_mm(int element_mm)
7936 return el2img(map_element_MM_to_RND(element_mm));
7939 int el_act_dir2img(int element, int action, int direction)
7941 element = GFX_ELEMENT(element);
7942 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
7944 // direction_graphic[][] == graphic[] for undefined direction graphics
7945 return element_info[element].direction_graphic[action][direction];
7948 static int el_act_dir2crm(int element, int action, int direction)
7950 element = GFX_ELEMENT(element);
7951 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
7953 // direction_graphic[][] == graphic[] for undefined direction graphics
7954 return element_info[element].direction_crumbled[action][direction];
7957 int el_act2img(int element, int action)
7959 element = GFX_ELEMENT(element);
7961 return element_info[element].graphic[action];
7964 int el_act2crm(int element, int action)
7966 element = GFX_ELEMENT(element);
7968 return element_info[element].crumbled[action];
7971 int el_dir2img(int element, int direction)
7973 element = GFX_ELEMENT(element);
7975 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7978 int el2baseimg(int element)
7980 return element_info[element].graphic[ACTION_DEFAULT];
7983 int el2img(int element)
7985 element = GFX_ELEMENT(element);
7987 return element_info[element].graphic[ACTION_DEFAULT];
7990 int el2edimg(int element)
7992 element = GFX_ELEMENT(element);
7994 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7997 int el2preimg(int element)
7999 element = GFX_ELEMENT(element);
8001 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8004 int el2panelimg(int element)
8006 element = GFX_ELEMENT(element);
8008 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8011 int font2baseimg(int font_nr)
8013 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8016 int getBeltNrFromBeltElement(int element)
8018 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8019 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8020 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8023 int getBeltNrFromBeltActiveElement(int element)
8025 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8026 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8027 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8030 int getBeltNrFromBeltSwitchElement(int element)
8032 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8033 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8034 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8037 int getBeltDirNrFromBeltElement(int element)
8039 static int belt_base_element[4] =
8041 EL_CONVEYOR_BELT_1_LEFT,
8042 EL_CONVEYOR_BELT_2_LEFT,
8043 EL_CONVEYOR_BELT_3_LEFT,
8044 EL_CONVEYOR_BELT_4_LEFT
8047 int belt_nr = getBeltNrFromBeltElement(element);
8048 int belt_dir_nr = element - belt_base_element[belt_nr];
8050 return (belt_dir_nr % 3);
8053 int getBeltDirNrFromBeltSwitchElement(int element)
8055 static int belt_base_element[4] =
8057 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8058 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8059 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8060 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8063 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8064 int belt_dir_nr = element - belt_base_element[belt_nr];
8066 return (belt_dir_nr % 3);
8069 int getBeltDirFromBeltElement(int element)
8071 static int belt_move_dir[3] =
8078 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8080 return belt_move_dir[belt_dir_nr];
8083 int getBeltDirFromBeltSwitchElement(int element)
8085 static int belt_move_dir[3] =
8092 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8094 return belt_move_dir[belt_dir_nr];
8097 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8099 static int belt_base_element[4] =
8101 EL_CONVEYOR_BELT_1_LEFT,
8102 EL_CONVEYOR_BELT_2_LEFT,
8103 EL_CONVEYOR_BELT_3_LEFT,
8104 EL_CONVEYOR_BELT_4_LEFT
8107 return belt_base_element[belt_nr] + belt_dir_nr;
8110 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8112 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8114 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8117 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8119 static int belt_base_element[4] =
8121 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8122 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8123 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8124 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8127 return belt_base_element[belt_nr] + belt_dir_nr;
8130 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8132 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8134 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8137 boolean getTeamMode_EM(void)
8139 return game.team_mode || network_playing;
8142 int getGameFrameDelay_EM(int native_em_game_frame_delay)
8144 int game_frame_delay_value;
8146 game_frame_delay_value =
8147 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
8148 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
8151 if (tape.playing && tape.warp_forward && !tape.pausing)
8152 game_frame_delay_value = 0;
8154 return game_frame_delay_value;
8157 unsigned int InitRND(int seed)
8159 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8160 return InitEngineRandom_EM(seed);
8161 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8162 return InitEngineRandom_SP(seed);
8163 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8164 return InitEngineRandom_MM(seed);
8166 return InitEngineRandom_RND(seed);
8169 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
8170 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
8172 static int get_effective_element_EM(int tile, int frame_em)
8174 int element = object_mapping[tile].element_rnd;
8175 int action = object_mapping[tile].action;
8176 boolean is_backside = object_mapping[tile].is_backside;
8177 boolean action_removing = (action == ACTION_DIGGING ||
8178 action == ACTION_SNAPPING ||
8179 action == ACTION_COLLECTING);
8185 case Yacid_splash_eB:
8186 case Yacid_splash_wB:
8187 return (frame_em > 5 ? EL_EMPTY : element);
8193 else // frame_em == 7
8197 case Yacid_splash_eB:
8198 case Yacid_splash_wB:
8201 case Yemerald_stone:
8204 case Ydiamond_stone:
8208 case Xdrip_stretchB:
8227 case Xsand_stonein_1:
8228 case Xsand_stonein_2:
8229 case Xsand_stonein_3:
8230 case Xsand_stonein_4:
8234 return (is_backside || action_removing ? EL_EMPTY : element);
8239 static boolean check_linear_animation_EM(int tile)
8243 case Xsand_stonesand_1:
8244 case Xsand_stonesand_quickout_1:
8245 case Xsand_sandstone_1:
8246 case Xsand_stonein_1:
8247 case Xsand_stoneout_1:
8266 case Yacid_splash_eB:
8267 case Yacid_splash_wB:
8268 case Yemerald_stone:
8275 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8276 boolean has_crumbled_graphics,
8277 int crumbled, int sync_frame)
8279 // if element can be crumbled, but certain action graphics are just empty
8280 // space (like instantly snapping sand to empty space in 1 frame), do not
8281 // treat these empty space graphics as crumbled graphics in EMC engine
8282 if (crumbled == IMG_EMPTY_SPACE)
8283 has_crumbled_graphics = FALSE;
8285 if (has_crumbled_graphics)
8287 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8288 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8289 g_crumbled->anim_delay,
8290 g_crumbled->anim_mode,
8291 g_crumbled->anim_start_frame,
8294 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8295 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8297 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8298 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8300 g_em->has_crumbled_graphics = TRUE;
8304 g_em->crumbled_bitmap = NULL;
8305 g_em->crumbled_src_x = 0;
8306 g_em->crumbled_src_y = 0;
8307 g_em->crumbled_border_size = 0;
8308 g_em->crumbled_tile_size = 0;
8310 g_em->has_crumbled_graphics = FALSE;
8315 void ResetGfxAnimation_EM(int x, int y, int tile)
8321 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8322 int tile, int frame_em, int x, int y)
8324 int action = object_mapping[tile].action;
8325 int direction = object_mapping[tile].direction;
8326 int effective_element = get_effective_element_EM(tile, frame_em);
8327 int graphic = (direction == MV_NONE ?
8328 el_act2img(effective_element, action) :
8329 el_act_dir2img(effective_element, action, direction));
8330 struct GraphicInfo *g = &graphic_info[graphic];
8332 boolean action_removing = (action == ACTION_DIGGING ||
8333 action == ACTION_SNAPPING ||
8334 action == ACTION_COLLECTING);
8335 boolean action_moving = (action == ACTION_FALLING ||
8336 action == ACTION_MOVING ||
8337 action == ACTION_PUSHING ||
8338 action == ACTION_EATING ||
8339 action == ACTION_FILLING ||
8340 action == ACTION_EMPTYING);
8341 boolean action_falling = (action == ACTION_FALLING ||
8342 action == ACTION_FILLING ||
8343 action == ACTION_EMPTYING);
8345 // special case: graphic uses "2nd movement tile" and has defined
8346 // 7 frames for movement animation (or less) => use default graphic
8347 // for last (8th) frame which ends the movement animation
8348 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8350 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8351 graphic = (direction == MV_NONE ?
8352 el_act2img(effective_element, action) :
8353 el_act_dir2img(effective_element, action, direction));
8355 g = &graphic_info[graphic];
8358 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8362 else if (action_moving)
8364 boolean is_backside = object_mapping[tile].is_backside;
8368 int direction = object_mapping[tile].direction;
8369 int move_dir = (action_falling ? MV_DOWN : direction);
8374 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8375 if (g->double_movement && frame_em == 0)
8379 if (move_dir == MV_LEFT)
8380 GfxFrame[x - 1][y] = GfxFrame[x][y];
8381 else if (move_dir == MV_RIGHT)
8382 GfxFrame[x + 1][y] = GfxFrame[x][y];
8383 else if (move_dir == MV_UP)
8384 GfxFrame[x][y - 1] = GfxFrame[x][y];
8385 else if (move_dir == MV_DOWN)
8386 GfxFrame[x][y + 1] = GfxFrame[x][y];
8393 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8394 if (tile == Xsand_stonesand_quickout_1 ||
8395 tile == Xsand_stonesand_quickout_2)
8399 if (graphic_info[graphic].anim_global_sync)
8400 sync_frame = FrameCounter;
8401 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8402 sync_frame = GfxFrame[x][y];
8404 sync_frame = 0; // playfield border (pseudo steel)
8406 SetRandomAnimationValue(x, y);
8408 int frame = getAnimationFrame(g->anim_frames,
8411 g->anim_start_frame,
8414 g_em->unique_identifier =
8415 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8418 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8419 int tile, int frame_em, int x, int y)
8421 int action = object_mapping[tile].action;
8422 int direction = object_mapping[tile].direction;
8423 boolean is_backside = object_mapping[tile].is_backside;
8424 int effective_element = get_effective_element_EM(tile, frame_em);
8425 int effective_action = action;
8426 int graphic = (direction == MV_NONE ?
8427 el_act2img(effective_element, effective_action) :
8428 el_act_dir2img(effective_element, effective_action,
8430 int crumbled = (direction == MV_NONE ?
8431 el_act2crm(effective_element, effective_action) :
8432 el_act_dir2crm(effective_element, effective_action,
8434 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8435 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8436 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8437 struct GraphicInfo *g = &graphic_info[graphic];
8440 // special case: graphic uses "2nd movement tile" and has defined
8441 // 7 frames for movement animation (or less) => use default graphic
8442 // for last (8th) frame which ends the movement animation
8443 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8445 effective_action = ACTION_DEFAULT;
8446 graphic = (direction == MV_NONE ?
8447 el_act2img(effective_element, effective_action) :
8448 el_act_dir2img(effective_element, effective_action,
8450 crumbled = (direction == MV_NONE ?
8451 el_act2crm(effective_element, effective_action) :
8452 el_act_dir2crm(effective_element, effective_action,
8455 g = &graphic_info[graphic];
8458 if (graphic_info[graphic].anim_global_sync)
8459 sync_frame = FrameCounter;
8460 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8461 sync_frame = GfxFrame[x][y];
8463 sync_frame = 0; // playfield border (pseudo steel)
8465 SetRandomAnimationValue(x, y);
8467 int frame = getAnimationFrame(g->anim_frames,
8470 g->anim_start_frame,
8473 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8474 g->double_movement && is_backside);
8476 // (updating the "crumbled" graphic definitions is probably not really needed,
8477 // as animations for crumbled graphics can't be longer than one EMC cycle)
8478 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8482 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8483 int player_nr, int anim, int frame_em)
8485 int element = player_mapping[player_nr][anim].element_rnd;
8486 int action = player_mapping[player_nr][anim].action;
8487 int direction = player_mapping[player_nr][anim].direction;
8488 int graphic = (direction == MV_NONE ?
8489 el_act2img(element, action) :
8490 el_act_dir2img(element, action, direction));
8491 struct GraphicInfo *g = &graphic_info[graphic];
8494 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8496 stored_player[player_nr].StepFrame = frame_em;
8498 sync_frame = stored_player[player_nr].Frame;
8500 int frame = getAnimationFrame(g->anim_frames,
8503 g->anim_start_frame,
8506 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8507 &g_em->src_x, &g_em->src_y, FALSE);
8510 void InitGraphicInfo_EM(void)
8515 int num_em_gfx_errors = 0;
8517 if (graphic_info_em_object[0][0].bitmap == NULL)
8519 // EM graphics not yet initialized in em_open_all()
8524 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8527 // always start with reliable default values
8528 for (i = 0; i < TILE_MAX; i++)
8530 object_mapping[i].element_rnd = EL_UNKNOWN;
8531 object_mapping[i].is_backside = FALSE;
8532 object_mapping[i].action = ACTION_DEFAULT;
8533 object_mapping[i].direction = MV_NONE;
8536 // always start with reliable default values
8537 for (p = 0; p < MAX_PLAYERS; p++)
8539 for (i = 0; i < SPR_MAX; i++)
8541 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8542 player_mapping[p][i].action = ACTION_DEFAULT;
8543 player_mapping[p][i].direction = MV_NONE;
8547 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8549 int e = em_object_mapping_list[i].element_em;
8551 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8552 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8554 if (em_object_mapping_list[i].action != -1)
8555 object_mapping[e].action = em_object_mapping_list[i].action;
8557 if (em_object_mapping_list[i].direction != -1)
8558 object_mapping[e].direction =
8559 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8562 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8564 int a = em_player_mapping_list[i].action_em;
8565 int p = em_player_mapping_list[i].player_nr;
8567 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8569 if (em_player_mapping_list[i].action != -1)
8570 player_mapping[p][a].action = em_player_mapping_list[i].action;
8572 if (em_player_mapping_list[i].direction != -1)
8573 player_mapping[p][a].direction =
8574 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8577 for (i = 0; i < TILE_MAX; i++)
8579 int element = object_mapping[i].element_rnd;
8580 int action = object_mapping[i].action;
8581 int direction = object_mapping[i].direction;
8582 boolean is_backside = object_mapping[i].is_backside;
8583 boolean action_exploding = ((action == ACTION_EXPLODING ||
8584 action == ACTION_SMASHED_BY_ROCK ||
8585 action == ACTION_SMASHED_BY_SPRING) &&
8586 element != EL_DIAMOND);
8587 boolean action_active = (action == ACTION_ACTIVE);
8588 boolean action_other = (action == ACTION_OTHER);
8590 for (j = 0; j < 8; j++)
8592 int effective_element = get_effective_element_EM(i, j);
8593 int effective_action = (j < 7 ? action :
8594 i == Xdrip_stretch ? action :
8595 i == Xdrip_stretchB ? action :
8596 i == Ydrip_s1 ? action :
8597 i == Ydrip_s1B ? action :
8598 i == Xball_1B ? action :
8599 i == Xball_2 ? action :
8600 i == Xball_2B ? action :
8601 i == Yball_eat ? action :
8602 i == Ykey_1_eat ? action :
8603 i == Ykey_2_eat ? action :
8604 i == Ykey_3_eat ? action :
8605 i == Ykey_4_eat ? action :
8606 i == Ykey_5_eat ? action :
8607 i == Ykey_6_eat ? action :
8608 i == Ykey_7_eat ? action :
8609 i == Ykey_8_eat ? action :
8610 i == Ylenses_eat ? action :
8611 i == Ymagnify_eat ? action :
8612 i == Ygrass_eat ? action :
8613 i == Ydirt_eat ? action :
8614 i == Xsand_stonein_1 ? action :
8615 i == Xsand_stonein_2 ? action :
8616 i == Xsand_stonein_3 ? action :
8617 i == Xsand_stonein_4 ? action :
8618 i == Xsand_stoneout_1 ? action :
8619 i == Xsand_stoneout_2 ? action :
8620 i == Xboom_android ? ACTION_EXPLODING :
8621 action_exploding ? ACTION_EXPLODING :
8622 action_active ? action :
8623 action_other ? action :
8625 int graphic = (el_act_dir2img(effective_element, effective_action,
8627 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8629 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8630 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8631 boolean has_action_graphics = (graphic != base_graphic);
8632 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8633 struct GraphicInfo *g = &graphic_info[graphic];
8634 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8637 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8638 boolean special_animation = (action != ACTION_DEFAULT &&
8639 g->anim_frames == 3 &&
8640 g->anim_delay == 2 &&
8641 g->anim_mode & ANIM_LINEAR);
8642 int sync_frame = (i == Xdrip_stretch ? 7 :
8643 i == Xdrip_stretchB ? 7 :
8644 i == Ydrip_s2 ? j + 8 :
8645 i == Ydrip_s2B ? j + 8 :
8654 i == Xfake_acid_1 ? 0 :
8655 i == Xfake_acid_2 ? 10 :
8656 i == Xfake_acid_3 ? 20 :
8657 i == Xfake_acid_4 ? 30 :
8658 i == Xfake_acid_5 ? 40 :
8659 i == Xfake_acid_6 ? 50 :
8660 i == Xfake_acid_7 ? 60 :
8661 i == Xfake_acid_8 ? 70 :
8663 i == Xball_2B ? j + 8 :
8664 i == Yball_eat ? j + 1 :
8665 i == Ykey_1_eat ? j + 1 :
8666 i == Ykey_2_eat ? j + 1 :
8667 i == Ykey_3_eat ? j + 1 :
8668 i == Ykey_4_eat ? j + 1 :
8669 i == Ykey_5_eat ? j + 1 :
8670 i == Ykey_6_eat ? j + 1 :
8671 i == Ykey_7_eat ? j + 1 :
8672 i == Ykey_8_eat ? j + 1 :
8673 i == Ylenses_eat ? j + 1 :
8674 i == Ymagnify_eat ? j + 1 :
8675 i == Ygrass_eat ? j + 1 :
8676 i == Ydirt_eat ? j + 1 :
8677 i == Xamoeba_1 ? 0 :
8678 i == Xamoeba_2 ? 1 :
8679 i == Xamoeba_3 ? 2 :
8680 i == Xamoeba_4 ? 3 :
8681 i == Xamoeba_5 ? 0 :
8682 i == Xamoeba_6 ? 1 :
8683 i == Xamoeba_7 ? 2 :
8684 i == Xamoeba_8 ? 3 :
8685 i == Xexit_2 ? j + 8 :
8686 i == Xexit_3 ? j + 16 :
8687 i == Xdynamite_1 ? 0 :
8688 i == Xdynamite_2 ? 8 :
8689 i == Xdynamite_3 ? 16 :
8690 i == Xdynamite_4 ? 24 :
8691 i == Xsand_stonein_1 ? j + 1 :
8692 i == Xsand_stonein_2 ? j + 9 :
8693 i == Xsand_stonein_3 ? j + 17 :
8694 i == Xsand_stonein_4 ? j + 25 :
8695 i == Xsand_stoneout_1 && j == 0 ? 0 :
8696 i == Xsand_stoneout_1 && j == 1 ? 0 :
8697 i == Xsand_stoneout_1 && j == 2 ? 1 :
8698 i == Xsand_stoneout_1 && j == 3 ? 2 :
8699 i == Xsand_stoneout_1 && j == 4 ? 2 :
8700 i == Xsand_stoneout_1 && j == 5 ? 3 :
8701 i == Xsand_stoneout_1 && j == 6 ? 4 :
8702 i == Xsand_stoneout_1 && j == 7 ? 4 :
8703 i == Xsand_stoneout_2 && j == 0 ? 5 :
8704 i == Xsand_stoneout_2 && j == 1 ? 6 :
8705 i == Xsand_stoneout_2 && j == 2 ? 7 :
8706 i == Xsand_stoneout_2 && j == 3 ? 8 :
8707 i == Xsand_stoneout_2 && j == 4 ? 9 :
8708 i == Xsand_stoneout_2 && j == 5 ? 11 :
8709 i == Xsand_stoneout_2 && j == 6 ? 13 :
8710 i == Xsand_stoneout_2 && j == 7 ? 15 :
8711 i == Xboom_bug && j == 1 ? 2 :
8712 i == Xboom_bug && j == 2 ? 2 :
8713 i == Xboom_bug && j == 3 ? 4 :
8714 i == Xboom_bug && j == 4 ? 4 :
8715 i == Xboom_bug && j == 5 ? 2 :
8716 i == Xboom_bug && j == 6 ? 2 :
8717 i == Xboom_bug && j == 7 ? 0 :
8718 i == Xboom_bomb && j == 1 ? 2 :
8719 i == Xboom_bomb && j == 2 ? 2 :
8720 i == Xboom_bomb && j == 3 ? 4 :
8721 i == Xboom_bomb && j == 4 ? 4 :
8722 i == Xboom_bomb && j == 5 ? 2 :
8723 i == Xboom_bomb && j == 6 ? 2 :
8724 i == Xboom_bomb && j == 7 ? 0 :
8725 i == Xboom_android && j == 7 ? 6 :
8726 i == Xboom_1 && j == 1 ? 2 :
8727 i == Xboom_1 && j == 2 ? 2 :
8728 i == Xboom_1 && j == 3 ? 4 :
8729 i == Xboom_1 && j == 4 ? 4 :
8730 i == Xboom_1 && j == 5 ? 6 :
8731 i == Xboom_1 && j == 6 ? 6 :
8732 i == Xboom_1 && j == 7 ? 8 :
8733 i == Xboom_2 && j == 0 ? 8 :
8734 i == Xboom_2 && j == 1 ? 8 :
8735 i == Xboom_2 && j == 2 ? 10 :
8736 i == Xboom_2 && j == 3 ? 10 :
8737 i == Xboom_2 && j == 4 ? 10 :
8738 i == Xboom_2 && j == 5 ? 12 :
8739 i == Xboom_2 && j == 6 ? 12 :
8740 i == Xboom_2 && j == 7 ? 12 :
8741 special_animation && j == 4 ? 3 :
8742 effective_action != action ? 0 :
8746 Bitmap *debug_bitmap = g_em->bitmap;
8747 int debug_src_x = g_em->src_x;
8748 int debug_src_y = g_em->src_y;
8751 int frame = getAnimationFrame(g->anim_frames,
8754 g->anim_start_frame,
8757 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8758 g->double_movement && is_backside);
8760 g_em->bitmap = src_bitmap;
8761 g_em->src_x = src_x;
8762 g_em->src_y = src_y;
8763 g_em->src_offset_x = 0;
8764 g_em->src_offset_y = 0;
8765 g_em->dst_offset_x = 0;
8766 g_em->dst_offset_y = 0;
8767 g_em->width = TILEX;
8768 g_em->height = TILEY;
8770 g_em->preserve_background = FALSE;
8772 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8775 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8776 effective_action == ACTION_MOVING ||
8777 effective_action == ACTION_PUSHING ||
8778 effective_action == ACTION_EATING)) ||
8779 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8780 effective_action == ACTION_EMPTYING)))
8783 (effective_action == ACTION_FALLING ||
8784 effective_action == ACTION_FILLING ||
8785 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8786 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8787 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8788 int num_steps = (i == Ydrip_s1 ? 16 :
8789 i == Ydrip_s1B ? 16 :
8790 i == Ydrip_s2 ? 16 :
8791 i == Ydrip_s2B ? 16 :
8792 i == Xsand_stonein_1 ? 32 :
8793 i == Xsand_stonein_2 ? 32 :
8794 i == Xsand_stonein_3 ? 32 :
8795 i == Xsand_stonein_4 ? 32 :
8796 i == Xsand_stoneout_1 ? 16 :
8797 i == Xsand_stoneout_2 ? 16 : 8);
8798 int cx = ABS(dx) * (TILEX / num_steps);
8799 int cy = ABS(dy) * (TILEY / num_steps);
8800 int step_frame = (i == Ydrip_s2 ? j + 8 :
8801 i == Ydrip_s2B ? j + 8 :
8802 i == Xsand_stonein_2 ? j + 8 :
8803 i == Xsand_stonein_3 ? j + 16 :
8804 i == Xsand_stonein_4 ? j + 24 :
8805 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8806 int step = (is_backside ? step_frame : num_steps - step_frame);
8808 if (is_backside) // tile where movement starts
8810 if (dx < 0 || dy < 0)
8812 g_em->src_offset_x = cx * step;
8813 g_em->src_offset_y = cy * step;
8817 g_em->dst_offset_x = cx * step;
8818 g_em->dst_offset_y = cy * step;
8821 else // tile where movement ends
8823 if (dx < 0 || dy < 0)
8825 g_em->dst_offset_x = cx * step;
8826 g_em->dst_offset_y = cy * step;
8830 g_em->src_offset_x = cx * step;
8831 g_em->src_offset_y = cy * step;
8835 g_em->width = TILEX - cx * step;
8836 g_em->height = TILEY - cy * step;
8839 // create unique graphic identifier to decide if tile must be redrawn
8840 /* bit 31 - 16 (16 bit): EM style graphic
8841 bit 15 - 12 ( 4 bit): EM style frame
8842 bit 11 - 6 ( 6 bit): graphic width
8843 bit 5 - 0 ( 6 bit): graphic height */
8844 g_em->unique_identifier =
8845 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8849 // skip check for EMC elements not contained in original EMC artwork
8850 if (element == EL_EMC_FAKE_ACID)
8853 if (g_em->bitmap != debug_bitmap ||
8854 g_em->src_x != debug_src_x ||
8855 g_em->src_y != debug_src_y ||
8856 g_em->src_offset_x != 0 ||
8857 g_em->src_offset_y != 0 ||
8858 g_em->dst_offset_x != 0 ||
8859 g_em->dst_offset_y != 0 ||
8860 g_em->width != TILEX ||
8861 g_em->height != TILEY)
8863 static int last_i = -1;
8871 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8872 i, element, element_info[element].token_name,
8873 element_action_info[effective_action].suffix, direction);
8875 if (element != effective_element)
8876 printf(" [%d ('%s')]",
8878 element_info[effective_element].token_name);
8882 if (g_em->bitmap != debug_bitmap)
8883 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8884 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8886 if (g_em->src_x != debug_src_x ||
8887 g_em->src_y != debug_src_y)
8888 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8889 j, (is_backside ? 'B' : 'F'),
8890 g_em->src_x, g_em->src_y,
8891 g_em->src_x / 32, g_em->src_y / 32,
8892 debug_src_x, debug_src_y,
8893 debug_src_x / 32, debug_src_y / 32);
8895 if (g_em->src_offset_x != 0 ||
8896 g_em->src_offset_y != 0 ||
8897 g_em->dst_offset_x != 0 ||
8898 g_em->dst_offset_y != 0)
8899 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8901 g_em->src_offset_x, g_em->src_offset_y,
8902 g_em->dst_offset_x, g_em->dst_offset_y);
8904 if (g_em->width != TILEX ||
8905 g_em->height != TILEY)
8906 printf(" %d (%d): size %d,%d should be %d,%d\n",
8908 g_em->width, g_em->height, TILEX, TILEY);
8910 num_em_gfx_errors++;
8917 for (i = 0; i < TILE_MAX; i++)
8919 for (j = 0; j < 8; j++)
8921 int element = object_mapping[i].element_rnd;
8922 int action = object_mapping[i].action;
8923 int direction = object_mapping[i].direction;
8924 boolean is_backside = object_mapping[i].is_backside;
8925 int graphic_action = el_act_dir2img(element, action, direction);
8926 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8928 if ((action == ACTION_SMASHED_BY_ROCK ||
8929 action == ACTION_SMASHED_BY_SPRING ||
8930 action == ACTION_EATING) &&
8931 graphic_action == graphic_default)
8933 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8934 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8935 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8936 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8939 // no separate animation for "smashed by rock" -- use rock instead
8940 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8941 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8943 g_em->bitmap = g_xx->bitmap;
8944 g_em->src_x = g_xx->src_x;
8945 g_em->src_y = g_xx->src_y;
8946 g_em->src_offset_x = g_xx->src_offset_x;
8947 g_em->src_offset_y = g_xx->src_offset_y;
8948 g_em->dst_offset_x = g_xx->dst_offset_x;
8949 g_em->dst_offset_y = g_xx->dst_offset_y;
8950 g_em->width = g_xx->width;
8951 g_em->height = g_xx->height;
8952 g_em->unique_identifier = g_xx->unique_identifier;
8955 g_em->preserve_background = TRUE;
8960 for (p = 0; p < MAX_PLAYERS; p++)
8962 for (i = 0; i < SPR_MAX; i++)
8964 int element = player_mapping[p][i].element_rnd;
8965 int action = player_mapping[p][i].action;
8966 int direction = player_mapping[p][i].direction;
8968 for (j = 0; j < 8; j++)
8970 int effective_element = element;
8971 int effective_action = action;
8972 int graphic = (direction == MV_NONE ?
8973 el_act2img(effective_element, effective_action) :
8974 el_act_dir2img(effective_element, effective_action,
8976 struct GraphicInfo *g = &graphic_info[graphic];
8977 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8983 Bitmap *debug_bitmap = g_em->bitmap;
8984 int debug_src_x = g_em->src_x;
8985 int debug_src_y = g_em->src_y;
8988 int frame = getAnimationFrame(g->anim_frames,
8991 g->anim_start_frame,
8994 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8996 g_em->bitmap = src_bitmap;
8997 g_em->src_x = src_x;
8998 g_em->src_y = src_y;
8999 g_em->src_offset_x = 0;
9000 g_em->src_offset_y = 0;
9001 g_em->dst_offset_x = 0;
9002 g_em->dst_offset_y = 0;
9003 g_em->width = TILEX;
9004 g_em->height = TILEY;
9008 // skip check for EMC elements not contained in original EMC artwork
9009 if (element == EL_PLAYER_3 ||
9010 element == EL_PLAYER_4)
9013 if (g_em->bitmap != debug_bitmap ||
9014 g_em->src_x != debug_src_x ||
9015 g_em->src_y != debug_src_y)
9017 static int last_i = -1;
9025 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
9026 p, i, element, element_info[element].token_name,
9027 element_action_info[effective_action].suffix, direction);
9029 if (element != effective_element)
9030 printf(" [%d ('%s')]",
9032 element_info[effective_element].token_name);
9036 if (g_em->bitmap != debug_bitmap)
9037 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
9038 j, (int)(g_em->bitmap), (int)(debug_bitmap));
9040 if (g_em->src_x != debug_src_x ||
9041 g_em->src_y != debug_src_y)
9042 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
9044 g_em->src_x, g_em->src_y,
9045 g_em->src_x / 32, g_em->src_y / 32,
9046 debug_src_x, debug_src_y,
9047 debug_src_x / 32, debug_src_y / 32);
9049 num_em_gfx_errors++;
9059 printf("::: [%d errors found]\n", num_em_gfx_errors);
9065 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9066 boolean any_player_moving,
9067 boolean any_player_snapping,
9068 boolean any_player_dropping)
9070 if (frame == 0 && !any_player_dropping)
9072 if (!local_player->was_waiting)
9074 if (!CheckSaveEngineSnapshotToList())
9077 local_player->was_waiting = TRUE;
9080 else if (any_player_moving || any_player_snapping || any_player_dropping)
9082 local_player->was_waiting = FALSE;
9086 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9087 boolean murphy_is_dropping)
9089 if (murphy_is_waiting)
9091 if (!local_player->was_waiting)
9093 if (!CheckSaveEngineSnapshotToList())
9096 local_player->was_waiting = TRUE;
9101 local_player->was_waiting = FALSE;
9105 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9106 boolean button_released)
9108 if (button_released)
9110 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9111 CheckSaveEngineSnapshotToList();
9113 else if (element_clicked)
9115 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9116 CheckSaveEngineSnapshotToList();
9118 game.snapshot.changed_action = TRUE;
9122 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9123 boolean any_player_moving,
9124 boolean any_player_snapping,
9125 boolean any_player_dropping)
9127 if (tape.single_step && tape.recording && !tape.pausing)
9128 if (frame == 0 && !any_player_dropping)
9129 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9131 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9132 any_player_snapping, any_player_dropping);
9135 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9136 boolean murphy_is_dropping)
9138 boolean murphy_starts_dropping = FALSE;
9141 for (i = 0; i < MAX_PLAYERS; i++)
9142 if (stored_player[i].force_dropping)
9143 murphy_starts_dropping = TRUE;
9145 if (tape.single_step && tape.recording && !tape.pausing)
9146 if (murphy_is_waiting && !murphy_starts_dropping)
9147 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9149 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9152 void CheckSingleStepMode_MM(boolean element_clicked,
9153 boolean button_released)
9155 if (tape.single_step && tape.recording && !tape.pausing)
9156 if (button_released)
9157 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9159 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9162 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9163 int graphic, int sync_frame, int x, int y)
9165 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9167 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9170 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9172 return (IS_NEXT_FRAME(sync_frame, graphic));
9175 int getGraphicInfo_Delay(int graphic)
9177 return graphic_info[graphic].anim_delay;
9180 void PlayMenuSoundExt(int sound)
9182 if (sound == SND_UNDEFINED)
9185 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9186 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9189 if (IS_LOOP_SOUND(sound))
9190 PlaySoundLoop(sound);
9195 void PlayMenuSound(void)
9197 PlayMenuSoundExt(menu.sound[game_status]);
9200 void PlayMenuSoundStereo(int sound, int stereo_position)
9202 if (sound == SND_UNDEFINED)
9205 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9206 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9209 if (IS_LOOP_SOUND(sound))
9210 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9212 PlaySoundStereo(sound, stereo_position);
9215 void PlayMenuSoundIfLoopExt(int sound)
9217 if (sound == SND_UNDEFINED)
9220 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9221 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9224 if (IS_LOOP_SOUND(sound))
9225 PlaySoundLoop(sound);
9228 void PlayMenuSoundIfLoop(void)
9230 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9233 void PlayMenuMusicExt(int music)
9235 if (music == MUS_UNDEFINED)
9238 if (!setup.sound_music)
9241 if (IS_LOOP_MUSIC(music))
9242 PlayMusicLoop(music);
9247 void PlayMenuMusic(void)
9249 char *curr_music = getCurrentlyPlayingMusicFilename();
9250 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9252 if (!strEqual(curr_music, next_music))
9253 PlayMenuMusicExt(menu.music[game_status]);
9256 void PlayMenuSoundsAndMusic(void)
9262 static void FadeMenuSounds(void)
9267 static void FadeMenuMusic(void)
9269 char *curr_music = getCurrentlyPlayingMusicFilename();
9270 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9272 if (!strEqual(curr_music, next_music))
9276 void FadeMenuSoundsAndMusic(void)
9282 void PlaySoundActivating(void)
9285 PlaySound(SND_MENU_ITEM_ACTIVATING);
9289 void PlaySoundSelecting(void)
9292 PlaySound(SND_MENU_ITEM_SELECTING);
9296 void ToggleFullscreenOrChangeWindowScalingIfNeeded(void)
9298 boolean change_fullscreen = (setup.fullscreen !=
9299 video.fullscreen_enabled);
9300 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
9301 setup.window_scaling_percent !=
9302 video.window_scaling_percent);
9304 if (change_window_scaling_percent && video.fullscreen_enabled)
9307 if (!change_window_scaling_percent && !video.fullscreen_available)
9310 if (change_window_scaling_percent)
9312 SDLSetWindowScaling(setup.window_scaling_percent);
9316 else if (change_fullscreen)
9318 SDLSetWindowFullscreen(setup.fullscreen);
9320 // set setup value according to successfully changed fullscreen mode
9321 setup.fullscreen = video.fullscreen_enabled;
9326 if (change_fullscreen ||
9327 change_window_scaling_percent)
9329 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9331 // save backbuffer content which gets lost when toggling fullscreen mode
9332 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9334 if (change_window_scaling_percent)
9336 // keep window mode, but change window scaling
9337 video.fullscreen_enabled = TRUE; // force new window scaling
9340 // toggle fullscreen
9341 ChangeVideoModeIfNeeded(setup.fullscreen);
9343 // set setup value according to successfully changed fullscreen mode
9344 setup.fullscreen = video.fullscreen_enabled;
9346 // restore backbuffer content from temporary backbuffer backup bitmap
9347 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9349 FreeBitmap(tmp_backbuffer);
9351 // update visible window/screen
9352 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9356 static void JoinRectangles(int *x, int *y, int *width, int *height,
9357 int x2, int y2, int width2, int height2)
9359 // do not join with "off-screen" rectangle
9360 if (x2 == -1 || y2 == -1)
9365 *width = MAX(*width, width2);
9366 *height = MAX(*height, height2);
9369 void SetAnimStatus(int anim_status_new)
9371 if (anim_status_new == GAME_MODE_MAIN)
9372 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9373 else if (anim_status_new == GAME_MODE_SCORES)
9374 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9376 global.anim_status_next = anim_status_new;
9378 // directly set screen modes that are entered without fading
9379 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9380 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9381 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9382 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9383 global.anim_status = global.anim_status_next;
9386 void SetGameStatus(int game_status_new)
9388 if (game_status_new != game_status)
9389 game_status_last_screen = game_status;
9391 game_status = game_status_new;
9393 SetAnimStatus(game_status_new);
9396 void SetFontStatus(int game_status_new)
9398 static int last_game_status = -1;
9400 if (game_status_new != -1)
9402 // set game status for font use after storing last game status
9403 last_game_status = game_status;
9404 game_status = game_status_new;
9408 // reset game status after font use from last stored game status
9409 game_status = last_game_status;
9413 void ResetFontStatus(void)
9418 void SetLevelSetInfo(char *identifier, int level_nr)
9420 setString(&levelset.identifier, identifier);
9422 levelset.level_nr = level_nr;
9425 boolean CheckIfAllViewportsHaveChanged(void)
9427 // if game status has not changed, viewports have not changed either
9428 if (game_status == game_status_last)
9431 // check if all viewports have changed with current game status
9433 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9434 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9435 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9436 int new_real_sx = vp_playfield->x;
9437 int new_real_sy = vp_playfield->y;
9438 int new_full_sxsize = vp_playfield->width;
9439 int new_full_sysize = vp_playfield->height;
9440 int new_dx = vp_door_1->x;
9441 int new_dy = vp_door_1->y;
9442 int new_dxsize = vp_door_1->width;
9443 int new_dysize = vp_door_1->height;
9444 int new_vx = vp_door_2->x;
9445 int new_vy = vp_door_2->y;
9446 int new_vxsize = vp_door_2->width;
9447 int new_vysize = vp_door_2->height;
9449 boolean playfield_viewport_has_changed =
9450 (new_real_sx != REAL_SX ||
9451 new_real_sy != REAL_SY ||
9452 new_full_sxsize != FULL_SXSIZE ||
9453 new_full_sysize != FULL_SYSIZE);
9455 boolean door_1_viewport_has_changed =
9458 new_dxsize != DXSIZE ||
9459 new_dysize != DYSIZE);
9461 boolean door_2_viewport_has_changed =
9464 new_vxsize != VXSIZE ||
9465 new_vysize != VYSIZE ||
9466 game_status_last == GAME_MODE_EDITOR);
9468 return (playfield_viewport_has_changed &&
9469 door_1_viewport_has_changed &&
9470 door_2_viewport_has_changed);
9473 boolean CheckFadeAll(void)
9475 return (CheckIfGlobalBorderHasChanged() ||
9476 CheckIfAllViewportsHaveChanged());
9479 void ChangeViewportPropertiesIfNeeded(void)
9481 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9482 FALSE : setup.small_game_graphics);
9483 int gfx_game_mode = game_status;
9484 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9486 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9487 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9488 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9489 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9490 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9491 int new_win_xsize = vp_window->width;
9492 int new_win_ysize = vp_window->height;
9493 int border_left = vp_playfield->border_left;
9494 int border_right = vp_playfield->border_right;
9495 int border_top = vp_playfield->border_top;
9496 int border_bottom = vp_playfield->border_bottom;
9497 int new_sx = vp_playfield->x + border_left;
9498 int new_sy = vp_playfield->y + border_top;
9499 int new_sxsize = vp_playfield->width - border_left - border_right;
9500 int new_sysize = vp_playfield->height - border_top - border_bottom;
9501 int new_real_sx = vp_playfield->x;
9502 int new_real_sy = vp_playfield->y;
9503 int new_full_sxsize = vp_playfield->width;
9504 int new_full_sysize = vp_playfield->height;
9505 int new_dx = vp_door_1->x;
9506 int new_dy = vp_door_1->y;
9507 int new_dxsize = vp_door_1->width;
9508 int new_dysize = vp_door_1->height;
9509 int new_vx = vp_door_2->x;
9510 int new_vy = vp_door_2->y;
9511 int new_vxsize = vp_door_2->width;
9512 int new_vysize = vp_door_2->height;
9513 int new_ex = vp_door_3->x;
9514 int new_ey = vp_door_3->y;
9515 int new_exsize = vp_door_3->width;
9516 int new_eysize = vp_door_3->height;
9517 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9518 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9519 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9520 int new_scr_fieldx = new_sxsize / tilesize;
9521 int new_scr_fieldy = new_sysize / tilesize;
9522 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9523 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9524 boolean init_gfx_buffers = FALSE;
9525 boolean init_video_buffer = FALSE;
9526 boolean init_gadgets_and_anims = FALSE;
9527 boolean init_em_graphics = FALSE;
9529 if (new_win_xsize != WIN_XSIZE ||
9530 new_win_ysize != WIN_YSIZE)
9532 WIN_XSIZE = new_win_xsize;
9533 WIN_YSIZE = new_win_ysize;
9535 init_video_buffer = TRUE;
9536 init_gfx_buffers = TRUE;
9537 init_gadgets_and_anims = TRUE;
9539 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9542 if (new_scr_fieldx != SCR_FIELDX ||
9543 new_scr_fieldy != SCR_FIELDY)
9545 // this always toggles between MAIN and GAME when using small tile size
9547 SCR_FIELDX = new_scr_fieldx;
9548 SCR_FIELDY = new_scr_fieldy;
9550 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9561 new_sxsize != SXSIZE ||
9562 new_sysize != SYSIZE ||
9563 new_dxsize != DXSIZE ||
9564 new_dysize != DYSIZE ||
9565 new_vxsize != VXSIZE ||
9566 new_vysize != VYSIZE ||
9567 new_exsize != EXSIZE ||
9568 new_eysize != EYSIZE ||
9569 new_real_sx != REAL_SX ||
9570 new_real_sy != REAL_SY ||
9571 new_full_sxsize != FULL_SXSIZE ||
9572 new_full_sysize != FULL_SYSIZE ||
9573 new_tilesize_var != TILESIZE_VAR
9576 // ------------------------------------------------------------------------
9577 // determine next fading area for changed viewport definitions
9578 // ------------------------------------------------------------------------
9580 // start with current playfield area (default fading area)
9583 FADE_SXSIZE = FULL_SXSIZE;
9584 FADE_SYSIZE = FULL_SYSIZE;
9586 // add new playfield area if position or size has changed
9587 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9588 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9590 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9591 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9594 // add current and new door 1 area if position or size has changed
9595 if (new_dx != DX || new_dy != DY ||
9596 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9598 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9599 DX, DY, DXSIZE, DYSIZE);
9600 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9601 new_dx, new_dy, new_dxsize, new_dysize);
9604 // add current and new door 2 area if position or size has changed
9605 if (new_vx != VX || new_vy != VY ||
9606 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9608 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9609 VX, VY, VXSIZE, VYSIZE);
9610 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9611 new_vx, new_vy, new_vxsize, new_vysize);
9614 // ------------------------------------------------------------------------
9615 // handle changed tile size
9616 // ------------------------------------------------------------------------
9618 if (new_tilesize_var != TILESIZE_VAR)
9620 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9622 // changing tile size invalidates scroll values of engine snapshots
9623 FreeEngineSnapshotSingle();
9625 // changing tile size requires update of graphic mapping for EM engine
9626 init_em_graphics = TRUE;
9637 SXSIZE = new_sxsize;
9638 SYSIZE = new_sysize;
9639 DXSIZE = new_dxsize;
9640 DYSIZE = new_dysize;
9641 VXSIZE = new_vxsize;
9642 VYSIZE = new_vysize;
9643 EXSIZE = new_exsize;
9644 EYSIZE = new_eysize;
9645 REAL_SX = new_real_sx;
9646 REAL_SY = new_real_sy;
9647 FULL_SXSIZE = new_full_sxsize;
9648 FULL_SYSIZE = new_full_sysize;
9649 TILESIZE_VAR = new_tilesize_var;
9651 init_gfx_buffers = TRUE;
9652 init_gadgets_and_anims = TRUE;
9654 // printf("::: viewports: init_gfx_buffers\n");
9655 // printf("::: viewports: init_gadgets_and_anims\n");
9658 if (init_gfx_buffers)
9660 // printf("::: init_gfx_buffers\n");
9662 SCR_FIELDX = new_scr_fieldx_buffers;
9663 SCR_FIELDY = new_scr_fieldy_buffers;
9667 SCR_FIELDX = new_scr_fieldx;
9668 SCR_FIELDY = new_scr_fieldy;
9670 SetDrawDeactivationMask(REDRAW_NONE);
9671 SetDrawBackgroundMask(REDRAW_FIELD);
9674 if (init_video_buffer)
9676 // printf("::: init_video_buffer\n");
9678 FreeAllImageTextures(); // needs old renderer to free the textures
9680 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9681 InitImageTextures();
9684 if (init_gadgets_and_anims)
9686 // printf("::: init_gadgets_and_anims\n");
9689 InitGlobalAnimations();
9692 if (init_em_graphics)
9694 InitGraphicInfo_EM();